//
// d3dCallback.cpp
//
// Source for DLL exports.  Dispatches DirectX calls to the appropriate parts of the AI
// Written by Matthew Fisher
//

#include "Main.h"
#include "d3d9Callback.h"

//
// Dummy function needed by D3D9GraphicsDevice
//
extern "C" {
IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion)
{
    SignalError("Unexpected call to Direct3DCreate9");
    return NULL;
}
}

//
// DLL entry point
//
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        {
            //g_Context = NULL;
        }
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

void SafeDLLInitialize()
{
    if(g_Context == NULL)
    {
        g_Context = new GlobalContext;
        g_Context->Init();
    }
}

D3D9CALLBACK_API void D3D9CallbackInitialize()
{
    
}

D3D9CALLBACK_API void D3D9CallbackFreeMemory()
{
    if(g_Context != NULL)
    {
        g_Context->Files.Events << "Freeing DLL memory\n";
        delete g_Context;
        g_Context = NULL;
    }
}

/*D3D9CALLBACK_API void BeginSingleFrameCapture()
{
    //
    // Open the single frame log file
    //
    g_Context->Files.CurrentFrameDump.open(FrameIndex());

#ifndef ULTRA_FAST
    g_ReportingEvents = true;
#endif
}

D3D9CALLBACK_API void EndSingleFrameCapture()
{
    //
    // Open the multi-frame log file
    //
    g_Context->Files.CurrentFrameAllEvents.close();
    g_Context->Files.CurrentFrameAllEvents.open((g_Context->Parameters.OutputFileDirectory + String("ReportedEventsAfterCapture.txt")).CString());

#ifndef ULTRA_FAST
    g_ReportingEvents = false;
#endif
}*/

D3D9CALLBACK_API bool ReportUnlockTexture(D3DSURFACE_DESC &Desc, Bitmap &Bmp, HANDLE TextureHandle)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "Unlock Texture " << TextureHandle << endl;
    }
    if(g_Context->Parameters.DebugTextureCreation)
    {
        g_Context->Files.Assert << "Unlock Texture " << TextureHandle << endl;
    }

    //
    // Tell the State Manager to create or update this texture and return said texture
    //
    Texture *ThisTexture = g_Context->Managers.State.UnlockTexture(Desc, Bmp, TextureHandle);

    return false;
}

D3D9CALLBACK_API void ReportLockVertexBuffer(BufferLockData &Data, D3DVERTEXBUFFER_DESC &Desc)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "LockVBuffer, Handle: " << Data.Handle << endl;
    }

    //
    // Tell the State Manager to create or update this vertex buffer
    //
    g_Context->Managers.State.LockVBuffer(Data, Desc);
}

D3D9CALLBACK_API void ReportLockIndexBuffer(BufferLockData &Data, D3DINDEXBUFFER_DESC &Desc)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "LockIBuffer, Handle: " << Data.Handle << endl;
    }

    //
    // Tell the State Manager to create or update this index buffer
    //
    g_Context->Managers.State.LockIBuffer(Data, Desc);
}

D3D9CALLBACK_API void ReportDestroy(HANDLE Handle)
{
    //if(!g_Context->Managers.State.DestroyVBuffer(Handle))
    {
        //if(!g_Context->Managers.State.DestroyIBuffer(Handle))
        {
            if(!g_Context->Managers.State.DestroyTexture(Handle))
            {
                //
                // We will recieve this a lot of unknown destroy handles since we are ignoring very small textures
                //
                //g_Context->Files.Assert << "Unknown destroy handle: " << Handle << endl;
            }
        }
    }
}

D3D9CALLBACK_API void ReportCreateDevice(D3D9Base::LPDIRECT3DDEVICE9 Device, ID3D9DeviceOverlay *Overlay)
{
    SafeDLLInitialize();

    g_Context->Graphics.SetDevice(Device);
    g_Context->Graphics.SetOverlay(Overlay);
}

D3D9CALLBACK_API void ReportFreeDevice()
{
    //g_Context->Files.Events << "Freeing device\n";
    g_Context->Managers.Render.ReleaseStorageObjects();
    //g_Context->Managers.State.VertexShaderSimulator.OutputNewShaderList();
}

D3D9CALLBACK_API void ReportSetTexture(DWORD Stage, HANDLE *SurfaceHandles, UINT SurfaceHandleCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetTexture Stage=" << Stage << " Handle=" << DWORD(SurfaceHandles[0]) << endl;
    }
    if(g_Context->Parameters.DebugTextureCreation)
    {
        g_Context->Files.Assert << "SetTexture " << SurfaceHandles[0] << endl;
    }
    g_Context->Managers.State.SetTexture(Stage, SurfaceHandles, SurfaceHandleCount);
}

D3D9CALLBACK_API void ReportSetViewport(CONST D3DVIEWPORT9 *pViewport)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetViewport " << pViewport->X << ' ' << pViewport->Y << ' ' << pViewport->Width << ' ' << pViewport->Height << ' ' << pViewport->MinZ << ' ' << pViewport->MaxZ << endl;
    }
    g_Context->Managers.State.SetViewport(pViewport);
}

