#pragma once

/**
\brief Conditional tracing structures
*/
struct TraceCondition
{
    ExpressionParser condition;
    duint steps;
    duint maxSteps;

    explicit TraceCondition(const String & expression, duint maxCount)
        : condition(expression), steps(0), maxSteps(maxCount) {}

    bool BreakTrace()
    {
        steps++;
        if(steps >= maxSteps)
            return true;
        duint value;
        return !condition.Calculate(value, valuesignedcalc(), true) || value;
    }
};

struct TextCondition
{
    ExpressionParser condition;
    String text;

    explicit TextCondition(const String & expression, const String & text)
        : condition(expression), text(text) {}

    bool Evaluate(bool defaultValue) const
    {
        duint value;
        if(condition.Calculate(value, valuesignedcalc(), true))
            return !!value;
        return defaultValue;
    }
};

struct TraceState
{
    bool InitTraceCondition(const String & expression, duint maxSteps)
    {
        delete traceCondition;
        traceCondition = new TraceCondition(expression, maxSteps);
        return traceCondition->condition.IsValidExpression();
    }

    bool InitLogFile()
    {
        if(logFile.empty())
            return true;
        auto hFile = CreateFileW(logFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
        if(hFile == INVALID_HANDLE_VALUE)
            return false;
        logWriter = new BufferedWriter(hFile);
        duint setting;
        if(BridgeSettingGetUint("Misc", "Utf16LogRedirect", &setting))
            writeUtf16 = !!setting;
        return true;
    }

    void LogWrite(String text)
    {
        if(logWriter)
        {
            if(writeUtf16)
            {
                auto textUtf16 = StringUtils::Utf8ToUtf16(text);
                logWriter->Write(textUtf16.c_str(), textUtf16.size() * 2);
                logWriter->Write(L"\r\n", 4);
            }
            else
            {
                logWriter->Write(text.c_str(), text.size());
                logWriter->Write("\n", 1);
            }
        }
        else
            dprintf_untranslated("%s\n", text.c_str());
    }

    bool IsActive() const
    {
        return traceCondition != nullptr;
    }

    bool IsExtended() const
    {
        return logCondition || cmdCondition;
    }

    bool BreakTrace() const
    {
        return !traceCondition || traceCondition->BreakTrace();
    }

    duint StepCount() const
    {
        return traceCondition ? traceCondition->steps : 0;
    }

    bool InitLogCondition(const String & expression, const String & text)
    {
        delete logCondition;
        logCondition = nullptr;
        if(text.empty())
            return true;
        logCondition = new TextCondition(expression, text);
        return logCondition->condition.IsValidExpression();
    }

    bool EvaluateLog(bool defaultValue) const
    {
        return logCondition && logCondition->Evaluate(defaultValue);
    }

    const String & LogText() const
    {
        return logCondition ? logCondition->text : emptyString;
    }

    bool InitCmdCondition(const String & expression, const String & text)
    {
        delete cmdCondition;
        cmdCondition = nullptr;
        if(text.empty())
            return true;
        cmdCondition = new TextCondition(expression, text);
        return cmdCondition->condition.IsValidExpression();
    }

    bool EvaluateCmd(bool defaultValue) const
    {
        return cmdCondition && cmdCondition->Evaluate(defaultValue);
    }

    const String & CmdText() const
    {
        return cmdCondition ? cmdCondition->text : emptyString;
    }

    bool InitSwitchCondition(const String & expression)
    {
        delete switchCondition;
        switchCondition = nullptr;
        if(expression.empty())
            return true;
        switchCondition = new TextCondition(expression, "");
        return switchCondition->condition.IsValidExpression();
    }

    bool EvaluateSwitch(bool defaultValue) const
    {
        return switchCondition && switchCondition->Evaluate(defaultValue);
    }

    void SetLogFile(const char* fileName)
    {
        logFile = StringUtils::Utf8ToUtf16(fileName);
    }

    bool ForceBreakTrace()
    {
        return forceBreakTrace;
    }

    void SetForceBreakTrace()
    {
        forceBreakTrace = true;
    }

    void Clear()
    {
        delete traceCondition;
        traceCondition = nullptr;
        delete logCondition;
        logCondition = nullptr;
        delete cmdCondition;
        cmdCondition = nullptr;
        delete switchCondition;
        switchCondition = nullptr;
        logFile.clear();
        delete logWriter;
        logWriter = nullptr;
        writeUtf16 = false;
        forceBreakTrace = false;
    }

private:
    TraceCondition* traceCondition = nullptr;
    TextCondition* logCondition = nullptr;
    TextCondition* cmdCondition = nullptr;
    TextCondition* switchCondition = nullptr;
    String emptyString;
    WString logFile;
    BufferedWriter* logWriter = nullptr;
    bool writeUtf16 = false;
    bool forceBreakTrace = false;
};
