// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


//+-----------------------------------------------------------------------
//

//
//  Abstract:
//      Routines to write generated C++ code.
//
//------------------------------------------------------------------------

#include "precomp.h"

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::CCodeGen
//
//  Synopsis:  ctor
//
//-----------------------------------------------------------------------------
CCodeGen::CCodeGen()
{
    m_pFileCpp = NULL;
    m_pFileHpp = NULL;
    m_pDevice = NULL;
    m_cbTotal = 0;
}

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::~CCodeGen
//
//  Synopsis:  dtor
//
//-----------------------------------------------------------------------------
CCodeGen::~CCodeGen()
{
    if (m_pFileCpp)
    {
        fprintf(m_pFileCpp, "// Total data size = %d (0x%08x) bytes.\n", m_cbTotal, m_cbTotal);
        fclose(m_pFileCpp);
    }
    if (m_pFileHpp)
    {
        fclose(m_pFileHpp);
    }
    ReleaseInterface(m_pDevice);
}


//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::Initialize
//
//  Synopsis:  Create an instance of CCodeGen with text file opened for writing.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::Initialize(
    __in char const *pszFileNameCpp,
    __in char const *pszFileNameHpp
    )
{
    HRESULT hr = S_OK;

    m_pFileCpp = fopen(pszFileNameCpp, "wt");
    if (!m_pFileCpp)
    {
        printf("Can't open %s for writing\n", pszFileNameCpp);
        IFC(E_FAIL);
    }

    m_pFileHpp = fopen(pszFileNameHpp, "wt");
    if (!m_pFileHpp)
    {
        printf("Can't open %s for writing\n", pszFileNameHpp);
        IFC(E_FAIL);
    }

    IFC(CFakeDevice::Create(&m_pDevice));

    fprintf(m_pFileCpp, sc_szTitle);
    fprintf(m_pFileCpp, "#include \"precomp.hpp\"\n\n");

    fprintf(m_pFileHpp, sc_szTitle);
    fprintf(m_pFileHpp, "#pragma once\n\n");

Cleanup:
    return hr;
}

char const CCodeGen::sc_szTitle[] =
    "//+-----------------------------------------------------------------------\n"
    "//\n"
    "\n"
    "//\n"
    "//  This file is automatically generated.\n"
    "//  Please do not edit it directly.\n"
    "//  Instead, edit *.fx files in \"Shaders\" folder and run ShaderGen.exe.\n"
    "//  ShaderGen.exe also should be in \"Shaders\" folder.\n"
    "//  To build ShaderGen.exe:\n"
    "//      >cd Shaders\\ShaderGen\n"
    "//      >bcz\n"
    "//      >.. //ShaderGen.exe should appear here\n"
    "//  If you are adding/removing/renaming *.fx files,\n"
    "//  you need to edit ShaderGen/main.cpp.\n"
    "//\n"
    "//------------------------------------------------------------------------\n"
    "\n"
;

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::CompileEffect
//
//  Synopsis:  Create an instance of CCodeGen with text file opened for writing.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::CompileEffect(
    __in WCHAR const *pszEffectFileName,
    __in char const *pszEffectName
    )
{
    HRESULT hr = S_OK;

    m_pszEffectName = pszEffectName;

    ID3DXBuffer *pCompilationErrors = NULL;

    IFC(D3DXCreateEffectFromFile(
        m_pDevice,
        pszEffectFileName,
        NULL,   //CONST D3DXMACRO* pDefines,
        NULL,   //LPD3DXINCLUDE pInclude,
        0,      //DWORD Flags: D3DXSHADER_DEBUG/SKIPVALIDATION/SKIPOPTIMIZATION 
        NULL,   //LPD3DXEFFECTPOOL pPool,
        &m_pEffect,
        &pCompilationErrors
        ));

    IFC(WriteEffect());

Cleanup:
    ReleaseInterface(m_pEffect);
    if (pCompilationErrors != NULL)
    {
        // Output compiler errors

        char *szErrors = (char *)pCompilationErrors->GetBufferPointer();
        printf("%s", szErrors);

        pCompilationErrors->Release();
    }
    return hr;
}

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::WriteEffect
//
//  Synopsis:
//      Traverse given ID3DXEffect, pointed by m_pEffect.
//      Generate C++ code for its components.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::WriteEffect()
{

    HRESULT hr = S_OK;
    D3DXEFFECT_DESC descEffect;

    IFC(m_pEffect->GetDesc(&descEffect));

    for (UINT i = 0; i < descEffect.Techniques; i++)
    {
        m_hTechnique = m_pEffect->GetTechnique(i);
        IFH(m_hTechnique);
        IFC(WriteTechnique());
    }

Cleanup:
    return hr;
}

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::WriteTechnique
//
//  Synopsis:
//      Traverse current technique, pointed by m_hTechnique.
//      Generate C++ code for its components.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::WriteTechnique()
{
    HRESULT hr = S_OK;

    IFC(m_pEffect->GetTechniqueDesc(m_hTechnique, &m_descTechnique));

    for (UINT i = 0; i < m_descTechnique.Passes; i++)
    {
        m_hPass = m_pEffect->GetPass(m_hTechnique, i);
        IFH(m_hPass);
        IFC(WritePass());
    }

Cleanup:
    return hr;
}

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::WritePass
//
//  Synopsis:
//      Traverse current pass, pointed by m_hPass.
//      Generate C++ code for its components.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::WritePass()
{
    HRESULT hr = S_OK;

    IFC(m_pEffect->GetPassDesc(m_hPass, &m_descPass));

    if (m_descPass.pPixelShaderFunction != NULL)
    {
        IFC(WritePixelShader());
    }

    if (m_descPass.pVertexShaderFunction != NULL)
    {
        IFC(WriteVertexShader());
    }

Cleanup:
    return hr;
}    

