//=============================================================================
// File:       disptype.cpp
// Contents:   Definitions for DwDispositionType
// Maintainer: Doug Sauder <dwsauder@fwb.gulf.net>
// WWW:        http://www.fwb.gulf.net/~dwsauder/mimepp.html
//
// Copyright (c) 1996, 1997 Douglas W. Sauder
// All rights reserved.
//
// IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT,
// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
// THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER
// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
// NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
// BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
//
//=============================================================================

#define DW_IMPLEMENTATION

#include <mimelib/config.h>
#include <mimelib/debug.h>
#include <mimelib/string.h>
#include <mimelib/param.h>
#include <mimelib/disptype.h>
#include <mimelib/token.h>
#include <mimelib/enum.h>


const char* const DwDispositionType::sClassName = "DwDispositionType";


DwDispositionType* (*DwDispositionType::sNewDispositionType)(
    const DwString&, DwMessageComponent*) = 0;


DwDispositionType* DwDispositionType::NewDispositionType(
    const DwString& aStr, DwMessageComponent* aParent)
{
    if (sNewDispositionType) {
        return sNewDispositionType(aStr, aParent);
    }
    else {
        return new DwDispositionType(aStr, aParent);
    }
}


DwDispositionType::DwDispositionType()
{
    mDispositionType = DwMime::kDispTypeNull;
    mFirstParameter = 0;
    mClassId = kCidDispositionType;
    mClassName = sClassName;
}


DwDispositionType::DwDispositionType(const DwDispositionType& aDispType)
  : DwFieldBody(aDispType),
    mDispositionTypeStr(aDispType.mDispositionTypeStr),
    mFilenameStr(aDispType.mFilenameStr)
{
    mFirstParameter = 0;
    mDispositionType = aDispType.mDispositionType;
    if (aDispType.mFirstParameter) {
        CopyParameterList(aDispType.mFirstParameter);
    }
    mClassId = kCidDispositionType;
    mClassName = sClassName;
}


DwDispositionType::DwDispositionType(const DwString& aStr,
    DwMessageComponent* aParent)
  : DwFieldBody(aStr, aParent)
{
    mDispositionType = DwMime::kDispTypeNull;
    mFirstParameter = 0;
    mClassId = kCidDispositionType;
    mClassName = sClassName;
}


DwDispositionType::~DwDispositionType()
{
    if (mFirstParameter) {
        DeleteParameterList();
    }
}


const DwDispositionType& DwDispositionType::operator = (
    const DwDispositionType& aDispType)
{
    if (this == &aDispType) return *this;
    mDispositionType    = aDispType.mDispositionType;
    mDispositionTypeStr = aDispType.mDispositionTypeStr;
    mFilenameStr        = aDispType.mFilenameStr;

    if (mFirstParameter) {
        DeleteParameterList();
    }
    if (aDispType.mFirstParameter) {
        CopyParameterList(aDispType.mFirstParameter);
    }

    if (mParent) {
        mParent->SetModified();
    }

    return *this;
}


int DwDispositionType::DispositionType() const
{
    return mDispositionType;
}


void DwDispositionType::SetDispositionType(int aType)
{
    mDispositionType = aType;
    EnumToStr();
    SetModified();
}


const DwString& DwDispositionType::DispositionTypeStr() const
{
    return mDispositionTypeStr;
}


void DwDispositionType::SetDispositionTypeStr(const DwString& aStr)
{
    mDispositionTypeStr = aStr;
    StrToEnum();
    SetModified();
}


const DwString& DwDispositionType::Filename() const
{
    DwParameter* param = mFirstParameter;
    while (param) {
        if (DwStrcasecmp(param->Attribute(), "filename") == 0) {
            // Filename parameter found. Return its value.
            // Implementation note: this member function is const, which
            // forbids us from assigning to mFilenameStr.  The following
            // trick gets around this.  (ANSI implementations could use the
            // "mutable" declaration).
            DwDispositionType* _this = (DwDispositionType*) this;
            _this->mFilenameStr = param->Value();
            break;
        }
        param = param->Next();
    }
    return mFilenameStr;
}


