// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "stdafx.h"
#include "string"
#include "sstream"
#include "fstream"

namespace std
{
    typedef std::basic_string<TCHAR> tstring;
    typedef std::basic_ostringstream<TCHAR> tostringstream;
}

using namespace std;

const TCHAR* fileHeader = _T("// Copyright (c) Microsoft. All rights reserved.\n")
_T("// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n")
_T("\n")
_T("// THIS FILE IS AUTOGENERATED!\n")
_T("// DO NOT EDIT!\n")
_T("//\n")
_T("\n")
_T("#ifndef MICROMOCKCALLMACROS_H\n")
_T("#define MICROMOCKCALLMACROS_H\n")
_T("\n")
_T("#pragma once\n")
_T("\n");


void GenerateMockCallMacros(_In_ size_t supportedArgCount)
{
    FILE* outFile = NULL;

    _tfopen_s(&outFile, _T("micromockcallmacros.h"), _T("wt"));
    if (NULL != outFile)
    {
        _ftprintf(outFile, fileHeader);

        for (UINT currentArgCount = 0; currentArgCount <= supportedArgCount; currentArgCount++)
        {
            tostringstream argsWithValuesSignatureStream;
            tostringstream argAndValuesMacroStream;
            tstring argsWithValuesSignature;
            tstring argAndValuesMacroString;

            for (UINT i = 1; i <= currentArgCount; i++)
            {
                if (i > 1)
                {
                    argsWithValuesSignatureStream << _T(", ");
                }
                argsWithValuesSignatureStream << _T("arg") << i << _T("Type arg") << i << _T("Value");
                argAndValuesMacroStream << _T(", arg") << i << _T("Type, arg") << i << _T("Value");
            }

            argsWithValuesSignature = argsWithValuesSignatureStream.str();

            argAndValuesMacroString = argAndValuesMacroStream.str();

            _ftprintf(outFile, _T("#define MOCK_ANY_METHOD_%u(static_, STATIC_, prefix, resultType, name%s) \\\n"), currentArgCount, argAndValuesMacroString.c_str());
            _ftprintf(outFile, _T("static_ CMockMethodCall<resultType>& prefix Expected_##name(%s) \\\n"), argsWithValuesSignature.c_str());
            _ftprintf(outFile, _T("{ \\\n"));
            if (currentArgCount > 0)
            {
                _ftprintf(outFile, _T("    CMockCallArgumentBase* args[%u]; \\\n"), currentArgCount);
            }
            for (UINT i = 1; i <= currentArgCount; i++)
            {
                _ftprintf(outFile, _T("    args[%u] = new CMockCallArgument<arg%uType>(arg%uValue); \\\n"), i - 1, i, i);
            }
            _ftprintf(outFile, _T("    CMockMethodCall<resultType>* mockMethodCall = \\\n"));
            _ftprintf(outFile, _T("        new CMockMethodCall<resultType>(_T(#name), %u, %s); \\\n"), currentArgCount, (currentArgCount > 0) ? _T("args") : _T("NULL"));
            _ftprintf(outFile, _T("        RECORD_EXPECTED_##STATIC_##MOCK_CALL(mockMethodCall); \\\n"));
            _ftprintf(outFile, _T("    return *mockMethodCall; \\\n"));
            _ftprintf(outFile, _T("} \\\n"));
            _ftprintf(outFile, _T("static_ resultType prefix name(%s) \\\n"), argsWithValuesSignature.c_str());
            _ftprintf(outFile, _T("{ \\\n"));
            if (currentArgCount > 0)
            {
                _ftprintf(outFile, _T("    CMockCallArgumentBase* args[%u]; \\\n"), currentArgCount);
            }
            for (UINT i = 1; i <= currentArgCount; i++)
            {
                _ftprintf(outFile, _T("    args[%u] = new CMockCallArgument<arg%uType>(arg%uValue); \\\n"), i - 1, i, i);
            }
            _ftprintf(outFile, _T("    CMockMethodCallBase* mockMethodCall = \\\n"));
            _ftprintf(outFile, _T("        new CMockMethodCall<resultType>(_T(#name), %u, %s); \\\n"), currentArgCount, (currentArgCount > 0) ? _T("args") : _T("NULL"));
            _ftprintf(outFile, _T("        bool failed=false; \\\n"));
            _ftprintf(outFile, _T("        CMockValueBase* result = RECORD_ACTUAL_##STATIC_##MOCK_CALL(mockMethodCall, &failed); \\\n"));
            _ftprintf(outFile, _T("        if((result!=NULL)&&(failed)) return dynamic_cast<CMockValue<resultType>*>(result)->GetValue();\n"));
            _ftprintf(outFile, _T("\n"));

            // regular member function mock
            _ftprintf(outFile, _T("#define MOCK_METHOD_%u(prefix, resultType, name%s) \\\n"), currentArgCount, argAndValuesMacroString.c_str());
            _ftprintf(outFile, _T("MOCK_ANY_METHOD_%u(,,prefix, resultType, name%s) \n"), currentArgCount, argAndValuesMacroString.c_str());
            _ftprintf(outFile, _T("\n"));

            // static function mock
            _ftprintf(outFile, _T("#define MOCK_STATIC_METHOD_%u(prefix, resultType, name%s) \\\n"), currentArgCount, argAndValuesMacroString.c_str());
            _ftprintf(outFile, _T("MOCK_ANY_METHOD_%u(static, STATIC_, prefix, resultType, name%s) \n"), currentArgCount, argAndValuesMacroString.c_str());
            _ftprintf(outFile, _T("\n"));

            // global mock declaration
            _ftprintf(outFile, _T("#define DECLARE_GLOBAL_MOCK_METHOD_%u(mockClass, prefix, resultType, name%s) \\\n"), currentArgCount, argAndValuesMacroString.c_str());
            _ftprintf(outFile, _T("prefix resultType name(%s) \\\n"), argsWithValuesSignature.c_str());
            _ftprintf(outFile, _T("{ \\\n"));
            _ftprintf(outFile, _T("    return mockClass::name("));
            for (UINT i = 1; i <= currentArgCount; i++)
            {
                if (i > 1)
                {
                    _ftprintf(outFile, _T(","));
                }
                _ftprintf(outFile, _T("arg%uValue"), i);
            }
            _ftprintf(outFile, _T(");\\\n"));
            
            _ftprintf(outFile, _T("} \\\n"));
            _ftprintf(outFile, _T("\n"));
        }

        _ftprintf(outFile, _T("#endif // MICROMOCKCALLMACROS_H\n"));
        _ftprintf(outFile, _T("\n"));

        fclose(outFile);
    }
}

