/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: filehdlr.cpp,v 1.2.24.1 2004/07/09 01:52:03 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
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "hxresult.h"
#include "hxcomm.h"
#include "hxengin.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxformt.h"
#include "hxplugn.h"
#include "hxengin.h"
#include "hxmon.h"
#include "ihxfgbuf.h"

// pnmisc
#include "baseobj.h"
#include "unkimp.h"

// pncont
#include "hxslist.h"
#include "hxmap.h"
#include "hxbuffer.h"
#include "carray.h"
#include "chxfgbuf.h"

// pxcomlib
#include "pxrect.h"
#include "pxcolor.h"
#include "pxeffect.h"
#include "pxcmpmgr.h"
#include "wirefmgr.h"
#include "pxffmcod.h"
#include "rpfile.h"
#include "pxffcmgr.h"

// pxff
#include "pxschedu.h"
#include "pxff2.h"
#include "filehdlr.h"

// pndebug
#include "errdbg.h"
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif

PXFileHandler::PXFileHandler()
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::PXFileHandler(0x%08X)\n", this));
#endif
    m_lRefCount           = 0;
    m_pContext            = NULL;
    m_pRPFileObject       = NULL;
    m_pResponse           = NULL;
    m_pCommonClassFactory = NULL;
    m_pFileSystemManager  = NULL;
    m_pFileObject         = NULL;
    m_pFragBuffer         = NULL;
    m_pImageNameStr       = NULL;
    m_pImageMimeTypeStr   = NULL;
    m_pFileStat           = NULL;
    m_pFileMimeMapper     = NULL;
    Reset();
}

PXFileHandler::~PXFileHandler()
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::~PXFileHandler(0x%08X)\n", this));
#endif
    Deallocate();
}

STDMETHODIMP PXFileHandler::Init(IUnknown*           pContext,
                                 IHXFileObject*     pRPFileObject,
                                 CRealPixFileFormat* pResponse,
				 ULONG32             ulReadChunkSize)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::Init(0x%08X,0x%08X,0x%08X,0x%08X)\n",
           this, pContext, pRPFileObject, pResponse));
