/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxpluginarchive.cpp,v 1.4.2.1 2004/07/09 02:07:27 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxtypes.h"
#include "hxcom.h"
#include "hxccf.h" // IHXCommonClassFactory
#include "ihxpckts.h" // IHXBuffer
#include "hlxclib/fcntl.h"
#include "chxdataf.h" // CHXDataFile
#include "debug.h"
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif

#include "hxpluginarchive.h"

//
// special chars; these need escaping if in value text
//
const char TERM_CHAR = ';';     // denotes end of value for most objects
const char ESC_CHAR = '\\';     // escape char

HXPluginArchiveReader::HXPluginArchiveReader()
: m_pFile(0)
, m_idxFile(0)
, m_pFactory(0)
{
  
}
HXPluginArchiveReader::~HXPluginArchiveReader()
{
    HX_DELETE(m_pFile);
    HX_RELEASE(m_pFactory);
}
//
// Open
//
HX_RESULT HXPluginArchiveReader::Open(IUnknown* pContext, const char* pszFile)
{
    DPRINTF(D_INFO, ("HXPluginArchiveReader::Open(): file = '%s'\n", pszFile));
    HX_RESULT hr = HXR_FAIL;
    
    HX_DELETE(m_pFile);

    pContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&m_pFactory);

    m_pFile = CHXDataFile::Construct();
    if (m_pFile)
    {
	hr = m_pFile->Open(pszFile, O_RDONLY, TRUE /*text*/);
        if(SUCCEEDED(hr))
        {
            LoadFile();
        }
    }
    else
    {
        hr = HXR_OUTOFMEMORY;
    }


    return hr;
}

void HXPluginArchiveReader::Close()
{
    HX_DELETE(m_pFile);
}


void HXPluginArchiveReader::LoadFile()
{
    #define BUF_SZ	0x0400	// read file in 1k chunks

    CHXString strBuf;

    char* buf = strBuf.GetBuffer(BUF_SZ + 1);
    if( buf)
    {
        for(;;)
        {
            INT32 cch = (INT32)m_pFile->Read(buf, BUF_SZ);
	    if (cch <= 0)
            {
                // end of file
	        break;
            }
        
            buf[cch] = '\0';
            m_strFile += buf;
        }
    }

    m_strFile.TrimRight(); // ensure no trailing space so 'AtEnd()' is easier
    m_idxFile = 0;
   
}


void HXPluginArchiveReader::GetNextToken(CHXString& str, char chTerm)
{
    UINT32 cchFile = m_strFile.GetLength();

    HX_ASSERT(chTerm != ESC_CHAR);

    while( m_idxFile < cchFile)
    {
        char ch = m_strFile[m_idxFile];

        if(chTerm == m_strFile[m_idxFile])
        {
            // found end of token; we're done
            ++m_idxFile;
            break;
        }

        if( ESC_CHAR == ch )
        {
            // skip escape to read in escaped char
            ++m_idxFile;
            if(m_idxFile == cchFile)
            {
                // unexpected: EOF in middle of escape sequence
                HX_ASSERT(false);
                break;
            }
            ch = m_strFile[m_idxFile];
        }
        str += ch;
        ++m_idxFile;
    }
    str.TrimLeft();
    str.TrimRight();
}