D3D9CALLBACK_API void ReportSetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix)
{
    g_Context->Managers.State.SetTransform(State, pMatrix);
}

D3D9CALLBACK_API void ReportSetVertexDeclaration(D3DVERTEXELEMENT9 *Elements, UINT ElementCount)
{
    if(g_ReportingEvents)
    {
        if(ElementCount <= 1 || Elements == NULL)
        {
            g_Context->Files.CurrentFrameAllEvents << "SetVertexDeclaration NULL\n";
        }
        else
        {
            g_Context->Files.CurrentFrameAllEvents << "SetVertexDeclaration Elements=" << ElementCount << "\tHash=" << Utility::Hash32((BYTE *)Elements, sizeof(D3DVERTEXELEMENT9) * (ElementCount - 1)) << endl;
        }
    }
    g_Context->Managers.State.SetVertexDeclaration(Elements, ElementCount);
}

D3D9CALLBACK_API void ReportSetFVF(DWORD FVF)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetFVF ";
        g_Context->Files.CurrentFrameAllEvents << "Unknown\n";
        g_Context->Files.Assert << "Unknown FVF " << FVF << endl;
    }
    g_Context->Managers.State.SetFVF(FVF);
}

D3D9CALLBACK_API void ReportSetStreamSource(UINT StreamNumber, HANDLE VBufferHandle, UINT OffsetInBytes, UINT Stride)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetStreamSource\t" << StreamNumber << '\t' << VBufferHandle << '\t' << OffsetInBytes << '\t' << Stride << endl;
    }
    g_Context->Managers.State.SetStreamSource(StreamNumber, VBufferHandle, OffsetInBytes, Stride);
}

D3D9CALLBACK_API void ReportSetStreamSourceFreq(UINT StreamNumber, UINT FrequencyParameter)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetStreamSourceFreq " << StreamNumber << " " << FrequencyParameter << endl;
    }
}

D3D9CALLBACK_API void ReportSetLight(DWORD Index, CONST D3DLIGHT9* pLight)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetLight\n";
    }
    g_Context->Managers.State.SetLight(Index, pLight);
}

D3D9CALLBACK_API void ReportLightEnable(DWORD Index, BOOL Enable)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "LightEnable " << Index << ' ' << Enable << endl;
    }
}

D3D9CALLBACK_API void ReportSetMaterial(CONST D3DMATERIAL9* pMaterial)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetMaterial\n";
    }
    g_Context->Managers.State.SetMaterial(pMaterial);
}

D3D9CALLBACK_API bool ReportDrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "DrawPrimitive\n";
    }
    return g_Context->Managers.Render.DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount);
}

D3D9CALLBACK_API bool ReportDrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "DrawIndexedPrimitive Type=" << PrimitiveType << " BaseVertexIndex=" << BaseVertexIndex << " MinIndex=" << MinIndex;
        g_Context->Files.CurrentFrameAllEvents << " NumVertices=" << NumVertices << " StartIndex=" << StartIndex << " PrimitiveCount=" << PrimitiveCount << endl;
    }
    return g_Context->Managers.Render.DrawIndexedPrimitive(PrimitiveType, BaseVertexIndex, MinIndex, NumVertices, StartIndex, PrimitiveCount);
}

D3D9CALLBACK_API bool PreRenderQuery(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount)
{
    bool Result = g_Context->Managers.Render.PreRenderQuery(PrimitiveType, PrimitiveCount);
    if(g_ReportingEvents)
    {
        if(Result)
        {
            g_Context->Files.CurrentFrameAllEvents << "Skipping Render\n";
        }
    }
    return Result;
}

D3D9CALLBACK_API bool ReportDrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "DrawPrimitiveUP\n";
    }
    return g_Context->Managers.Render.DrawPrimitiveUP(PrimitiveType, PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride);
}

D3D9CALLBACK_API bool ReportDrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, CONST void* pIndexData, D3DFORMAT IndexDataFormat, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "DrawPrimitiveUP\n";
    }
    return g_Context->Managers.Render.DrawIndexedPrimitiveUP(PrimitiveType, MinVertexIndex, NumVertices, PrimitiveCount, pIndexData, IndexDataFormat, pVertexStreamZeroData, VertexStreamZeroStride);
}

D3D9CALLBACK_API void ReportSetRenderState(D3DRENDERSTATETYPE State, DWORD Value)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetRenderState " << State << ' ' << Value << endl;
    }
    g_Context->Managers.State.SetRenderState(State, Value);
}

D3D9CALLBACK_API void ReportSetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetTextureStageState " << Stage << ' ' << Type << ' ' << Value << endl;
    }
    g_Context->Managers.State.SetTextureStageState(Stage, Type, Value);
}

D3D9CALLBACK_API void ReportSetIndices(HANDLE IBufferHandle)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetIndices\t" << IBufferHandle << "\n";
    }
    g_Context->Managers.State.SetIndices(IBufferHandle);
}

D3D9CALLBACK_API void ReportCreateVertexShader(CONST DWORD* pFunction, HANDLE Shader)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "CreateVertexShader " << Shader << endl;
    }
    g_Context->Managers.State.CreateVertexShader(pFunction, Shader);
}