#endif
    HX_RESULT retVal = HXR_OK;

    if (pContext && pRPFileObject && pResponse)
    {
        // Clear out everything
        Deallocate();
        Reset();

	m_ulReadChunkSize = ulReadChunkSize;

        // Save arguments into members
        m_pContext = pContext;
        m_pContext->AddRef();
        m_pRPFileObject = pRPFileObject;
        m_pRPFileObject->AddRef();
        m_pResponse = pResponse;
        m_pResponse->AddRef();

        // Get an IHXCommonClassFactory interface
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                            (void**) &m_pCommonClassFactory);
        if (SUCCEEDED(retVal))
        {
            // Get an IHXFileSystemManager interface
            retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXFileSystemManager,
                                                           (void**) &m_pFileSystemManager);
            if (SUCCEEDED(retVal))
            {
                // Set the state
                m_ulState = kStateInitialized;
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    if (FAILED(retVal))
    {
        m_ulState = kStateError;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::ReadRPFile()
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::ReadRPFile(0x%08X)\n", this));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateInitialized)
    {
        // Set state
        m_ulState = kStateReadRPFileInit;
        // Init the file object
        m_pRPFileObject->Init(HX_FILE_READ | HX_FILE_BINARY, this);
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::StatImageFile(UINT32 ulInstance, IHXBuffer* pImageNameStr)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::StatImageFile(0x%08X,%lu,%s)\n",
               this, ulInstance, (const char*) pImageNameStr->GetBuffer()));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateInitialized)
    {
        if (pImageNameStr)
        {
            // Save the instance index
            m_ulInstance = ulInstance;

            // Save the name buffer
            HX_RELEASE(m_pImageNameStr);
            m_pImageNameStr = pImageNameStr;
            m_pImageNameStr->AddRef();

            // Set the state
            m_ulState = kStateStatImageFileFSMInit;

            // Init the file system manager
            retVal = m_pFileSystemManager->Init(this);
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(retVal, ulInstance, 0, NULL);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::ReadImageFile(UINT32 ulInstance, IHXBuffer* pImageNameStr)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::ReadImageFile(0x%08X,%lu,%s)\n",
               this, ulInstance, (const char*) pImageNameStr->GetBuffer()));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateInitialized)
    {
        if (ulInstance && pImageNameStr)
        {
            // Save the handle
            m_ulInstance = ulInstance;
            // Save the image name
            HX_RELEASE(m_pImageNameStr);
            m_pImageNameStr = pImageNameStr;
            m_pImageNameStr->AddRef();
            // Set the state
            m_ulState = kStateReadImageFileFSMInit;
            // Init the file system manager
            m_pFileSystemManager->Init(this);
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(retVal, ulInstance, NULL);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::Shutdown()
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::Shutdown(0x%08X)\n", this));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_pResponse)
    {
        if (m_ulState == kStateConstructed          ||
            m_ulState == kStateError                ||
            m_ulState == kStateInitialized          ||
            m_ulState == kStateReadRPFileDone       ||
            m_ulState == kStateStatImageFileDone    ||
            m_ulState == kStateReadImageFileDone)
        {
            // In these cases, we there is no pending callback
            // because we either finished or we never started.
            // Therefore we simply change the state to the final
            // state and call ShutdownDone().
            m_ulState = kStateShutdownDone;
            // Call ShutdownDone()
            m_pResponse->ShutdownDone(HXR_OK, m_ulInstance);
        }
        else if (m_ulState == kStateReadRPFileInit ||
                 m_ulState == kStateReadRPFileRead ||
                 m_ulState == kStateReadRPFileHold)
        {
            // Here we have an .rp file object for which we're 
            // waiting on a pending callback. We will call 
            // Close() on this file object and then wait on
            // the CloseDone() to call ShutdownDone() in the application.
            // Depending on the file system, the pending callback may come
            // back immediately or it may come back normally.
            // All that we have to do is make sure that we do
            // nothing in that callback.
            //
            // First we change the state
            m_ulState = kStateShutdownClose;
            // Call Close on the .rp file object
            m_pRPFileObject->Close();
        }
        else if (m_ulState == kStateStatImageFileInit ||
                 m_ulState == kStateStatImageFileStat ||
                 m_ulState == kStateStatImageFileMime ||
                 m_ulState == kStateReadImageFileInit ||
                 m_ulState == kStateReadImageFileRead)
        {
            // Here we have an image file object for which we're 
            // waiting on a pending callback. We will call 
            // Close() on this image file object and then wait on
            // the CloseDone() to call ShutdownDone() in the application.
            // Depending on the file system, the pending callback may come
            // back immediately or it may come back normally.
            // All that we have to do is make sure that we do
            // nothing in that callback.
            //
            // First change the state
            m_ulState = kStateShutdownClose;
            // Call Close()
            m_pFileObject->Close();
        }
        else if (m_ulState == kStateReadRPFileClose    ||
                 m_ulState == kStateStatImageFileClose ||
                 m_ulState == kStateReadImageFileClose)
        {
            // Here we have already called Close() on the
            // file object so we don't have to do it again.
            // We simply have to set the state and when we
            // get called back with CloseDone(), then we need
            // to call ShutdownDone() instead of the normal
            // callback to the application.
            m_ulState = kStateShutdownClose;
        }
        else if (m_ulState == kStateStatImageFileFSMInit ||
                 m_ulState == kStateStatImageFileCreate  ||
                 m_ulState == kStateReadImageFileFSMInit ||
                 m_ulState == kStateReadImageFileCreate)
        {
            // Here we are waiting on callbacks from the 
            // file system manager. In these cases, we simply
            // need to change the state to the final state
            // and then call ShutdownDone() in the application.
            // Also, we need to make sure that we don't do anything
            // when/if the pending callback comes back.
            m_ulState = kStateShutdownDone;
            // Call ShutdownDone() in the application
            m_pResponse->ShutdownDone(HXR_OK, m_ulInstance);
        }
        else if (m_ulState == kStateShutdownClose)
        {
            // We've already set this object up for shutdown
            // and are awaiting a CloseDone() from the file
            // object. Therefore, we will return HXR_UNEXPECTED;
            retVal = HXR_UNEXPECTED;
        }
        else if (m_ulState == kStateShutdownDone)
        {
            // We've already shut this object down, so
            // will return HXR_UNEXPECTED
            retVal = HXR_UNEXPECTED;
        }
        else
        {
            // Unknown state
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP_(UINT32) PXFileHandler::GetInstanceHandle()
{
    return m_ulInstance;
}

STDMETHODIMP_(BOOL) PXFileHandler::IsShutdownDone()
{
    return (m_ulState == kStateShutdownDone ? TRUE : FALSE);
}

STDMETHODIMP PXFileHandler::QueryInterface(REFIID riid, void** ppvObj)
{
    HX_RESULT retVal = HXR_OK;

    if (IsEqualIID(riid, IID_IUnknown))
    {
        AddRef();
        *ppvObj = this;
    }
    else if (IsEqualIID(riid, IID_IHXFileSystemManagerResponse))
    {
        AddRef();
        *ppvObj = (IHXFileSystemManagerResponse*) this;
    }
    else if (IsEqualIID(riid, IID_IHXFileResponse))
    {
        AddRef();
        *ppvObj = (IHXFileResponse*) this;
    }
    else if (IsEqualIID(riid, IID_IHXFileStatResponse))
    {
        AddRef();
        *ppvObj = (IHXFileStatResponse*) this;
    }
    else if (IsEqualIID(riid, IID_IHXFileMimeMapperResponse))
    {
        AddRef();
        *ppvObj = (IHXFileMimeMapperResponse*) this;
    }
    else if (IsEqualIID(riid, IID_IHXThreadSafeMethods))
    {
        AddRef();
        *ppvObj = (IHXThreadSafeMethods*) this;
    }
    else
    {
        *ppvObj = NULL;
        retVal  = HXR_NOINTERFACE;
    }

    return retVal;
}

STDMETHODIMP_(UINT32) PXFileHandler::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32) PXFileHandler::Release()
{
    
    if (InterlockedDecrement(&m_lRefCount) > 0)
        return m_lRefCount;

    delete this;

    return 0;
}

STDMETHODIMP PXFileHandler::InitDone(HX_RESULT status)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::InitDone(0x%08X,0x%08x) ",
               this, status));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateStatImageFileFSMInit)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileFSMInit\n"));