const char* CfileHeader = "// Copyright (c) Microsoft. All rights reserved.\n"
"// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n"
"\n"
"\n"
"// THIS FILE IS AUTOGENERATED!\n"
"// DO NOT EDIT!\n"
"\n"
"\n"
"#ifndef TIMEDISCRETEMICROMOCKCALLMACROS_H\n"
"#define TIMEDISCRETEMICROMOCKCALLMACROS_H\n"
"\n"
"#pragma once\n"
"\n";

#define tab (char)(9)
void GenerateTimeDiscreteMockCallMacros(_In_ size_t supportedArgCount)
{
    unsigned int i,j;
    /*intended as ANSI, non TCHAR, since it produces C++ source code*/
    ofstream fout("timediscretemicromockcallmacros.h");
    fout<<CfileHeader<<endl;
    for(i=0;i<supportedArgCount;i++)
    {
        fout<<"#define MOCK_TD_METHOD_"<<i<<"(prefix, resultType, name";
        
        //write the argument list... 
        for(j=1;j<=i;j++)
        {
            fout<<", arg"<<j<<"Type, arg"<<j<<"Value";
        }
        //close the argument list
        fout<<") \\"<<endl;

        //MOCK_METHOD1... same thing
        fout<<tab<<"MOCK_METHOD_"<<i<<"(prefix, resultType, name";

        for(j=1;j<=i;j++)
        {
            fout<<", arg"<<j<<"Type, arg"<<j<<"Value";
        }
        fout<<")\\"<<endl;

        fout<<tab<<"mockMethodCall->AddExtraCallArgument(new CMockCallArgument<UINT32>(MOCK_TIMEPROVIDER(__FUNCTION__)));\\"<<endl;
        fout<<tab<<"result = REMATCH_ACTUAL_STATIC_MOCK_CALL(mockMethodCall);\\"<<endl;
        fout<<endl;


        fout << "#define MOCK_STATIC_TD_METHOD_" << i << "(prefix, resultType, name";

        //write the argument list... 
        for (j = 1; j <= i; j++)
        {
            fout << ", arg" << j << "Type, arg" << j << "Value";
        }
        //close the argument list
        fout << ") \\" << endl;

        //MOCK_METHOD_n ... same thing
        fout << tab << "MOCK_STATIC_METHOD_" << i << "(prefix, resultType, name";

        for (j = 1; j <= i; j++)
        {
            fout << ", arg" << j << "Type, arg" << j << "Value";
        }
        fout << ")\\" << endl;

        fout << tab << "mockMethodCall->AddExtraCallArgument(new CMockCallArgument<UINT32>(MOCK_TIMEPROVIDER(__FUNCTION__)));\\" << endl;
        fout << tab << "result = REMATCH_ACTUAL_STATIC_MOCK_CALL(mockMethodCall);\\" << endl;
        fout << endl;
    }

    fout<<"#define STIM_CALL_AT(var, time, ...)	var.__VA_ARGS__.SetTime(time, var.getAndIncOrder(time))"<<endl;
    fout<<endl;

    for(i=0;i<supportedArgCount;i++)
    {
        fout<<"#define DECLARE_STIM_STATIC_TD_METHOD_"<<i<<"(prefix, resultType, name";
        for(j=1;j<=i;j++)
        {
            fout<<", arg"<<j<<"Type, arg"<<j<<"Value";
        }
        fout<<")\\"<<endl;
        fout<<"class pFunctionCall_Wrapper_##name \\"<<endl;
        fout<<"{\\"<<endl;
        fout<<tab<<"public:\\"<<endl;
        fout<<tab<<"typedef resultType (*pRealFunctionType)(";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type arg"<<j<<"Value";
            if((i>0) &&(j<i))fout<<", ";
        }
        fout<<");\\"<<endl;
        fout<<tab<<"static pRealFunctionType realFunction;\\"<<endl;
        fout<<"};\\"<<endl;

        fout<<"pFunctionCall_Wrapper_##name::pRealFunctionType pFunctionCall_Wrapper_##name::realFunction = ::name; /*so this never can go to a header*/"<<endl;
        fout<<endl;

        fout<<"#define STIM_STATIC_TD_METHOD_"<<i<<"(prefix, resultType, name ";
        for(j=1;j<=i;j++)
        {
            fout<<", arg"<<j<<"Type, arg"<<j<<"Value";
        }
        fout<<") call"<<i<<"Arg<resultType, ";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type, ";
        }
        fout<<"pFunctionCall_Wrapper_##name > name;"<<endl;
        fout<<endl;
    }

    for(i=0;i<supportedArgCount;i++)
    {
        fout<<"template<typename resultType,";
        for(j=1;j<=i;j++)
        {
            fout<<"typename arg"<<j<<"Type, ";
        }
        fout<<"class C>";
        fout<<"class call"<<i<<"Arg : public canPlay"<<endl;
        fout<<"{"<<endl;
        fout<<"private:"<<endl;
        fout<<tab<<"class timeS"<<endl;
        fout<<tab<<"{"<<endl;
        fout<<tab<<"public:"<<endl;
        fout<<tab<<tab<<"timeS(";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type arg"<<j<<"Value";
            if((i>0) && (j<i)) fout<<", ";
        }
        fout<<"): "<<endl;
        fout<<tab<<tab<<"time(0), order(0)";
        for(j=1;j<=i;j++)
        {
            fout<<", arg"<<j<<"(arg"<<j<<"Value)";
        }
        fout<<endl;
        fout<<tab<<tab<<"{"<<endl;
        fout<<tab<<tab<<"}"<<endl;
        fout<<tab<<tab<<"UINT32 time;"<<endl;
        fout<<tab<<tab<<"UINT32 order;"<<endl;
        for(j=1;j<=i;j++)
        {
            fout<<tab<<tab<<"valueHolder<arg"<<j<<"Type> arg"<<j<<";"<<endl;
        }
        fout<<tab<<"};"<<endl;

        fout<<tab<<"std::vector<timeS> allCalls;"<<endl;

        fout<<"public:"<<endl;
        fout<<tab<<"call"<<i<<"Arg()"<<endl;
        fout<<tab<<"{"<<endl;
        fout<<tab<<tab<<"stims_base::registerCallXArg(this);"<<endl;
        fout<<tab<<"}"<<endl;
        fout<<endl;
    
        fout<<tab<<"virtual ~call"<<i<<"Arg<resultType, ";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type, ";
        }
        fout<<" C>()"<<endl;
        fout<<tab<<"{"<<endl;
        fout<<tab<<"}"<<endl;


        fout<<tab<<"virtual void PlayTick(_In_ UINT32 tick, _In_ UINT32 order)"<<endl;
        fout<<tab<<"{"<<endl;
        fout<<tab<<tab<<"for(UINT32 i=0; i<allCalls.size();i++)"<<endl;
        fout<<tab<<tab<<"{"<<endl;
        fout<<tab<<tab<<tab<<"if((allCalls[i].time==tick)&&(allCalls[i].order==order))"<<endl;
        fout<<tab<<tab<<tab<<"{"<<endl;
        fout<<tab<<tab<<tab<<tab<<"C::realFunction(";
        for(j=1;j<=i;j++)
        {
            fout<<"allCalls[i].arg"<<j;
            if((i>0)&&(j<i)) fout<<", ";
        }
        fout<<");"<<endl;
        fout<<tab<<tab<<tab<<"}"<<endl;
        fout<<tab<<tab<<"}"<<endl;
        fout<<tab<<"}"<<endl;

        fout<<endl;

        fout<<tab<<"call"<<i<<"Arg<resultType, ";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type, ";
        }
        fout<<"C>& SetTime(_In_ UINT32 time, _In_ UINT32 order)"<<endl;
        fout<<tab<<"{"<<endl;
        fout<<tab<<tab<<"if(allCalls.size()==0)"<<endl;
        fout<<tab<<tab<<"{"<<endl;
        fout<<tab<<tab<<tab<<"throw CMicroMockException(MICROMOCK_EXCEPTION_SET_TIME_BEFORE_CALL, _T(\"using SetTime before the call has been defined usually indicates an error in test code\"));"<<endl;
        fout<<tab<<tab<<"}"<<endl;
        fout<<tab<<tab<<"else"<<endl;
        fout<<tab<<tab<<"{"<<endl;
        fout<<tab<<tab<<tab<<"allCalls[allCalls.size()-1].time=time;"<<endl;
        fout<<tab<<tab<<tab<<"allCalls[allCalls.size()-1].order=order;"<<endl;
        fout<<tab<<tab<<"}"<<endl;
        fout<<tab<<tab<<"return *this;"<<endl;
        fout<<tab<<"}"<<endl;
        fout<<endl;

        fout<<tab<<"call"<<i<<"Arg<resultType, ";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type, ";
        }
        fout<<" C>& operator()(";
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j<<"Type arg"<<j;
            if((i>0)&&(j<i)) fout<<",";
        }
        fout<<")"<<endl;
        fout<<tab<<"{"<<endl;
        fout<<tab<<tab<<"timeS s"<<((i==0)?"":"(");
        for(j=1;j<=i;j++)
        {
            fout<<"arg"<<j;
            if((i>0)&&(j<i)) fout<<", ";
        }
        fout<<((i==0)?"":")")<<";"<<endl;
        fout<<tab<<tab<<"allCalls.push_back(s); /*time is updated \"later\" by chaining*/"<<endl;
        fout<<tab<<tab<<"return *this;"<<endl;
        fout<<tab<<"}"<<endl;
        fout<<endl;

        if(i>0)
        {
            fout<<tab<<"call"<<i<<"Arg<resultType, ";
            for(j=1;j<=i;j++)
            {
                fout<<"arg"<<j<<"Type, ";
            }
            fout<<" C>& setArraySize(_In_ UINT32 parameter, _In_ size_t nElements)"<<endl;
            fout<<tab<<"{"<<endl;
            fout<<tab<<tab<<"if(allCalls.size()==0)"<<endl;
            fout<<tab<<tab<<"{"<<endl;	
            fout<<tab<<tab<<tab<<"throw CMicroMockException(MICROMOCK_EXCEPTION_SET_ARRAY_SIZE_BEFORE_CALL, _T(\"using setArraySize before the call has been defined usually indicates an error in test code\"));"<<endl;
            fout<<tab<<tab<<"}"<<endl;
            fout<<tab<<tab<<"else"<<endl;
            fout<<tab<<tab<<"{"<<endl;
            fout<<tab<<tab<<tab<<"timeS& s = allCalls[allCalls.size()-1]; /*get the last element*/"<<endl;
            fout<<tab<<tab<<tab<<"switch(parameter)"<<endl;
            fout<<tab<<tab<<tab<<"{"<<endl;
            for(j=1;j<=i;j++)
            {
                fout<<tab<<tab<<tab<<tab<<"case "<<j<<":"<<endl;
                fout<<tab<<tab<<tab<<tab<<"{"<<endl;
                fout<<tab<<tab<<tab<<tab<<tab<<"s.arg"<<j<<".setArraySize(nElements);"<<endl;
                fout<<tab<<tab<<tab<<tab<<tab<<"break;"<<endl;
                fout<<tab<<tab<<tab<<tab<<"}"<<endl;
            }
            fout<<tab<<tab<<tab<<tab<<"default:"<<endl;
            fout<<tab<<tab<<tab<<tab<<"{"<<endl;
            fout<<tab<<tab<<tab<<tab<<tab<<"ASSERT_FAIL(_T(\"there are no parameters so big\"));"<<endl;
            fout<<tab<<tab<<tab<<tab<<"}"<<endl;
            fout<<tab<<tab<<tab<<"}"<<endl;
            fout<<tab<<tab<<tab<<"return *this;"<<endl;
            fout<<tab<<tab<<"}"<<endl;
            fout<<tab<<"}"<<endl;
        }
        fout<<"};"<<endl;
        fout<<endl;
    }

    fout<<"#endif // TIMEDISCRETEMICROMOCKCALLMACROS_H"<<endl;
    fout<<endl;

    fout.close();
}

int __cdecl _tmain(const int argc, const _TCHAR* argv[])
{
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    size_t supportedArgCount = 16;

    _tprintf(_T("MicroMock code generator.\n"));
    _tprintf(_T("GENERATED OUTPUT HAS TO BE HAND COPIED TO THE DESTINATION!!! (because building out of source tree).\n"));

    _tprintf(_T("Generating MicroMock call macros...\n"));
    GenerateMockCallMacros(supportedArgCount);

    _tprintf(_T("Generating TimeDiscreteMicroMock call macros...\n"));
    GenerateTimeDiscreteMockCallMacros(supportedArgCount);

    _tprintf(_T("Done.\n"));

    return 0;
}

