// This file added in headers queue
// File: "Sources.h"

namespace GOTHIC_ENGINE {

    int togglekey = KEY_P;

    //main timer variables
    zCView* TimerView = Null;
    zCView* RealTimeView = Null;
    uint64 SessionTimerBegin = 0; // Main session timer
    bool SessionEnd = false;
    bool CanUpdateTimerView = true;
    uint64 realtimeAdd = 0;
    uint64 LastRealTime = 0;

    bool showRealTime = false;
    bool Standard = false;
    bool FinishCloseGame = false;
    string FinishVideo, FinishText, FinishDialogueAppears, FinishDocumentText = "NOT SET // DISABLED";


    void ReadSettings() {
        CDocument Settings;
        Settings.ReadFromFile(Union.GetSystemDirectory() + "zSpeedRunTimer_Settings.txt");
        string type;
        
        if (Settings.GetNum() > 0) {
            string& help = Settings[0];

            if (help.HasWord("closegame")) {
                FinishCloseGame = true;
            }
            else if (help.HasWord("video")) {
                type = "video";
            }
            else if (help.HasWord("textdisplay")) {
                type = "textdisplay";
            }
            else if (help.HasWord("dialogchoiceselectionappears")) {
                type = "dialogchoiceselectionappears";
            }
            else if (help.HasWord("document")) {
                type = "document";
            }
            else {
                Standard = true;
            }
        }
        else {
            Standard = true;
        }


        if (Settings.GetNum() > 1) {
            if (type == "video") {
                FinishVideo = Settings[1];
            }
            else if (type == "textdisplay") {
                FinishText = Settings[1];
            }
            else if (type == "dialogchoiceselectionappears") {
                FinishDialogueAppears = Settings[1];
            }
            else if (type == "document") {
                FinishDocumentText = Settings[1];
            }
        }


        if (Settings.GetNum() > 2) {
            string& help = Settings[2];
            if (help == "showrealtime=true" || help == "showrealtime=1")
                showRealTime = true;
        }


        if (Settings.GetNum() > 3) {
            string& help = Settings[3];
            if (help == "togglekey=F1")  { togglekey = KEY_F1; }
            if (help == "togglekey=F2")  { togglekey = KEY_F2; }
            if (help == "togglekey=F3")  { togglekey = KEY_F3; }
            if (help == "togglekey=F4")  { togglekey = KEY_F4; }
            if (help == "togglekey=F5")  { togglekey = KEY_F5; }
            if (help == "togglekey=F6")  { togglekey = KEY_F6; }
            if (help == "togglekey=F7")  { togglekey = KEY_F7; }
            if (help == "togglekey=F8")  { togglekey = KEY_F8; }
            if (help == "togglekey=F9")  { togglekey = KEY_F9; }
            if (help == "togglekey=F10") { togglekey = KEY_F10;}
            if (help == "togglekey=F11") { togglekey = KEY_F11;}
            if (help == "togglekey=F12") { togglekey = KEY_F12;}
            if (help == "togglekey=F13") { togglekey = KEY_F13;}
            if (help == "togglekey=F14") { togglekey = KEY_F14;}
            if (help == "togglekey=F15") { togglekey = KEY_F15;}
            if (help == "togglekey=A")   { togglekey = KEY_A;  }
            if (help == "togglekey=B")   { togglekey = KEY_B;  }
            if (help == "togglekey=C")   { togglekey = KEY_C;  }
            if (help == "togglekey=D")   { togglekey = KEY_D;  }
            if (help == "togglekey=E")   { togglekey = KEY_E;  }
            if (help == "togglekey=F")   { togglekey = KEY_F;  }
            if (help == "togglekey=G")   { togglekey = KEY_G;  }
            if (help == "togglekey=H")   { togglekey = KEY_H;  }
            if (help == "togglekey=I")   { togglekey = KEY_I;  }
            if (help == "togglekey=J")   { togglekey = KEY_J;  }
            if (help == "togglekey=K")   { togglekey = KEY_K;  }
            if (help == "togglekey=L")   { togglekey = KEY_L;  }
            if (help == "togglekey=M")   { togglekey = KEY_M;  }
            if (help == "togglekey=N")   { togglekey = KEY_N;  }
            if (help == "togglekey=O")   { togglekey = KEY_O;  }
            if (help == "togglekey=P")   { togglekey = KEY_P;  }
            if (help == "togglekey=Q")   { togglekey = KEY_Q;  }
            if (help == "togglekey=R")   { togglekey = KEY_R;  }
            if (help == "togglekey=S")   { togglekey = KEY_S;  }
            if (help == "togglekey=T")   { togglekey = KEY_T;  }
            if (help == "togglekey=U")   { togglekey = KEY_U;  }
            if (help == "togglekey=V")   { togglekey = KEY_V;  }
            if (help == "togglekey=W")   { togglekey = KEY_W;  }
            if (help == "togglekey=X")   { togglekey = KEY_X;  }
            if (help == "togglekey=Y")   { togglekey = KEY_Y;  }
            if (help == "togglekey=Z")   { togglekey = KEY_Z;  }
        }
    }