#endif
        if (SUCCEEDED(status))
        {
            // Set the state
            m_ulState  = kStateStatImageFileCreate;
            // Create the file
            retVal     = CreateImageFile((const char*) m_pImageNameStr->GetBuffer());
        }
        else
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(status, m_ulInstance, 0, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(retVal, m_ulInstance, 0, NULL);
        }
    }
    else if (m_ulState == kStateReadImageFileFSMInit)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadImageFileFSMInit\n"));
#endif
        if (SUCCEEDED(status))
        {
            // Set the state
            m_ulState  = kStateReadImageFileCreate;
            // Create the file
            retVal = CreateImageFile((const char*) m_pImageNameStr->GetBuffer());
        }
        else
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(status, m_ulInstance, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(retVal, m_ulInstance, NULL);
        }
    }
    else if (m_ulState == kStateReadRPFileInit)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadRPFileInit\n"));
#endif
        if (SUCCEEDED(status))
        {
            // Create an IHXFragmentedBuffer object
            HX_RELEASE(m_pFragBuffer);
            retVal = CHXFragmentedBuffer::CreateObject((CHXFragmentedBuffer**) &m_pFragBuffer);
            if (SUCCEEDED(retVal))
            {
                // Addref the object
                m_pFragBuffer->AddRef();
// XXXMEH - when IHXFragmentedBuffer gets added to the CCF, then
// reinstate this code
//            retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXFragmentedBuffer,
//                                                           (void**) &m_pFragBuffer);
//            if (SUCCEEDED(retVal))
//            {
                // Initialize the number of bytes read
                m_ulFileBytesRead = 0;
                // Set the new state
                m_ulState         = kStateReadRPFileRead;
		if (m_ulReadChunkSize < kRPMinFileReadSize)
		{
		    m_ulReadChunkSize = kRPMinFileReadSize;
		}
		else if (m_ulReadChunkSize > kRPMaxFileReadSize)
		{
		    m_ulReadChunkSize = kRPMaxFileReadSize;
		}
                // Read kRPFileReadSize bytes of the file at a time
                m_pRPFileObject->Read(m_ulReadChunkSize);
            }
        }
        else
        {
            retVal = status;
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->ReadRPFileDone(retVal, NULL);
        }
    }
    else if (m_ulState == kStateStatImageFileInit)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileInit\n"));