//
// read name;val;name;val;;
//
//
bool HXPluginArchiveReader::Read(IHXValues*& pval)
{
    HX_ASSERT(!AtEnd());
    UINT32 cchFile = m_strFile.GetLength();

    HX_ASSERT(m_pFactory);

    m_pFactory->CreateInstance(CLSID_IHXValues,(void**)&pval);
    if(!pval)
    {
        return false;
    }


    CHXString strName;
    CHXString strVal;


    // collect name-value pairs until we reach closing double TERM_CHAR
    for(;;)
    {
        strName = "";
        GetNextToken(strName, TERM_CHAR);

        if(strName.IsEmpty())
        {
            // end of list (ends with empty token, i.e., double TERM_CHAR
            return true;
        }

        if(m_idxFile == cchFile)
        {
            // corrupt file; end of file after name
            HX_ASSERT(false); 
            return false;
        }

        
        strVal = "";
        GetNextToken(strVal, TERM_CHAR);

        if(m_idxFile == cchFile)
        {
            // corrupt file
            HX_ASSERT(false); 
            return false;
        }

        if(strVal.IsEmpty())
        {
            // corrupt file
            HX_ASSERT(false);
            return false;
        }
 
        char type = strVal[0];
        CHXString strNakedVal = strVal.Mid(1);
        switch(type)
        {
            case 'N':
                {
                    int val = atoi(strNakedVal);
                    pval->SetPropertyULONG32(strName, ULONG32(val));
                }
                break;
            case 'B':
                {
                    IHXBuffer* pbuff = HXBufferUtil::CreateBuffer(m_pFactory, strNakedVal);
                    if( pbuff)
                    {
                        pval->SetPropertyBuffer(strName, pbuff);
                        HX_RELEASE(pbuff);
                    }
                }
                break;
            case 'S':
                {
                    IHXBuffer* pbuff = HXBufferUtil::CreateBuffer(m_pFactory, strNakedVal);
                    if( pbuff)
                    {
                        pval->SetPropertyCString(strName, pbuff);
                        HX_RELEASE(pbuff);
                    }
                }
                break;
            default:
                HX_ASSERT(false);
                return false;
        }

    }

    HX_ASSERT(false);
    return false;
}

void HXPluginArchiveReader::Read(bool& b)
{
    HX_ASSERT(!AtEnd());
    UINT32 val = 0;
    Read(val);
    b = (val ? true : false);

}

void HXPluginArchiveReader::Read(UINT16& val)
{
    HX_ASSERT(!AtEnd());
    UINT32 v;
    Read(v);
    HX_ASSERT(v <= 0xffff);
    val = UINT16(v);
}


void HXPluginArchiveReader::Read(UINT32& val)
{
    HX_ASSERT(!AtEnd());
    CHXString str;
    GetNextToken(str, TERM_CHAR);
    val = atoi(str);
}

void HXPluginArchiveReader::Read(CHXString& str)
{
    HX_ASSERT(!AtEnd());
    GetNextToken(str, TERM_CHAR);
}


void HXPluginArchiveReader::Read(IHXBuffer*& pBuff)
{
    HX_ASSERT(!AtEnd());
    CHXString str;
    GetNextToken(str, TERM_CHAR);
    pBuff = HXBufferUtil::CreateBuffer(m_pFactory, str);
}









//
// HXPluginArchiveWriter
//

HXPluginArchiveWriter::HXPluginArchiveWriter()
: m_pFile(0)
, m_bAtLeastOneVal(false)
{
  
}
HXPluginArchiveWriter::~HXPluginArchiveWriter()
{
    HX_DELETE(m_pFile);
}
//
// Open/create archive, wiping out existing if necessary
//
HX_RESULT HXPluginArchiveWriter::Open(IUnknown* pContext, const char* pszFile)
{
    DPRINTF(D_INFO, ("HXPluginArchiveWriter::Open(): file = '%s'\n", pszFile));
    HX_RESULT hr = HXR_FAIL;

    HX_DELETE(m_pFile);
    m_pFile = CHXDataFile::Construct();
    if (m_pFile)
    {
        hr = m_pFile->Open(pszFile, O_WRONLY | O_CREAT | O_TRUNC, TRUE /*text*/);
    }
    else
    {
        hr = HXR_OUTOFMEMORY;
    }


    return hr;
}

void HXPluginArchiveWriter::Close()
{
    HX_DELETE(m_pFile);
}