D3D9CALLBACK_API void ReportSetVertexShader(HANDLE Shader)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetVertexShader " << Shader << endl;
    }
    g_Context->Managers.State.SetVertexShader(Shader);
}

D3D9CALLBACK_API void ReportCreatePixelShader(CONST DWORD* pFunction, HANDLE Shader)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "CreatePixelShader " << Shader << endl;
    }
    g_Context->Managers.State.CreatePixelShader(pFunction, Shader);
}

D3D9CALLBACK_API void ReportSetPixelShader(HANDLE Shader)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetPixelShader " << Shader << endl;
    }
    g_Context->Managers.State.SetPixelShader(Shader);
}

D3D9CALLBACK_API void ReportSetVertexShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetVertexShaderConstantF Start=" << StartRegister << ", Count=" << Vector4fCount << endl;
        for(UINT i = 0; i < Vector4fCount; i++)
        {
            g_Context->Files.CurrentFrameAllEvents << pConstantData[i * 4 + 0] << '\t' 
                        << pConstantData[i * 4 + 1] << '\t' 
                        << pConstantData[i * 4 + 2] << '\t' 
                        << pConstantData[i * 4 + 3] << endl;
        }
    }
    g_Context->Managers.State.SetVertexShaderConstantF(StartRegister, pConstantData, Vector4fCount);
}

D3D9CALLBACK_API void ReportSetVertexShaderConstantI(UINT StartRegister, CONST UINT* pConstantData, UINT Vector4iCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetVertexShaderConstantI " << Vector4iCount << endl;
        for(UINT i = 0; i < Vector4iCount; i++)
        {
            g_Context->Files.CurrentFrameAllEvents << pConstantData[i * 4 + 0] << '\t' 
                        << pConstantData[i * 4 + 1] << '\t' 
                        << pConstantData[i * 4 + 2] << '\t' 
                        << pConstantData[i * 4 + 3] << endl;
        }
    }
    g_Context->Managers.State.SetVertexShaderConstantI(StartRegister, pConstantData, Vector4iCount);
}

D3D9CALLBACK_API void ReportSetVertexShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT BoolCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetVertexShaderConstantB " << BoolCount << endl;
        for(UINT i = 0; i < BoolCount; i++)
        {
            g_Context->Files.CurrentFrameAllEvents << pConstantData[i] << endl;
        }
    }
    g_Context->Managers.State.SetVertexShaderConstantB(StartRegister, pConstantData, BoolCount);
}

D3D9CALLBACK_API void ReportSetPixelShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetPixelShaderConstantF Start=" << StartRegister << " Count=" << Vector4fCount << endl;
        for(UINT i = 0; i < Vector4fCount; i++)
        {
            g_Context->Files.CurrentFrameAllEvents << pConstantData[i * 4 + 0] << '\t' 
                        << pConstantData[i * 4 + 1] << '\t' 
                        << pConstantData[i * 4 + 2] << '\t' 
                        << pConstantData[i * 4 + 3] << endl;
        }
    }
    g_Context->Managers.State.SetPixelShaderConstantF(StartRegister, pConstantData, Vector4fCount);
}

D3D9CALLBACK_API void ReportSetPixelShaderConstantI(UINT StartRegister, CONST UINT* pConstantData, UINT Vector4iCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetPixelShaderConstantI " << Vector4iCount << endl;
        for(UINT i = 0; i < Vector4iCount; i++)
        {
            g_Context->Files.CurrentFrameAllEvents << pConstantData[i * 4 + 0] << '\t' 
                        << pConstantData[i * 4 + 1] << '\t' 
                        << pConstantData[i * 4 + 2] << '\t' 
                        << pConstantData[i * 4 + 3] << endl;
        }
    }
    g_Context->Managers.State.SetPixelShaderConstantI(StartRegister, pConstantData, Vector4iCount);
}

D3D9CALLBACK_API void ReportSetPixelShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT BoolCount)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetPixelShaderConstantB " << BoolCount << endl;
        for(UINT i = 0; i < BoolCount; i++)
        {
            g_Context->Files.CurrentFrameAllEvents << pConstantData[i] << endl;
        }
    }
    g_Context->Managers.State.SetPixelShaderConstantB(StartRegister, pConstantData, BoolCount);
}

D3D9CALLBACK_API void ReportSetRenderTarget(DWORD RenderTargetIndex, HANDLE Surface)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "SetRenderTarget " << Surface << endl;
    }
}

D3D9CALLBACK_API void ReportPresent(CONST RECT* pSourceRect,CONST RECT* pDestRect)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "Present\n";
    }
}

D3D9CALLBACK_API void ReportClear(DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil)
{
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "Clear\n";
    }
}

D3D9CALLBACK_API void ReportBeginScene()
{
    g_Context->Controller.FrameStart();
    if(g_ReportingEvents)
    {
        g_Context->Files.CurrentFrameAllEvents << "BeginScene\n";
    }
}

D3D9CALLBACK_API void ReportEndScene()
{
    g_Context->Controller.FrameEnd();
}