    void EndSession() {
        SessionEnd = true;
    }


    HOOK Hook_oCDocumentManager_PrintLines AS(&oCDocumentManager::PrintLines, &oCDocumentManager::PrintLines_Union);
    void __fastcall oCDocumentManager::PrintLines_Union(int a1, int a2, zSTRING& text) {
        //Sequel Phoenix Quest
#if ENGINE == Engine_G1A
        if (text.HasWordI("Du hast das R")) {
            EndSession();
        }
#endif

        //mod end
        if (text.HasWordI(FinishDocumentText)) {
            EndSession();
        }
        THISCALL(Hook_oCDocumentManager_PrintLines)(a1, a2, text);
    }


    //stop game closing after finish in G1
    HOOK Hook_CGameManager_ExitGame AS(&CGameManager::ExitGame, &CGameManager::ExitGame_Union);
    int CGameManager::ExitGame_Union() {
        //stop timer for mods
        if (FinishCloseGame) {
            EndSession();
        }
        return 0;
    }

    //TextDisplay
    HOOK Hook_zCView_CreateText AS(&zCView::CreateText, &zCView::CreateText_Union);
    zCViewText* __thiscall zCView::CreateText_Union(int a1, int a2, zSTRING const& a3, float a4, struct zCOLOR& a5, int a6, int a7) {
        // End session!
        if (a3.HasWordI(FinishText)) {
            EndSession();
        }
        return THISCALL(Hook_zCView_CreateText)(a1, a2, a3, a4, a5, a6, a7);
    }
    


