#include <windows.h>
#ifdef _DEBUG
#include <crtdbg.h>
#endif
#include "GCASDK/GcaSDK.h"
#include "IGca.h"

//#define IGCA_SUPPORT_COMPRESS  //GcaSDK が Archive() を未実装なので意味なし(そもそも KbMedia Player には必要ない)
//Crypto 関係(GetSpellFromKey/SetCrypto/SetSpell)も動作確認済(プラグインには未実装)

class CGcaWrapper : public IGca
{
private:
    CGca   m_Gca;
    DWORD  m_dwUser;
    BOOL (*m_pfnProgress)(QWORD, QWORD, PVOID);
    int  (*m_pfnMessage)(int, const char *, PVOID);

    static BOOL GcaProgress(QWORD qwProgress, QWORD qwAll, PVOID pContext);
    static int  GcaMessage(int nNum, string strFileName, LPVOID pContext);
    ~CGcaWrapper(void){}
public:
    CGcaWrapper(void){
        m_dwUser = 0;
        m_pfnProgress = NULL;
        m_pfnMessage = NULL;
    }
    DWORD WINAPI GetStartOffset(const char* strPathName){return nsGcaSDK::GetStartOffset(strPathName);}
    BOOL WINAPI GetSpellFromKeyFile(char* pstrSpell, int size, const char* strKeyFile)
    {//strlen(pstrSpell) == 40 固定(?)
        pstrSpell[0] = 0;
        string strSpell;
        BOOL ret = nsGcaSDK::GetSpellFromKeyFile(strSpell, strKeyFile);
        if(ret){
            lstrcpyn(pstrSpell, strSpell.c_str(), size);
        }
        return ret;
    }
    BOOL WINAPI Init(void){return m_Gca.Init();}
    void WINAPI Free(void){m_Gca.Free();}
    void WINAPI SetArcFilePath(const char* strArcFilePath){m_Gca.SetArcFilePath(strArcFilePath);}
    void WINAPI SetOutputPath(const char* strOutputPath){m_Gca.SetOutputPath(strOutputPath);}
    void WINAPI SetRestoreDirectory(BOOL bRestoreDirectory){m_Gca.SetRestoreDirectory(bRestoreDirectory);}
    void WINAPI SetCrypto(BOOL bCrypto){m_Gca.SetCrypto(bCrypto);}
    void WINAPI SetSpell(const char* strSpell){m_Gca.SetSpell(strSpell);}
    void WINAPI SetStartOffset(int nStartOffset){m_Gca.SetStartOffset(nStartOffset);}
    void WINAPI SetProgressProc(BOOL (*pfnProgress)(QWORD, QWORD, PVOID))
    {//単に m_Gca.SetProgressProc(pfnProgress) としてはいけない
        m_pfnProgress = pfnProgress;
        if(pfnProgress)m_Gca.SetProgressProc(GcaProgress);
        else m_Gca.SetProgressProc(NULL);
    }
    void WINAPI SetMessageProc(int (*pfnMessage)(int, const char *, PVOID))
    {
        m_pfnMessage = pfnMessage;
        if(pfnMessage)m_Gca.SetMessageProc(GcaMessage);
        else m_Gca.SetMessageProc(NULL);
    }
    int WINAPI GetArcType(void){return m_Gca.GetArcType();}
    int WINAPI GetNumFiles(void){return m_Gca.GetNumFiles();}
    void WINAPI GetFileName(int n, char *pszFileName, int size)
    {//ファイル名なので size は MAX_PATH*2 以上あれば十分と思われる
        lstrcpyn(pszFileName, m_Gca.GetFileName(n).c_str(), size);
    }
    DWORD WINAPI GetFileCRC(int n){return m_Gca.GetFileCRC(n);}
    QWORD WINAPI GetDataSize(int n){return m_Gca.GetDataSize(n);}
    QWORD WINAPI GetFileSize(int n){return m_Gca.GetFileSize(n);}
    DWORD WINAPI GetFileAttributes(int n){return m_Gca.GetFileAttributes(n);}
    FILETIME WINAPI GetFileTime(int n){return m_Gca.GetFileTime(n);}
    BOOL WINAPI OpenArchive(void){return m_Gca.OpenArchive();}
    BOOL WINAPI CloseArchive(void){return m_Gca.CloseArchive();}
    BOOL WINAPI Extract(void){return m_Gca.Extract();}
    BOOL WINAPI ExtractFile(int n){return m_Gca.ExtractFile(n);}
    BOOL WINAPI ExtractFileToMemory(int n, BYTE *pBuffer){return m_Gca.ExtractFileToMemory(n, pBuffer);}
    void  WINAPI SetUser(DWORD dwUser){m_dwUser = dwUser;m_Gca.SetUser((DWORD)this);}//m_Gca.SetUser(dwUser) ではない
    DWORD WINAPI GetUser(void){return m_dwUser;}//return m_Gca.GetUser() ではない

    //圧縮関係(機能しない)(GcaSDK が Archive() 未実装なので)
#ifdef IGCA_SUPPORT_COMPRESS
    //CGca の関連メソッドを呼ぶようにするが Archive() が未実装なので無意味
    void WINAPI SetOrder(const char** astrOrder)
    {//動作未確認(確認のしようがない)
        vector<string> order;
        for(int i = 0; astrOrder[i]; i++){
            order.push_back(astrOrder[i]);
        }
        m_Gca.SetOrder(order);
    }
    void WINAPI SetCompress(BOOL bCompress){m_Gca.SetCompress(bCompress);}
    void WINAPI SetArcType(int nType){m_Gca.SetArcType(nType);}
    BOOL WINAPI Archive(void){/*return m_Gca.Archive();*/return FALSE;} //GcaSDK が未実装なので常に FALSE を返す
#else //何もしない
    void WINAPI SetOrder(const char** astrOrder){}
    void WINAPI SetCompress(BOOL bCompress){}
    void WINAPI SetArcType(int nType){}
    BOOL WINAPI Archive(void){return FALSE;} //GcaSDK が未実装
#endif
    //圧縮関係ここまで
    //IGca 専用
    void WINAPI Release(void){delete this;}
};
BOOL CGcaWrapper::GcaProgress(QWORD qwProgress, QWORD qwAll, PVOID pContext)
{
    CGca *pGca = (CGca*)pContext;
    CGcaWrapper *pWrapper = (CGcaWrapper*)pGca->GetUser();
    return pWrapper->m_pfnProgress(qwProgress, qwAll, (IGca*)pWrapper);
}
int CGcaWrapper::GcaMessage(int nNum, string strFileName, LPVOID pContext)
{
    CGca *pGca = (CGca*)pContext;
    CGcaWrapper *pWrapper = (CGcaWrapper*)(pGca->GetUser());
    return pWrapper->m_pfnMessage(nNum, strFileName.c_str(), (IGca*)pWrapper);
}

extern "C" IGca* WINAPI CreateGca(int version)
{
    if(version != IGCA_INTERFACE_VERSION)return NULL;
    return new CGcaWrapper();
}

BOOL APIENTRY DllMain (HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:{
#ifdef _DEBUG
    //メモリリーク検出
        int DbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
        //DbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
        DbgFlag |= _CRTDBG_LEAK_CHECK_DF;
        //DbgFlag |= _CRTDBG_CHECK_ALWAYS_DF;
        _CrtSetDbgFlag(DbgFlag);
#endif
        DisableThreadLibraryCalls((HMODULE)hModule);
        break;
    }
    case DLL_PROCESS_DETACH:
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}
