//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "ParserPch.h"

#if ENABLE_REGEX_CONFIG_OPTIONS

namespace UnifiedRegex
{
    const wchar16* RegexStats::PhaseNames[RegexStats::NumPhases] = { CH_WSTR("parse"), CH_WSTR("compile"), CH_WSTR("execute") };
    const wchar16* RegexStats::UseNames[RegexStats::NumUses] = { CH_WSTR("match"), CH_WSTR("exec"), CH_WSTR("test"), CH_WSTR("replace"), CH_WSTR("split"), CH_WSTR("search") };

    RegexStats::RegexStats(RegexPattern* pattern)
        : pattern(pattern)
        , inputLength(0)
        , numCompares(0)
        , numPushes(0)
        , numPops(0)
        , stackHWM(0)
        , numInsts(0)
    {
        for (int i = 0; i < NumPhases; i++)
            phaseTicks[i] = 0;
        for (int i = 0; i < NumUses; i++)
            useCounts[i] = 0;
    }

    void RegexStats::Print(DebugWriter* w, RegexStats* totals, Ticks ticksPerMillisecond)
    {
        if (pattern == 0)
            w->PrintEOL(CH_WSTR("TOTAL"));
        else
            pattern->Print(w);
        w->EOL();
        w->Indent();

        for (int i = 0; i < NumPhases; i++)
        {
            double ms = (double)phaseTicks[i] / (double)ticksPerMillisecond;
            if (totals == 0 || totals->phaseTicks[i] == 0)
                w->PrintEOL(CH_WSTR("%-12s: %10.4fms"), PhaseNames[i], ms);
            else
            {
                double pc = (double)phaseTicks[i] * 100.0  / (double)totals->phaseTicks[i];
                w->PrintEOL(CH_WSTR("%-12s: %10.4fms (%10.4f%%)"), PhaseNames[i], ms, pc);
            }
        }

        for (int i = 0; i < NumUses; i++)
        {
            if (useCounts[i] > 0)
            {
                if (totals == 0 || totals->useCounts[i] == 0)
                    w->PrintEOL(CH_WSTR("#%-11s: %10I64u"), UseNames[i], useCounts[i]);
                else
                {
                    double pc = (double)useCounts[i] * 100.0 / (double)totals->useCounts[i];
                    w->PrintEOL(CH_WSTR("#%-11s: %10I64u   (%10.4f%%)"), UseNames[i], useCounts[i], pc);
                }
            }
        }

        if (inputLength > 0)
        {
            double r = (double)numCompares * 100.0 / (double)inputLength;
            if (totals == 0 || totals->numCompares == 0)
                w->PrintEOL(CH_WSTR("numCompares : %10.4f%%"), r);
            else
            {
                double pc = (double)numCompares * 100.0 / (double)totals->numCompares;
                w->PrintEOL(CH_WSTR("numCompares : %10.4f%%  (%10.4f%%)"), r, pc);
            }
        }

        if (totals == 0 || totals->inputLength == 0)
            w->PrintEOL(CH_WSTR("inputLength : %10I64u"), inputLength);
        else
        {
            double pc = (double)inputLength * 100.0 / (double)totals->inputLength;
            w->PrintEOL(CH_WSTR("inputLength : %10I64u   (%10.4f%%)"), inputLength, pc);
        }

        if (totals == 0 || totals->numPushes == 0)
            w->PrintEOL(CH_WSTR("numPushes   : %10I64u"), numPushes);
        else
        {
            double pc = (double)numPushes * 100.0 / (double)totals->numPushes;
            w->PrintEOL(CH_WSTR("numPushes   : %10I64u   (%10.4f%%)"), numPushes, pc);
        }

        if (totals == 0 || totals->numPops == 0)
            w->PrintEOL(CH_WSTR("numPops     : %10I64u"), numPops);
        else
        {
            double pc = (double)numPops * 100.0 / (double)totals->numPops;
            w->PrintEOL(CH_WSTR("numPops     : %10I64u   (%10.4f%%)"), numPops, pc);
        }

        if (totals == 0 || totals->stackHWM == 0)
            w->PrintEOL(CH_WSTR("stackHWM    : %10I64u"), stackHWM);
        else
        {
            double pc = (double)stackHWM * 100.0 / (double)totals->stackHWM;
            w->PrintEOL(CH_WSTR("stackHWM    : %10I64u   (%10.4f%%)"), stackHWM, pc);
        }

        if (totals == 0 || totals->numInsts == 0)
            w->PrintEOL(CH_WSTR("numInsts    : %10I64u"), numInsts);
        else
        {
            double pc = (double)numInsts * 100.0 / (double)totals->numInsts;
            w->PrintEOL(CH_WSTR("numInsts    : %10I64u   (%10.4f%%)"), numInsts, pc);
        }

        w->Unindent();
    }

    void RegexStats::Add(RegexStats* other)
    {
        for (int i = 0; i < NumPhases; i++)
            phaseTicks[i] += other->phaseTicks[i];
        for (int i = 0; i < NumUses; i++)
            useCounts[i] += other->useCounts[i];
        inputLength += other->inputLength;
        numCompares += other->numCompares;
        numPushes += other->numPushes;
        numPops += other->numPops;
        if (other->stackHWM > stackHWM)
            stackHWM = other->stackHWM;
        numInsts += other->numInsts;
    }

    RegexStats::Ticks RegexStatsDatabase::Now()
    {
        LARGE_INTEGER tmp;
        if (QueryPerformanceCounter(&tmp))
            return tmp.QuadPart;
        else
        {
            Assert(false);
            return 0;
        }
    }

    RegexStats::Ticks RegexStatsDatabase::Freq()
    {
        LARGE_INTEGER tmp;
        if (QueryPerformanceFrequency(&tmp))
        {
            return tmp.QuadPart / 1000;
        }
        else
        {
            Assert(false);
            return 1;
        }
    }

    RegexStatsDatabase::RegexStatsDatabase(ArenaAllocator* allocator)
        : start(0), allocator(allocator)
    {
        ticksPerMillisecond = Freq();
        map = Anew(allocator, RegexStatsMap, allocator, 17);
    }

    RegexStats* RegexStatsDatabase::GetRegexStats(RegexPattern* pattern)
    {
        Js::InternalString str = pattern->GetSource();
        RegexStats *res;
        if (!map->TryGetValue(str, &res))
        {
            res = Anew(allocator, RegexStats, pattern);
            map->Add(str, res);
        }
        return res;
    }

    void RegexStatsDatabase::BeginProfile()
    {
        start = Now();
    }

    void RegexStatsDatabase::EndProfile(RegexStats* stats, RegexStats::Phase phase)
    {
        stats->phaseTicks[phase] += Now() - start;
    }

    void RegexStatsDatabase::Print(DebugWriter* w)
    {
        RegexStats totals(0);

        Output::Print(CH_WSTR("Regular Expression Statistics\n"));
        Output::Print(CH_WSTR("=============================\n"));

        for (int i = 0; i < map->Count(); i++)
            totals.Add(map->GetValueAt(i));

        for (int i = 0; i < map->Count(); i++)
            map->GetValueAt(i)->Print(w, &totals, ticksPerMillisecond);

        totals.Print(w, 0, ticksPerMillisecond);

        allocator->Free(w, sizeof(DebugWriter));
    }
}

#endif