void DwDispositionType::SetFilename(const DwString& aStr)
{
    mFilenameStr = aStr;
    // Search for filename parameter in parameter list.  If found, set its
    // value.
    DwParameter* param = mFirstParameter;
    while (param) {
        if (DwStrcasecmp(param->Attribute(), "filename") == 0) {
            param->SetValue(mFilenameStr);
            return;
        }
        param = param->Next();
    }
    // Boundary parameter not found. Add it.
    param = DwParameter::NewParameter("", 0);
    param->SetAttribute("Filename");
    param->SetValue(aStr);
    AddParameter(param);
}


DwParameter* DwDispositionType::FirstParameter() const
{
    return mFirstParameter;
}


void DwDispositionType::AddParameter(DwParameter* aParam)
{
    _AddParameter(aParam);
    SetModified();
}


void DwDispositionType::_AddParameter(DwParameter* aParam)
{
    if (!mFirstParameter) {
        mFirstParameter = aParam;
    }
    else {
        DwParameter* cur = mFirstParameter;
        if( cur ) {
            DwParameter* next = cur->Next();
            while (next) {
                cur = next;
                next = cur->Next();
            }
            cur->SetNext(aParam);
        }
    }
    aParam->SetParent(this);
}


void DwDispositionType::Parse()
{
    mIsModified = 0;
    mDispositionType = DwMime::kDispTypeNull;
    mDispositionTypeStr = "";
    if (mFirstParameter) {
        DeleteParameterList();
    }
    if (mString.length() == 0) return;
    DwRfc1521Tokenizer tokenizer(mString);
    int found = 0;
    while (!found && tokenizer.Type() != eTkNull) {
        if (tokenizer.Type() == eTkToken) {
            mDispositionTypeStr = tokenizer.Token();
            found = 1;
        }
        ++tokenizer;
    }
    // Get parameters
    DwTokenString tokenStr(mString);
    while (1) {
        // Get ';'
        found = 0;
        while (!found && tokenizer.Type() != eTkNull) {
            if (tokenizer.Type() == eTkTspecial
                && tokenizer.Token()[0] == ';') {
                found = 1;
            }
            ++tokenizer;
        }
        if (tokenizer.Type() == eTkNull) {
            // No more parameters
            break;
        }
        tokenStr.SetFirst(tokenizer);
        // Get attribute
        DwString attrib;
        int attribFound = 0;
        while (!attribFound && tokenizer.Type() != eTkNull) {
            if (tokenizer.Type() == eTkToken) {
                attrib = tokenizer.Token();
                attribFound = 1;
            }
            ++tokenizer;
        }
        // Get '='
        found = 0;
        while (!found && tokenizer.Type() != eTkNull) {
            if (tokenizer.Type() == eTkTspecial
                && tokenizer.Token()[0] == '=') {
                found = 1;
            }
            ++tokenizer;
        }
        // Get value
        int valueFound = 0;
        while (!valueFound && tokenizer.Type() != eTkNull) {
            if (tokenizer.Type() == eTkToken
                || tokenizer.Type() == eTkQuotedString) {
                valueFound = 1;
            }
            ++tokenizer;
        }
        if (attribFound && valueFound) {
            tokenStr.ExtendTo(tokenizer);
            DwParameter* param =
                DwParameter::NewParameter(tokenStr.Tokens(), this);
            param->Parse();
            _AddParameter(param);
        }
    }
    StrToEnum();
}