#endif
        if (SUCCEEDED(status))
        {
            // Get an IHXFileStat interface
            HX_RELEASE(m_pFileStat);
            retVal = m_pFileObject->QueryInterface(IID_IHXFileStat, (void**) &m_pFileStat);
            if (SUCCEEDED(retVal))
            {
                // Set the new state
                m_ulState = kStateStatImageFileStat;
                // Call Stat()
                m_pFileStat->Stat(this);
            }
        }
        else
        {
            // Set the state saying we failed to stat
            m_ulState = kStateStatImageFileFailClose;
            // Close the file
            m_pFileObject->Close();
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            retVal    = m_pResponse->StatImageFileDone(retVal, m_ulInstance, 0, NULL);
        }
    }
    else if (m_ulState == kStateReadImageFileInit)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadImageFileInit\n"));
#endif
        if (SUCCEEDED(status))
        {
            // Create an IHXFragmentedBuffer object
            HX_RELEASE(m_pFragBuffer);
            retVal = CHXFragmentedBuffer::CreateObject((CHXFragmentedBuffer**) &m_pFragBuffer);
            if (SUCCEEDED(retVal))
            {
                // Addref the object
                m_pFragBuffer->AddRef();
//            retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXFragmentedBuffer,
//                                                           (void**) &m_pFragBuffer);
//            if (SUCCEEDED(retVal))
//            {
                // Init the bytes read counter
                m_ulFileBytesRead = 0;
                // Set the state
                m_ulState         = kStateReadImageFileRead;
		if (m_ulReadChunkSize < kImageMinFileReadSize)
		{
		    m_ulReadChunkSize = kImageMinFileReadSize;
		}
		else if (m_ulReadChunkSize > kImageMaxFileReadSize)
		{
		    m_ulReadChunkSize = kImageMaxFileReadSize;
		}
                // Read the first kImageFileReadSize bytes of the file
                m_pFileObject->Read(m_ulReadChunkSize);
            }
        }
        else
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(status, m_ulInstance, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(retVal, m_ulInstance, NULL);
        }
    }
    else if (m_ulState == kStateShutdownClose ||
             m_ulState == kStateShutdownDone)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownClose\n"));
#endif
        // We got shutdown while we were waiting on a callback,
        // so we do nothing
        retVal = HXR_OK;
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::FileObjectReady(HX_RESULT status, IUnknown* pObject)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::FileObjectReady(0x%08X,0x%08x,0x%08x) ",
               this, status, pObject));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateStatImageFileCreate)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileCreate\n"));
#endif
        // We no longer need the IHXFileSystemManager interface
        HX_RELEASE(m_pFileSystemManager);
        if (SUCCEEDED(status))
        {
            // Get an IHXFileObject interface
            HX_RELEASE(m_pFileObject);
            retVal = pObject->QueryInterface(IID_IHXFileObject, (void**) &m_pFileObject);
            if (SUCCEEDED(retVal))
            {
                // Set the state
                m_ulState = kStateStatImageFileInit;
                // Init the file object
                m_pFileObject->Init(HX_FILE_READ | HX_FILE_BINARY, this);
            }
        }
        else
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(status, m_ulInstance, 0, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(retVal, m_ulInstance, 0, NULL);
        }
    }
    else if (m_ulState == kStateReadImageFileCreate)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadImageFileCreate\n"));
#endif
        // We no longer need the IHXFileSystemManager interface
        HX_RELEASE(m_pFileSystemManager);
        if (SUCCEEDED(status))
        {
            // Get an IHXFileObject interface
            retVal = pObject->QueryInterface(IID_IHXFileObject, (void**) &m_pFileObject);
            if (SUCCEEDED(retVal))
            {
                // Set the state
                m_ulState = kStateReadImageFileInit;
                // Init the file object
                m_pFileObject->Init(HX_FILE_READ | HX_FILE_BINARY, this);
            }
        }
        else
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(status, m_ulInstance, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(retVal, m_ulInstance, NULL);
        }
    }
    else if (m_ulState == kStateShutdownClose ||
             m_ulState == kStateShutdownDone)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownClose\n"));
