#ifndef __DCL_HTTP_COLLECTION_H__
#define __DCL_HTTP_COLLECTION_H__       20050526

#ifndef __DCL_STRING_H__
#include <dcl/core/String.h>
#endif
#ifndef __DCL_OBJECT_H__
#include <dcl/core/Object.h>
#endif
#ifndef __DCL_EXCEPTION_H__
#include <dcl/core/Exception.h>
#endif
#ifndef __DCL_STREAM_H__
#include <dcl/core/Stream.h>
#endif
#ifndef __DCL_COLLECTION_H__
#include <dcl/core/Collection.h>
#endif

__DCL_BEGIN_NAMESPACE

class HttpServletContext;

/*
    HttpCookieDecoder
    HttpQueryStringDecoder
    HttpQueryStringEncoder

    InputStream
        HttpFormDataInputStream
    HttpFormData
        BufferedHttpFormData
        StoredHttpFormData
    HttpFormDataDecoder
*/

class DCLNAPI HttpCookieDecoder
{
public:
    static void decode(
                StringToStringMap& mapResult,
                const String& strContents
                );
};

class DCLNAPI HttpQueryStringDecoder
{
public:
    static void decode(
                StringToStringVectorMap& mapResult,
                const char* pszQueryString,
                size_t nLength = (size_t)-1
                );

    static void decode(
                StringToStringVectorMap& mapResult,
                const String& strQueryString
                );

    static bool isValidType(
                const char* pszContentType
                );
};

class DCLNAPI HttpQueryStringEncoder
{
public:
    static String encode(
                const StringToStringVectorMap& mapResult
                );
};

// RFC 1867 - Form-based File Upload in HTML
// http://www.faqs.org/rfcs/rfc1867.html

class HttpFormDataDecoder;
class DCLNAPI HttpFormData : public Object
{
    DECLARE_CLASSINFO(HttpFormData)
protected:
    struct PartHeader
    {
        // Content-Disposition
        String strPartType;     // "form-data", "attachement"
        String strName;         // value of "name"
        String strFileName;     // value of "filename"

        String strContentType;  // value of "Content-Type"
        String strTransferEncoding; // value of "TransferEncoding"
    };

    // PartHeader::strFileName이 있을경우 파일의 시작이다.
    // 만약 onFileStart내부에서 에러가 발생하여 false를 리턴하면
    // 이후의 onFileData, onFileEnd는 호출되지 않는다.
    virtual bool onFileStart(
                    const PartHeader& header,
                    void**      ppCallbackData,
                    String&     strCallbackError    // return false 이면
                    );

    // formdata의 파일의 데이터가 있을때 불려진다.
    // 만약 onFileData 내부에서 에러가 발생하여 false를 리턴하면
    // onFileEnd 는 호출되지 않는다. 따라서 pCallbackData 관련하여
    // 자원이 할당되어 있으면 리턴하기 전에 해제해야 한다.
    virtual bool onFileData(
                    const void* pData,
                    size_t      nSize,
                    void*       pCallbackData,
                    String&     strCallbackError    // return false 이면
                    );
    
    // 종결 part가 나타나면 불려진다. 종결 part는 CRLF로 구분되며
    // 만약 이것이 타나타지 않은 상태에는 bDataSuccess는 false를 넘긴다.
    virtual bool onFileEnd(
                    const PartHeader& header,
                    void*       pCallbackData,
                    bool        bDataSuccess,
                    String&     strCallbackError    // return false 이면
                    );

    friend class HttpFormDataDecoder;
};

class DCLNAPI HttpFormDataDecoderException : public Exception
{
    DECLARE_CLASSINFO(HttpFormDataDecoderException)
public:
    enum ErrorCode
    {
        ePostReadError,
        eFormDataCallbackError
    };

    ErrorCode   m_errorCode;
    String      m_strMsg;

    HttpFormDataDecoderException(
        ErrorCode errorCode,
        IOException* pDetailEx
        );