//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::WritePixelShader
//
//  Synopsis:
//      Generate code for pixel shader.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::WritePixelShader()
{
    HRESULT hr = S_OK;

    const DWORD* pFunction = m_descPass.pPixelShaderFunction;

    // form shader data array definition header, like
    // const DWORD
    // g_PixelShader_Foo_Tech_Pass[] =
    fprintf(
        m_pFileCpp,
        "const DWORD\ng_PixelShader_%s_%s_%s[] =\n",
        m_pszEffectName,
        m_descTechnique.Name,
        m_descPass.Name
        );

    fprintf(
        m_pFileHpp,
        "extern const DWORD g_PixelShader_%s_%s_%s[];\n",
        m_pszEffectName,
        m_descTechnique.Name,
        m_descPass.Name
        );

    WriteDwordArray(pFunction);

//Cleanup:
    return hr;
}


//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::WriteVertexShader
//
//  Synopsis:
//      Generate code for vertex shader.
//
//-----------------------------------------------------------------------------
HRESULT
CCodeGen::WriteVertexShader()
{
    HRESULT hr = S_OK;

    const DWORD* pFunction = m_descPass.pVertexShaderFunction;

    // form shader data array definition header, like
    // const DWORD
    // g_VertexShader_Foo_Tech_Pass[] =
    fprintf(
        m_pFileCpp,
        "const DWORD\ng_VertexShader_%s_%s_%s[] =\n",
        m_pszEffectName,
        m_descTechnique.Name,
        m_descPass.Name
        );

    fprintf(
        m_pFileHpp,
        "extern const DWORD g_VertexShader_%s_%s_%s[];\n",
        m_pszEffectName,
        m_descTechnique.Name,
        m_descPass.Name
        );

    WriteDwordArray(pFunction);

//Cleanup:
    return hr;
}


//+----------------------------------------------------------------------------
//
//  Member:    CCodeGen::WriteDwordArray
//
//  Synopsis:
//      Helper for WritePixelShader() and WriteVertexShader().
//      Generate array data C++ code.
//
//-----------------------------------------------------------------------------
void
CCodeGen::WriteDwordArray(
    __in DWORD const *pFunction
    )
{
    // open array data
    fprintf(m_pFileCpp, "{\n");

    // write data array, uRowSize DWORDs per line
    static const UINT uRowSize = 6;

    UINT cbSize = D3DXGetShaderSize(pFunction);

    for (UINT i = 0, n = cbSize/sizeof(DWORD); i < n; i++)
    {
        UINT j = i % uRowSize;
        if (j == 0)
        {
            fprintf(m_pFileCpp, "    ");
        }
        fprintf(m_pFileCpp, "0x%08x", pFunction[i]);
        if (i+1 == n)
        {
            fprintf(m_pFileCpp, "\n");
        }
        else
        {
            fprintf(m_pFileCpp, ",");
            if (j+1 == uRowSize)
            {
                fprintf(m_pFileCpp, "\n");
            }
            else
            {
                fprintf(m_pFileCpp, " ");
            }
        }
    }

    // close array data
    fprintf(m_pFileCpp, "};\n\n");

    // update total size of all arrays
    m_cbTotal += cbSize;
}

