#define INITGUID
#include <windows.h>
#include <stdio.h>
#include "kmp_pi.h"

#ifdef _DEBUG
#define KBQCP_VERSION      0x7FFFFFFF
#define KBQCP_VERSION_STR "0x7FFFFFFF(Debug)"
#else
#define KBQCP_VERSION      5
#define KBQCP_VERSION_STR "0.05"
#endif

#define KBQCP_COPYRIGHT   "Copyright (C) 1997-2016 QUALCOMM Inc, Kobarin"
#define KBQCP_DESCRIPTION "Qualcomm PureVoice Decoder v" KBQCP_VERSION_STR " based on PureVoice SDK 2.1"

#include "purevoicesdk/Qpl.h"

typedef HRESULT (WINAPI *pfnQPLCreateInstance)(REFCLSID, REFIID, void**);

pfnQPLCreateInstance qpl_QPLCreateInstance = NULL;

static HINSTANCE g_hKpi = NULL;
static HINSTANCE g_hQplDll = NULL;

static BOOL __fastcall LoadQplDll(void)
{
    if(g_hQplDll)return TRUE;
    char szQplPath[MAX_PATH*2+32];
    GetModuleFileName(g_hKpi, szQplPath, sizeof(szQplPath));
    char *p = szQplPath;
    char *last_path_delimiter = NULL;
    while(*p){
        if(IsDBCSLeadByte((BYTE)*p)){
            p+=2;
        }
        else{
            if(*p == '\\'){
                last_path_delimiter = p;
            }
            p++;
        }
    }
    if(last_path_delimiter){
        strcpy(last_path_delimiter+1, "qpl.dll");
    }
    else{
        return FALSE;
    }
    HINSTANCE hDll = LoadLibrary(szQplPath);
    if(!hDll)return FALSE;
    qpl_QPLCreateInstance = (pfnQPLCreateInstance)GetProcAddress(hDll, "QPLCreateInstance");
    if(!qpl_QPLCreateInstance){
        FreeLibrary(hDll);
        return FALSE;
    }
    g_hQplDll = hDll;
    return TRUE;
}
static void __fastcall UnloadQplDll(void)
{
    if(!g_hQplDll)return;
    FreeLibrary(g_hQplDll);
    g_hQplDll = NULL;
}

class KbQplDecoder
{
    IQPLQcpFile1 *m_pQcpFile;
    char m_szFileName[MAX_PATH*2];
    int m_Pos;
public:
    BOOL  __fastcall Open(const char *cszFileName, SOUNDINFO *pInfo);
    DWORD __fastcall Render(BYTE *Buffer, DWORD dwSize);
    DWORD __fastcall SetPosition(DWORD dwPos);
    void  __fastcall Close(void);
    KbQplDecoder(void);
    ~KbQplDecoder(void);
};

KbQplDecoder::KbQplDecoder(void)
{
    m_pQcpFile = NULL;
    m_Pos = 0;
    m_szFileName[0] = 0;
}
KbQplDecoder::~KbQplDecoder(void)
{
    Close();
}
void KbQplDecoder::Close(void)
{
    if(m_szFileName[0]){
        m_szFileName[0] = 0;
    }
    if(m_pQcpFile){
        m_pQcpFile->Release();
        m_pQcpFile = NULL;
    }
    m_Pos = 0;
}
BOOL __fastcall KbQplDecoder::Open(const char *cszFileName, SOUNDINFO *pInfo)
{
    if(!pInfo)return FALSE;
    ZeroMemory(pInfo, sizeof(SOUNDINFO));
    if(!qpl_QPLCreateInstance){
        return FALSE;
    }
    HRESULT hr = qpl_QPLCreateInstance(CLSID_QPLPvQcpFile1, 
                                       IID_IQPLQcpFile1, 
                                       (void**)&m_pQcpFile);
    if(!m_pQcpFile){
        return FALSE;
    }
    if(!m_pQcpFile->Open((const unsigned char*)cszFileName, QPL_READ)){
        m_pQcpFile->Release();
        m_pQcpFile = NULL;
        return FALSE;
    }
    pInfo->dwSamplesPerSec = 8000;
    pInfo->dwChannels = 1;
    pInfo->dwBitsPerSample = 16;
    int bs = m_pQcpFile->GetBlockSize();
    int ln = m_pQcpFile->GetLength();
    pInfo->dwLength = bs*ln;
    pInfo->dwSeekable = 0;
    pInfo->dwUnitRender = bs*2*(8000/1000);
    pInfo->dwReserved1 = 0;
    pInfo->dwReserved2 = 0;
    strcpy(m_szFileName, cszFileName);
    return TRUE;
}
DWORD __fastcall KbQplDecoder::Render(BYTE *Buffer, DWORD dwSize)
{
    int Ret = m_pQcpFile->Read(1, Buffer);
    if(Ret <= 0)Ret = 0;
    DWORD dwRet;
    if(Ret){
        dwRet = Ret - m_Pos;
        m_Pos = Ret;
    }
    else{
        dwRet = 0;
    }
    return dwRet*dwSize;
}
DWORD __fastcall KbQplDecoder::SetPosition(DWORD dwPos)
{
    if(dwPos == 0 && m_Pos == 0){
        return 0;
    }
    if(m_szFileName[0]){
        char szFileName[sizeof(m_szFileName)];
        strcpy(szFileName, m_szFileName);
        Close();
        SOUNDINFO info;
        Open(szFileName, &info);        
    }
    return 0;
}
static void WINAPI kmp_Init (void)
{   
    LoadQplDll();
}
static void WINAPI kmp_Deinit (void)
{
    UnloadQplDll();
}
static HKMP WINAPI kmp_Open (const char *cszFileName, SOUNDINFO *pInfo)
{
    KbQplDecoder *qcp = new KbQplDecoder;
    if(qcp->Open(cszFileName, pInfo)){
        return (HKMP)qcp;
    }
    else{
        delete qcp;
        return NULL;
    }
}

static void WINAPI kmp_Close (HKMP hKMP)
{
    KbQplDecoder *qcp = (KbQplDecoder*)hKMP;
    if(qcp){
        delete qcp;
    }
}

static DWORD WINAPI kmp_Render (HKMP hKMP, BYTE* Buffer, DWORD dwSize)
{
    KbQplDecoder *qcp = (KbQplDecoder*)hKMP;
    if(qcp){
        return qcp->Render(Buffer, dwSize);
    }
    return 0;
}

static DWORD WINAPI kmp_SetPosition (HKMP hKMP, DWORD dwPos)
{
    KbQplDecoder *qcp = (KbQplDecoder*)hKMP;
    if(qcp)
        return qcp->SetPosition(dwPos);
    return 0;
}

BOOL APIENTRY DllMain (HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call){
        case DLL_PROCESS_ATTACH:{
            g_hKpi = (HINSTANCE)hModule;
            DisableThreadLibraryCalls((HMODULE)hModule);
            break;
        }
        case DLL_PROCESS_DETACH:{
            break;
        }
    }
    return TRUE;
}

KMPMODULE* WINAPI kmp_GetTestModule (void)
{
    static const char *ppszSupportExts[] = {".qcp", NULL};
    static KMPMODULE MOD = 
    {
        KMPMODULE_VERSION,
        KBQCP_VERSION,
        KBQCP_COPYRIGHT,
        KBQCP_DESCRIPTION,
        ppszSupportExts,
        1,
        kmp_Init,
        kmp_Deinit,
        kmp_Open,
        NULL,
        kmp_Close,
        kmp_Render,
        kmp_SetPosition
    };
    return &MOD;
}