    HttpFormDataDecoderException(
        ErrorCode errorCode,
        const String& strMsg
        );

    virtual String getMessage() const;
};


class DCLNAPI HttpFormDataDecoder : public Object
{
    DECLARE_CLASSINFO(HttpFormDataDecoder)
public:
    static bool isValidType(const char* pszContentType);
    static String getBoundary(const char* pszContentType);

public:
    HttpFormDataDecoder(size_t  nBufferSize = 4096);

    virtual ~HttpFormDataDecoder();

    // 디코딩 과정중에 입력데이터와 관련한 에러가 발생하면
    // warnings() 을 통해 에러 메시지를 얻을 수 있다.
    // 에러가 발생한 데이터는 버려진다.
    void decode(
            StringToStringVectorMap& paramResult,
            HttpFormData*       pFileDataResult,
            InputStream*    pInput,
            const char*     pszContentType,
            size_t          nContentLength
            ) __DCL_THROWS1(HttpFormDataDecoderException*);

    const String& warnings() const { return m_strWarnings; }

private:
    // NULL : 버퍼가 비어 있거나, not found CRLF
    char* getLine(size_t& n);

    // true : valid first part boundary
    // false : close boundary, or invalid data
    bool getFirstBoundary(const String& strBoundary);

    // false EOF
    bool getPartHeader(HttpFormData::PartHeader& header)
            __DCL_THROWS1(HttpFormDataDecoderException*);

    // NULL : 버퍼가 비어 있거나 데이터가 유효하지 않다.
    enum DataState
    {
        dsDataMore,
        dsBeforeNextBoundary,
        dsBeforeCloseBoundary
    };
        
    DataState getDataBlock(
                char* & pDataStart,
                size_t& n,          // data size
                const String& strBoundary
                );

    // false data empty && EOF
    bool readInput() __DCL_THROWS1(HttpFormDataDecoderException*);
    void appendWarning(const char* pszWarning);


    InputStream*    m_pInput;
    size_t          m_nContentLength;
    size_t          m_nRemainder;

    char*       m_pBuffer;
    size_t      m_nBufferSize;
    char*       m_pDataStart;
    char*       m_pDataEnd;

    String      m_strWarnings;  // decoding warnings, string delimeter '\n'

    // const strings for compare
    _CONST String   m_strContentDisposition;        // "Content-Dispositon"
    _CONST String   m_strContentTransferEncoding;   // "Content-Transfer-Encoding"
    _CONST String   m_strContentType;               // "Content-Type"
    _CONST String   m_strName;                      // "name"
    _CONST String   m_strFileName;                  // "filename"
};


// 파일데이터를 버퍼에 유지

class DCLNAPI BufferedHttpFormData : public HttpFormData
{
    DECLARE_CLASSINFO(BufferedHttpFormData)
public:
    class FileInfoVector;

    class DCLNAPI FileInfo
    {
    public:
        String  strFileName;        // IE는 절대경로 포함, Netscape는 basename
        String  strContentType;
        String  strTransferEncoding;
        size_t  nFileSize;
        const BYTE* fileData() const { return m_pFileData; }

        // 파일데이터를 위한 버퍼를 지운다.
        void freeFileData();

    protected:
        // not using
        //      FileInfo info = v[i];
        // use
        //      FileInfo& info = v[i];
        FileInfo();
        FileInfo(const FileInfo& src);
        ~FileInfo();

        BYTE*   m_pFileData;

        friend class FileInfoVector;
        friend class BufferedHttpFormData;
    };

    class DCLNAPI FileInfoVector
    {
    public:
        FileInfo& operator[] (int nIndex);
        int count() const;
        bool isEmpty() const;
        const String& name() const { return m_strName; }

    private:
        void*   m_pHandle;

    protected:
        String  m_strName;      // <input name="name"