#endif
        // We got shutdown while we were waiting on a pending
        // callback, so we do nothing here
        retVal = HXR_OK;
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::DirObjectReady(HX_RESULT status, IUnknown* pDirObject)
{
    return HXR_NOTIMPL;
}

STDMETHODIMP PXFileHandler::CloseDone(HX_RESULT status)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::CloseDone(0x%08X,0x%08x) ",
               this, status));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateReadRPFileClose)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadRPFileClose\n"));
#endif
        retVal = HXR_UNEXPECTED;

        if (FAILED(retVal))
        {
            m_pResponse->ReadRPFileDone(retVal, NULL);
        }
    }
    else if (m_ulState == kStateStatImageFileClose)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileClose\n"));
#endif
        // Now we can release the file object
        HX_RELEASE(m_pFileObject);

        if (SUCCEEDED(status))
        {
            // Set the state
            m_ulState = kStateStatImageFileDone;
            // Call the response back
            m_pResponse->StatImageFileDone(status, m_ulInstance, m_ulImageFileSize, m_pImageMimeTypeStr);
        }
        else
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(status, m_ulInstance, 0, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->StatImageFileDone(retVal, m_ulInstance, 0, NULL);
        }
    }
    else if (m_ulState == kStateStatImageFileFailClose)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileFailClose\n"));
#endif
        // Now we can release the file object
        HX_RELEASE(m_pFileObject);

        // Set the state
        m_ulState = kStateError;

        // Call back - we don't care about the status,
        // caused we failed to stat the image anyway
        m_pResponse->StatImageFileDone(HXR_FAIL, m_ulInstance, 0, NULL);
    }
    else if (m_ulState == kStateReadImageFileClose)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadImageFileClose\n"));
#endif
        // Check to make sure we have actually read some bytes
        if (SUCCEEDED(status) && m_ulFileBytesRead > 0)
        {
            // Get an IHXBuffer interface from the IHXFragmentedBuffer
            IHXBuffer* pWholeBuffer = NULL;
            retVal                   = m_pFragBuffer->QueryInterface(IID_IHXBuffer,
                                                                     (void**) &pWholeBuffer);
            if (SUCCEEDED(retVal))
            {
		// Now we can release the fragmented buffer interface
		HX_RELEASE(m_pFragBuffer);
                // Set the state
                m_ulState = kStateReadImageFileDone;
                // We're done, so inform the response interface
                m_pResponse->ReadImageFileDone(retVal, m_ulInstance, pWholeBuffer);
            }
            HX_RELEASE(pWholeBuffer);
        }
        else
        {
            retVal = HXR_FAIL;
        }

        if (FAILED(retVal))
        {
            m_pResponse->ReadImageFileDone(retVal, m_ulInstance, NULL);
        }
    }
    else if (m_ulState == kStateShutdownClose)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownClose\n"));
#endif
        // We got called with Shutdown() and then a 
        // Close() on the file object. Therefore, we need
        // to callback with ShutdownDone() instead of the
        // normal callback.
        //
        // Set the state to the final state
        m_ulState = kStateShutdownDone;
        // Call back with ShutdownDone()
        m_pResponse->ShutdownDone(status, m_ulInstance);
    }
    else if (m_ulState == kStateShutdownDone)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownDone\n"));
#endif
        retVal = HXR_OK;
    }
    else if (m_ulState == kStateReadRPFileHold)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadRPFileHold\n"));
#endif
        m_ulState = kStateShutdownDone;
    }
    else
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "UNEXPECTED STATE\n"));
#endif
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}


STDMETHODIMP PXFileHandler::ReadDone(HX_RESULT status, IHXBuffer* pBuffer)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::ReadDone(0x%08X,0x%08x,0x%08x) ",
               this, status, pBuffer));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateReadRPFileRead)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadRPFileRead\n"));