void HXPluginArchiveWriter::EscapeValue(const CHXString& str, CHXString& strOut)
{
    //
    // Escape special characters used for escaping and field termination
    //
    if( -1 == str.Find(ESC_CHAR) && -1 == str.Find(TERM_CHAR) )
    {
        // this may avoid copy if string class is optimized
        strOut = str;
    }
    else
    {
        INT32 cch = str.GetLength();
        for(INT32 idx = 0; idx < cch; ++idx)
        {
            char ch = str[idx];
            if(ch == ESC_CHAR || ch == TERM_CHAR)
            {
                strOut += ESC_CHAR;
            }

            strOut += ch;
        }
    }
}


// write line break (for sake of human consumption of file contents)
void HXPluginArchiveWriter::Break()
{
    const char* const BREAK_STRING = "\n";
    const UINT32 CCH_BREAK_STRING = 1;

    m_pFile->Write(BREAK_STRING, CCH_BREAK_STRING);
}

void HXPluginArchiveWriter::Write(const char* psz)
{
    CHXString strEscaped;
    EscapeValue(psz, strEscaped);

    HX_ASSERT(m_pFile);
    strEscaped += TERM_CHAR;
    m_pFile->Write(strEscaped, strEscaped.GetLength());  
}

void HXPluginArchiveWriter::Write(IHXBuffer* pBuffer)
{
    HX_ASSERT(pBuffer);
    const char* psz = (const char*)pBuffer->GetBuffer();
  
    Write(psz);
}

void HXPluginArchiveWriter::Write(bool b)
{
    UINT32 val = (b ? 1 : 0);
    Write(val);
}

void HXPluginArchiveWriter::Write(UINT32 val)
{
    CHXString str;
    str.Format("%lu", val);
    Write(str);
}

void HXPluginArchiveWriter::Write(UINT16 val)
{
    Write(UINT32(val));
}


void HXPluginArchiveWriter::Write(IHXValues* pval)
{
    HX_ASSERT(pval);

    CHXString str;
    ULONG32 ulValue;
    const char* pszKey = NULL;

    // Dump ULONG32 values
    HX_RESULT hr = pval->GetFirstPropertyULONG32( pszKey, ulValue );
    if( SUCCEEDED(hr) )
    {
	do
	{
            //key
            Write(pszKey);

            //value
	    str.Format("N%d",  ulValue);
            Write(str);
	   
	    hr = pval->GetNextPropertyULONG32( pszKey, ulValue);
	}
	while( SUCCEEDED(hr) );
    }

    // Dump IHXBuffer values
    IHXBuffer* pbuff;
    hr = pval->GetFirstPropertyBuffer( pszKey, pbuff );
    if( SUCCEEDED(hr) )
    {
	do
	{	
            //key
            Write(pszKey);

            //value
	    str.Format("B%.*s",  pbuff->GetSize(), pbuff->GetBuffer() );
            Write(str);
           
	    HX_RELEASE(pbuff);
	    hr = pval->GetNextPropertyBuffer( pszKey,pbuff);
	}
	while( SUCCEEDED(hr) );
    }

    // Dump CString values
    hr = pval->GetFirstPropertyCString( pszKey, pbuff );
    if( SUCCEEDED(hr) )
    {
	do
	{
            //key
            Write(pszKey);

            //value
	    str.Format("S%.*s",  pbuff->GetSize(), pbuff->GetBuffer() );
            Write(str);

	    HX_RELEASE(pbuff);
	    hr = pval->GetNextPropertyCString( pszKey, pbuff);
	}
	while( SUCCEEDED(hr) );
    }

    // empty value terminates list
    Write("");	
}


//XXXLCM util
IHXBuffer* HXBufferUtil::CreateBuffer(IHXCommonClassFactory* pFact, const char* psz)
{
    if(!psz)
    {
        psz = "";
    }
    IHXBuffer* pbuff = 0;
    HX_RESULT hr = pFact->CreateInstance(CLSID_IHXBuffer, (void**)&pbuff);
    if( SUCCEEDED(hr) )
    {
        hr = pbuff->Set( (BYTE*)psz, strlen(psz) + 1);
        if( FAILED(hr) )
        {
            HX_RELEASE(pbuff);
        }
    }
    return pbuff;
}