        // not using
        //      FileInfoVector v = formData["name"];
        // use
        //      FileInfoVector& v = formData.byName("name");
        //  or  FileInfoVecotr& v = formData[i];
        FileInfoVector(const String& strName);
        FileInfoVector(const FileInfo& src);
        ~FileInfoVector();
        void addTail(FileInfo* pNewItem);

        friend class BufferedHttpFormData;
    };

public:
    BufferedHttpFormData();
    virtual ~BufferedHttpFormData();

    // <input name="name" type="file"/> 에서 "name" 이 개수
    // 서로 다른 "name"의 개수
    // FileInfoVector의 개수
    int count() const;
    bool isEmpty() const;
    FileInfoVector& operator[] (int nIndex);
    FileInfoVector& byName(const char* pszName);

private:
    void*   m_pHandle;

    void insert(const String& strName, FileInfo* pNewItem);

protected:
    virtual bool onFileStart(
                    const PartHeader& header,
                    void** ppCallbackData,
                    String& strCallbackError    // return false 이면
                    );

    virtual bool onFileData(
                    const void* pData,
                    size_t  nSize,
                    void* pCallbackData,
                    String& strCallbackError    // return false 이면
                    );

    virtual bool onFileEnd(
                    const PartHeader& header,
                    void* pCallbackData,
                    bool        bDataSuccess,
                    String& strCallbackError    // return false 이면
                    );
};
// 파일데이터를 임시파일에 저장

class DCLNAPI StoredHttpFormData : public HttpFormData
{
    DECLARE_CLASSINFO(StoredHttpFormData)
public:
    class FileInfoVector;

    class DCLNAPI FileInfo
    {
    public :
        String  strFileName;        // basename
        String  strContentType;
        String  strTransferEncoding;
        size_t  nFileSize;
        String  strTempFileName;

    protected:
        // not using
        //      FileInfo info = v[i];
        // use
        //      FileInfo& info = v[i];
        FileInfo();
        FileInfo(const FileInfo& str);
        ~FileInfo();

        friend class FileInfoVector;
        friend class StoredHttpFormData;
    };

    class DCLNAPI FileInfoVector
    {
    public:
        FileInfo& operator[] (int nIndex);
        int count() const;
        bool isEmpty() const;
        const String& name() const { return m_strName; }

    private:
        void*   m_pHandle;

    protected:
        String  m_strName;      // <input name="name"

        // not using
        //      FileInfoVector v = formData["name"];
        // use
        //      FileInfoVector& v = formData.byName("name");
        //  or  FileInfoVecotr& v = formData[i];
        FileInfoVector(const String& strName);
        FileInfoVector(const FileInfo& src);
        ~FileInfoVector();
        void addTail(FileInfo* pNewItem);

        friend class StoredHttpFormData;
    };

public:
    StoredHttpFormData(const String& strTempDir);
    virtual ~StoredHttpFormData();

    // <input name="name" type="file"/> 에서 "name" 이 개수
    // 서로 다른 "name"의 개수
    // FileInfoVector의 개수
    int count() const;
    bool isEmpty() const;
    FileInfoVector& operator[] (int nIndex);
    FileInfoVector& byName(const char* pszName);

private:
    String  m_strTempDir;
    void*   m_pHandle;

    void insert(const String& strName, FileInfo* pNewItem);

protected:
    virtual bool onFileStart(
                    const PartHeader& header,
                    void**      ppCallbackData,
                    String&     strCallbackError    // return false 이면
                    );

    virtual bool onFileData(
                    const void* pData,
                    size_t      nSize,
                    void*       pCallbackData,
                    String&     strCallbackError    // return false 이면
                    );

    virtual bool onFileEnd(
                    const PartHeader& header,
                    void*       pCallbackData,
                    bool        bDataSuccess,
                    String&     strCallbackError    // return false 이면
                    );
};

__DCL_END_NAMESPACE


#endif  // __DCL_HTTP_COLLECTION_H__