#include "EvalValue.hpp"
#include "NumericEvalValue.hpp"
#include "StringEvalValue.hpp"
#include <cstring>
#include <memory>

namespace Porygon::Evaluation {

    extern "C" {
    Porygon::TypeClass GetEvalValueTypeClass(const EvalValue *v) {
        return v->GetTypeClass();
    }

    int64_t EvaluateEvalValueInteger(const EvalValue *v) {
        return v->EvaluateInteger();
    }

    double EvaluateEvalValueFloat(const EvalValue *v) {
        return v->EvaluateFloat();
    }

    bool EvaluateEvalValueBool(const EvalValue *v) {
        return v->EvaluateBool();
    }

    size_t GetEvalValueStringLength(const EvalValue *v) {
        auto result = v->EvaluateString();
        return result.size();
    }

    int EvaluateEvalValueString(const EvalValue *v, char16_t* dst, size_t capacity){
        auto result = v->EvaluateString();
        for (int i = 0; i < capacity; i++){
            dst[i] = result[i];
        }
        return 0;
    }


    EvalValue *CreateIntegerEvalValue(long l) {
        return new IntegerEvalValue(l);
    }

    EvalValue *CreateFloatEvalValue(double d) {
        return new FloatEvalValue(d);
    }

    EvalValue *CreateBoolEvalValue(bool b) {
        return new BooleanEvalValue(b);
    }

    EvalValue *CreateStringEvalValue(const char16_t *s) {
        return new StringEvalValue(s);
    }
    }
}

#ifdef TESTS_BUILD
#include <catch.hpp>
#include "../src/Script.hpp"

TEST_CASE( "Evaluate String", "[integration]" ) {
    auto sc = new u16string(u"\"foo bar\"");
    auto script = Porygon::Script::Create(*sc);
    REQUIRE(!script->Diagnostics -> HasErrors());
    auto result = script->Evaluate();
    size_t size = GetEvalValueStringLength(result.Get());
    auto dst = new char16_t[size + 1]{'\0'};
    EvaluateEvalValueString(result.Get(), dst, size);
    auto s = u16string(dst);
    REQUIRE(s == u"foo bar");
    delete[] dst;
    delete script;
    delete sc;
}


#endif