#endif
        if (SUCCEEDED(status))
        {
            retVal = m_pFragBuffer->Append(pBuffer, 0, pBuffer->GetSize());
            if (SUCCEEDED(retVal))
            {
                m_ulFileBytesRead += pBuffer->GetSize();
            }
        }

        if (SUCCEEDED(retVal))
        {
            if ((SUCCEEDED(status) && pBuffer->GetSize() < m_ulReadChunkSize) || FAILED(status))
            {
                if (m_ulFileBytesRead > 0)
                {
                    // Get an IHXBuffer interface from the IHXFragmentedBuffer
                    IHXBuffer* pWholeBuffer = NULL;
                    retVal                   = m_pFragBuffer->QueryInterface(IID_IHXBuffer,
                                                                             (void**) &pWholeBuffer);
                    if (SUCCEEDED(retVal))
                    {
			// Now we can release the fragmented buffer interface
			HX_RELEASE(m_pFragBuffer);
                        // Set the state
                        m_ulState = kStateReadRPFileHold;
                        // We're done, so inform the response interface
                        m_pResponse->ReadRPFileDone(retVal, pWholeBuffer);
                    }
                    HX_RELEASE(pWholeBuffer);
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                // Set the state (actually, we'll keep it the same)
                m_ulState = kStateReadRPFileRead;
                // Do another read
                m_pRPFileObject->Read(m_ulReadChunkSize);
            }
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            retVal    = m_pResponse->ReadRPFileDone(retVal, NULL);
        }
    }
    else if (m_ulState == kStateReadImageFileRead)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateReadImageFileRead\n"));
#endif
        if (SUCCEEDED(status))
        {
            retVal = m_pFragBuffer->Append(pBuffer, 0, pBuffer->GetSize());
            if (SUCCEEDED(retVal))
            {
                m_ulFileBytesRead += pBuffer->GetSize();
            }
        }

        if (SUCCEEDED(retVal))
        {
            if ((SUCCEEDED(status) && pBuffer->GetSize() < m_ulReadChunkSize) || FAILED(status))
            {
                // Set the state
                m_ulState = kStateReadImageFileClose;
                // Close the file
                m_pFileObject->Close();
            }
            else
            {
                // Set the state (actually, we'll keep it the same)
                m_ulState = kStateReadImageFileRead;
                // Do another read
                m_pFileObject->Read(m_ulReadChunkSize);
            }
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            m_pResponse->ReadImageFileDone(retVal, m_ulInstance, NULL);
        }
    }
    else if (m_ulState == kStateShutdownClose ||
             m_ulState == kStateShutdownDone)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownClose\n"));
#endif
        // We got called with a Shutdown() while we were
        // waiting on a pending ReadDone() call. Therefore,
        // we do nothing here.
        retVal = HXR_OK;
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::WriteDone(HX_RESULT status)
{
    HX_RESULT retVal = HXR_NOTIMPL;

    return retVal;
}

STDMETHODIMP PXFileHandler::SeekDone(HX_RESULT status)
{
    HX_RESULT retVal = HXR_NOTIMPL;

    return retVal;
}

STDMETHODIMP PXFileHandler::StatDone(HX_RESULT status, UINT32 ulSize, UINT32 ulCreationTime,
                                     UINT32 ulAccessTime, UINT32 ulModificationTime, UINT32 ulMode)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::StatDone(0x%08X,0x%08x,%lu,...) ",
               this, status, ulSize));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateStatImageFileStat)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileStat\n"));
#endif
        // We don't need the IHXFileStat interface anymore
        HX_RELEASE(m_pFileStat);

        // Save the size
        m_ulImageFileSize = ulSize;

        if (SUCCEEDED(status))
        {
            // See if we can get an IHXFileMimeMapper interface
            HX_RELEASE(m_pFileMimeMapper);
            HX_RESULT rv = m_pFileObject->QueryInterface(IID_IHXFileMimeMapper,
                                                         (void**) &m_pFileMimeMapper);
            if (SUCCEEDED(rv))
            {
                // Set the state
                m_ulState = kStateStatImageFileMime;
                // Get the mime type
                m_pFileMimeMapper->FindMimeType((const char*) m_pImageNameStr->GetBuffer(), this);
            }
            else
            {
                // Set the state
                m_ulState = kStateStatImageFileClose;
                // Make sure the mime type is NULL'd out
                HX_RELEASE(m_pImageMimeTypeStr);
                // We can close the file
                m_pFileObject->Close();
            }
        }
        else
        {
            m_ulState = kStateError;
            retVal    = m_pResponse->StatImageFileDone(status, m_ulInstance, 0, NULL);
        }

        if (FAILED(retVal))
        {
            m_ulState = kStateError;
            retVal    = m_pResponse->StatImageFileDone(retVal, m_ulInstance, 0, NULL);
        }
    }
    else if (m_ulState == kStateShutdownClose ||
             m_ulState == kStateShutdownDone)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownClose\n"));