    HOOK Hook_CGameManager_PlayVideo AS(&CGameManager::PlayVideo, &CGameManager::PlayVideo_Union);
#if ENGINE >= Engine_G2
    int CGameManager::PlayVideo_Union(zSTRING name) {
#else
    int CGameManager::PlayVideo_Union(zSTRING name, int a2) {
#endif
        // End session!
        if (name.HasWordI(FinishVideo) || (Standard && (name.HasWordI("Extro.bik") || name.HasWordI("Extro_Xardas"))))
            EndSession();

        // Don't update timer in video
        CanUpdateTimerView = false;
#if ENGINE >= Engine_G2
        int Ok = THISCALL(Hook_CGameManager_PlayVideo)(name);
#else
        int Ok = THISCALL(Hook_CGameManager_PlayVideo)(name, a2);
#endif
        CanUpdateTimerView = true;
        return Ok;
    }


    HOOK Hook_zCViewDialogChoice_AddChoice AS(&zCViewDialogChoice::AddChoice, &zCViewDialogChoice::AddChoice_Union);
    void __fastcall zCViewDialogChoice::AddChoice_Union(zSTRING& a1, int a2) {
        //Sequel WIP
#if ENGINE == Engine_G1A
        if (a1.HasWordI("(WORKINPROGRESS)")) {
            EndSession();
        }
#endif

        //mod ending
        if (a1.HasWordI(FinishDialogueAppears)) {
            EndSession();
        }
        THISCALL(Hook_zCViewDialogChoice_AddChoice)(a1, a2);
    }


    string GetTimeNumberFormated(uint64 timePtr, uint length) {
        string number = timePtr;
        while (number.Length() < length)
            number = "0" + number;

        return number;
    }

    string GetTimeStringFormated_Help(uint64 timePtr) {
#pragma warning(disable:4244)
        long hr = timePtr / 3600000;
        timePtr = timePtr - 3600000 * hr;

        long min = timePtr / 60000;
        timePtr = timePtr - 60000 * min;

        long sec = timePtr / 1000;
        timePtr = timePtr - 1000 * sec;
#pragma warning(default:4244)

        string hour = string::Combine("%i:", hr);
        string rest = string::Combine("%s:%s.%s", GetTimeNumberFormated(min, 2), GetTimeNumberFormated(sec, 2), GetTimeNumberFormated(timePtr, 3));
        if (hr > 0)
            return hour + rest;
        else
            return rest;
    }

    string GetTimeStringFormated(uint64 timePtr, string pre) {
        return pre + "time: " + GetTimeStringFormated_Help(timePtr);
    }


    void Game_Entry() {
        ReadSettings();
    }

    void Game_Init() {
        TimerView = new zCView();
        screen->InsertItem(TimerView, True);
        TimerView->SetPos(0, 0);
        TimerView->SetSize(3000, 1000);
        TimerView->SetFont("FONT_OLD_20_WHITE.TGA");
        TimerView->Print(100, 100, GetTimeStringFormated(0, "Game "));

        RealTimeView = new zCView();
        screen->InsertItem(RealTimeView, True);
        RealTimeView->SetPos(0, 300);
        RealTimeView->SetSize(3000, 1000);
        RealTimeView->SetFont("FONT_OLD_20_WHITE.TGA");
        if (showRealTime)
            RealTimeView->Print(100, 100, GetTimeStringFormated(0, "Real "));

        SessionTimerBegin = GetTickCount64();
        SessionEnd = false;
    }

    void Game_Exit() {
    }

    bool inLoad, newLoad = false;
    uint64 loadStart, loadEnd = 0;
    uint64 realTime, gameTime, loadTime = 0;
    bool start = false;

    int FreezeTimerFrames = -1;

    //detect normal gameplay
    HOOK Hook_zCCamera_PostRenderProcessing AS(&zCCamera::PostRenderProcessing, &zCCamera::PostRenderProcessing_Union);
    void __thiscall zCCamera::PostRenderProcessing_Union(void) {

        CanUpdateTimerView = true;

        if (FreezeTimerFrames != -1 && FreezeTimerFrames-- == 0)
        {
            FreezeTimerFrames = 1;

            if (inLoad == true) {
                inLoad = false;
                loadEnd = GetTickCount64();
                newLoad = true;
                //detect new game
                if (start == false) {
                    loadTime = 0;
                    realtimeAdd = GetTickCount64() - loadStart;
                    SessionTimerBegin = GetTickCount64();
                    start = true;
                    loadStart, loadEnd = 0;
                }
            }
        }

        THISCALL(Hook_zCCamera_PostRenderProcessing)();
    }

    //detect loading/saving
    HOOK Hook_zCViewProgressBar_Draw AS(&zCViewProgressBar::Draw, &zCViewProgressBar::Draw_Union);
    void __thiscall zCViewProgressBar::Draw_Union() {

        CanUpdateTimerView = false;

        if (inLoad == false) {
            loadStart = GetTickCount64();
            inLoad = true;
        }

        THISCALL(Hook_zCViewProgressBar_Draw)();
    }



    void Game_Loop() {

        string exitstring = "";

        //update total time
        realTime = GetTickCount64() - SessionTimerBegin;
        //add new load time
        if (newLoad == true) {
            inLoad = false;
            loadEnd = loadEnd - loadStart;
            if (loadEnd > 0) {
                loadTime += loadEnd;
            }
            loadStart, loadEnd = 0;
            newLoad = false;
        }
        if (loadTime > realTime) {
            loadTime = 0;
        }
        //subtract loads from total time
        gameTime = realTime - loadTime;
        realTime = realTime + realtimeAdd;

        if (CanUpdateTimerView && SessionEnd == false) {
            TimerView->ClrPrintwin();
            TimerView->Print(100, 100, GetTimeStringFormated(gameTime, "Game "));
        }

        if (zKeyToggled(togglekey))
            showRealTime = !showRealTime;

        if (CanUpdateTimerView && SessionEnd == false) {
            LastRealTime = realTime;
        }

        if (CanUpdateTimerView && SessionEnd == false && showRealTime) {
            RealTimeView->ClrPrintwin();
            RealTimeView->Print(100, 100, GetTimeStringFormated(LastRealTime, "Real "));
        }
        else if (SessionEnd == true && showRealTime) {
            RealTimeView->ClrPrintwin();
            RealTimeView->Print(100, 100, GetTimeStringFormated(LastRealTime, "Real "));
        }
        else if (!showRealTime) {
            RealTimeView->ClrPrintwin();
            RealTimeView->Print(100, 100, "");
        }
    }

    // Information about current saving or loading world
    TSaveLoadGameInfo& SaveLoadGameInfo = UnionCore::SaveLoadGameInfo;

    void Game_SaveBegin() {
    }

    void Game_SaveEnd() {
    }

    void LoadBegin() {
    }

    void LoadEnd() {
        FreezeTimerFrames = 1; // == 1 frame
    }

    void Game_LoadBegin_NewGame() {
        LoadBegin();
    }

    void Game_LoadEnd_NewGame() {
        LoadEnd();

        // Start timer
        SessionTimerBegin = GetTickCount64();
        SessionEnd = false;
        //inLoad = true;
        start = false;
    }

    void Game_LoadBegin_SaveGame() {
        LoadBegin();
    }

    void Game_LoadEnd_SaveGame() {
        LoadEnd();
    }

    void Game_LoadBegin_ChangeLevel() {
        LoadBegin();
    }

    void Game_LoadEnd_ChangeLevel() {
        LoadEnd();
    }

    void Game_LoadBegin_Trigger() {
    }

    void Game_LoadEnd_Trigger() {
    }

    void Game_Pause() {
    }

    void Game_Unpause() {
    }

    void Game_DefineExternals() {
    }


    /*
    Functions call order on Game initialization:
      - Game_Entry           * Gothic entry point
      - Game_DefineExternals * Define external script functions
      - Game_Init            * After DAT files init

    Functions call order on Change level:
      - Game_LoadBegin_Trigger     * Entry in trigger
      - Game_LoadEnd_Trigger       *
      - Game_Loop                  * Frame call window
      - Game_LoadBegin_ChangeLevel * Load begin
      - Game_SaveBegin             * Save previous level information
      - Game_SaveEnd               *
      - Game_LoadEnd_ChangeLevel   *

    Functions call order on Save game:
      - Game_Pause     * Open menu
      - Game_Unpause   * Click on save
      - Game_Loop      * Frame call window
      - Game_SaveBegin * Save begin
      - Game_SaveEnd   *

    Functions call order on Load game:
      - Game_Pause              * Open menu
      - Game_Unpause            * Click on load
      - Game_LoadBegin_SaveGame * Load begin
      - Game_LoadEnd_SaveGame   *
    */

#define AppDefault True
    CApplication* lpApplication = !CHECK_THIS_ENGINE ? Null : CApplication::CreateRefApplication(
        Enabled(AppDefault) Game_Entry,
        Enabled(AppDefault) Game_Init,
        Enabled(AppDefault) Game_Exit,
        Enabled(AppDefault) Game_Loop,
        Enabled(AppDefault) Game_SaveBegin,
        Enabled(AppDefault) Game_SaveEnd,
        Enabled(AppDefault) Game_LoadBegin_NewGame,
        Enabled(AppDefault) Game_LoadEnd_NewGame,
        Enabled(AppDefault) Game_LoadBegin_SaveGame,
        Enabled(AppDefault) Game_LoadEnd_SaveGame,
        Enabled(AppDefault) Game_LoadBegin_ChangeLevel,
        Enabled(AppDefault) Game_LoadEnd_ChangeLevel,
        Enabled(AppDefault) Game_LoadBegin_Trigger,
        Enabled(AppDefault) Game_LoadEnd_Trigger,
        Enabled(AppDefault) Game_Pause,
        Enabled(AppDefault) Game_Unpause,
        Enabled(AppDefault) Game_DefineExternals
    );
    }