void DwDispositionType::Assemble()
{
    if (!mIsModified) return;
    mString = "";
    if (mDispositionTypeStr.length() == 0)
        return;
    mString += mDispositionTypeStr;
    DwParameter* param = FirstParameter();
    while (param) {
        param->Assemble();
        if (IsFolding()) {
            mString += ";" DW_EOL "  ";
        }
        else {
            mString += "; ";
        }
        mString += param->AsString();
        param = param->Next();
    }
    mIsModified = 0;
}


DwMessageComponent* DwDispositionType::Clone() const
{
    return new DwDispositionType(*this);
}


void DwDispositionType::EnumToStr()
{
    switch (mDispositionType) {
    case DwMime::kDispTypeInline:
        mDispositionTypeStr = "inline";
        break;
    case DwMime::kDispTypeAttachment:
        mDispositionTypeStr = "attachment";
        break;
    }
}


void DwDispositionType::StrToEnum()
{
    switch (mDispositionTypeStr[0]) {
    case 'i':
        if (DwStrcasecmp(mDispositionTypeStr, "inline") == 0) {
            mDispositionType = DwMime::kDispTypeInline;
        }
        else {
            mDispositionType = DwMime::kDispTypeUnknown;
        }
        break;
    case 'a':
        if (DwStrcasecmp(mDispositionTypeStr, "attachment") == 0) {
            mDispositionType = DwMime::kDispTypeAttachment;
        }
        else {
            mDispositionType = DwMime::kDispTypeUnknown;
        }
        break;
    }
}


void DwDispositionType::DeleteParameterList()
{
    DwParameter* param = mFirstParameter;
    while (param) {
        DwParameter* nextParam = param->Next();
        delete param;
        param = nextParam;
    }
    mFirstParameter = 0;
    SetModified();
}


void DwDispositionType::CopyParameterList(DwParameter* aFirst)
{
    DwParameter* param = aFirst;
    while (param) {
        DwParameter* newParam = (DwParameter*) param->Clone();
        AddParameter(newParam);
        param = param->Next();
    }
}


#if defined(DW_DEBUG_VERSION)
void DwDispositionType::PrintDebugInfo(std::ostream& aStrm, int aDepth) const
{
    aStrm <<
    "------------ Debug info for DwDispositionType class ------------\n";
    _PrintDebugInfo(aStrm);
    int depth = aDepth - 1;
    depth = (depth >= 0) ? depth : 0;
    if (aDepth == 0 || depth > 0) {
        DwParameter* param = mFirstParameter;
        while (param) {
            param->PrintDebugInfo(aStrm, depth);
            param = param->Next();
        }
    }
}
#else
void DwDispositionType::PrintDebugInfo(std::ostream&, int) const {}
#endif // defined(DW_DEBUG_VERSION)


#if defined(DW_DEBUG_VERSION)
void DwDispositionType::_PrintDebugInfo(std::ostream& aStrm) const
{
    DwFieldBody::_PrintDebugInfo(aStrm);
    aStrm << "Disposition Type: " << mDispositionTypeStr
        << " (" << mDispositionType    << ")\n";
    aStrm << "Filename:         " << mFilenameStr << "\n";
    aStrm << "Parameters:       ";
    DwParameter* param = mFirstParameter;
    if (param) {
        int count = 0;
        while (param) {
            if (count) aStrm << ' ';
            aStrm << param->ObjectId();
            param = param->Next();
            ++count;
        }
        aStrm << '\n';
    }
    else {
        aStrm << "(none)\n";
    }
}
#else
void DwDispositionType::_PrintDebugInfo(std::ostream& ) const {}
#endif // defined(DW_DEBUG_VERSION)


void DwDispositionType::CheckInvariants() const
{
#if defined(DW_DEBUG_VERSION)
    mDispositionTypeStr.CheckInvariants();
    mFilenameStr.CheckInvariants();
    DwParameter* param = mFirstParameter;
    while (param) {
        param->CheckInvariants();
        assert((DwMessageComponent*) this == param->Parent());
        param = param->Next();
    }
#endif // defined(DW_DEBUG_VERSION)
}