#endif
        // We got called with a Shutdown() while we were
        // waiting on a pending StatDone() call. Therefore,
        // we do nothing here.
        retVal = HXR_OK;
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXFileHandler::MimeTypeFound(HX_RESULT status, const char* pMimeType)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\pxff.log", (s, "PXFileHandler::MimeTypeFound(0x%08X,0x%08x,%s) ",
               this, status, pMimeType));
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateStatImageFileMime)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateStatImageFileMime\n"));
#endif
        // Make sure the mime type is NULL'd out
        HX_RELEASE(m_pImageMimeTypeStr);

        if (SUCCEEDED(status) && pMimeType)
        {
            // Put the mime type string into an IHXBuffer
            retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                           (void**) &m_pImageMimeTypeStr);
            if (SUCCEEDED(retVal))
            {
                retVal = m_pImageMimeTypeStr->Set((const BYTE*) pMimeType,
                                                  strlen(pMimeType) + 1);
            }
        }

        // We no longer need the IHXFileMimeMapper interface
        HX_RELEASE(m_pFileMimeMapper);

        // Set the state
        m_ulState = kStateStatImageFileClose;
        // We can close the file
        m_pFileObject->Close();
    }
    else if (m_ulState == kStateShutdownClose ||
             m_ulState == kStateShutdownDone)
    {
#ifdef XXXMEH_DEBUG_LOG
        DEBUG_OUTF("c:\\pxff.log", (s, "state = kStateShutdownClose\n"));
#endif
        // We got called with a Shutdown() while we were
        // waiting on a pending MimeTypeFound() call. Therefore,
        // we do nothing here.
        retVal = HXR_OK;
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

void PXFileHandler::Reset()
{
    m_ulState         = kStateConstructed;
    m_ulFileBytesRead = 0;
    m_ulInstance      = 0;
    m_ulImageFileSize = 0;
    m_ulReadChunkSize = 0;
}

void PXFileHandler::Deallocate()
{
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pRPFileObject);
    HX_RELEASE(m_pResponse);
    HX_RELEASE(m_pCommonClassFactory);
    HX_RELEASE(m_pFileSystemManager);
    HX_RELEASE(m_pFileObject);
    HX_RELEASE(m_pFragBuffer);
    HX_RELEASE(m_pImageNameStr);
    HX_RELEASE(m_pImageMimeTypeStr);
    HX_RELEASE(m_pFileStat);
    HX_RELEASE(m_pFileMimeMapper);
}

HX_RESULT PXFileHandler::CreateImageFile(const char* pszImageName)
{
    HX_RESULT retVal = HXR_OK;

    if (pszImageName)
    {
        if (m_pCommonClassFactory && m_pRPFileObject)
        {
            // First determine if this is relative or absolute
            if (strstr(pszImageName, "://") || pszImageName[0] == '/' || pszImageName[0] == '\\')
            {
                // Protocol string found or mount slash found - it's an "absolute" path - use
                // IHXFileSystemManager::GetNewFileObject to get the
                // file object for this file
                IHXRequest* pRequest = NULL;
                retVal                = m_pCommonClassFactory->CreateInstance(CLSID_IHXRequest,
                                                                              (void **) &pRequest);
                if (SUCCEEDED(retVal))
                {
                    // Set the URL
                    pRequest->SetURL(pszImageName);
                    // Call IHXFileSystemManager::GetNewFileObject()
                    m_pFileSystemManager->GetNewFileObject(pRequest, NULL);
                }
                HX_RELEASE(pRequest);
            }
            else
            {
                // It's a relative file name, so call IHXFileSystemManager::GetRelativeFileObject()
                retVal = m_pFileSystemManager->GetRelativeFileObject(m_pRPFileObject, pszImageName);
            }
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

/************************************************************************
 *      Method:
 *          IHXThreadSafeMethods::IsThreadSafe
 */
STDMETHODIMP_(UINT32)
PXFileHandler::IsThreadSafe()
{
    return HX_THREADSAFE_METHOD_FSR_READDONE;
}

