/*

 * Copyright (c) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 * http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

#include "script_expression.h"

#include "script_function.h"

#include "script_interpreter.h"

#include "script_utils.h"



using namespace std;



namespace Uscript {

UScriptExpression::UScriptExpression(ExpressionType expressType) : expressType_(expressType) {}

UScriptExpression::~UScriptExpression() {}



UScriptExpression* AssignExpression::CreateExpression(const std::string identifier, UScriptExpression *expression)

{

    return new AssignExpression(identifier, expression);

}

void AssignExpression::AddIdentifier(const std::string &identifier)

{

    multipleIdentifiers_.push_back(identifier);

}



UScriptExpression* AssignExpression::AddIdentifier(UScriptExpression *expression, const std::string identifier)

{

    auto assign = reinterpret_cast<AssignExpression*>(expression);

    if (assign != nullptr) {

        assign->AddIdentifier(identifier);

    }

    return assign;

}



// binary operator

UScriptExpression* BinaryExpression::CreateExpression(ExpressionAction action,

    UScriptExpression *left,

    UScriptExpression *right)

{

    return new BinaryExpression(action, left, right);

}

UScriptExpression* FunctionCallExpression::CreateExpression(const std::string identifier, ScriptParams *params)

{

    return new FunctionCallExpression(identifier, params);

}

UScriptValuePtr UScriptExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    return std::make_shared<UScriptValue>(UScriptValue::VALUE_TYPE_ERROR);

}

UScriptValuePtr IntegerExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    return std::make_shared<IntegerValue>(this->value_);

}

UScriptValuePtr FloatExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    return std::make_shared<FloatValue>(this->value_);

}

UScriptValuePtr StringExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    return std::make_shared<StringValue>(this->value_);

}

UScriptValuePtr IdentifierExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    INTERPRETER_LOGI(inter, local, "Execute statements identifier %s ", identifier_.c_str());

    UScriptValuePtr variable = inter.FindVariable(local, identifier_);

    INTERPRETER_LOGI(inter, local, "IdentifierExpression::Execute '%s = %s ' ", identifier_.c_str(),

        UScriptValue::ScriptToString(variable).c_str());

    if (variable != nullptr) {

        return variable;

    }

    return std::make_shared<UScriptValue>(UScriptValue::VALUE_TYPE_ERROR);

}



int32_t IdentifierExpression::GetIdentifierName(UScriptExpression *expression, std::string &name)

{

    if (expression == nullptr) {

        return USCRIPT_INVALID_PARAM;

    }

    if (expression->GetExpressType() != EXPRESSION_TYPE_IDENTIFIER) {

        return USCRIPT_INVALID_PARAM;

    } else {

        auto identifier = reinterpret_cast<IdentifierExpression*>(expression);

        if (identifier != nullptr) {

            name = identifier->GetIdentifier();

            return USCRIPT_SUCCESS;

        }

    }

    return USCRIPT_INVALID_PARAM;

}

UScriptValuePtr AssignExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    UScriptValuePtr result = expression_->Execute(inter, local);

    INTERPRETER_LOGI(inter, local, "AssignExpression::Execute update local var '%s = %s ' ", identifier_.c_str(),

        UScriptValue::ScriptToString(result).c_str());

    if (result->GetValueType() == UScriptValue::VALUE_TYPE_ERROR) {

        return result;

    }

    UScriptValuePtr var = inter.FindVariable(local, identifier_);

    if (var != nullptr) {

        inter.UpdateVariable(local, identifier_, result);

        return result;

    }



    std::vector<std::string> identifiers;

    identifiers.push_back(identifier_);

    identifiers.insert(identifiers.begin() + 1, multipleIdentifiers_.begin(), multipleIdentifiers_.end());

    size_t index = 0;

    local->UpdateVariables(inter, result, identifiers, index);

    return result;

}



UScriptValuePtr BinaryExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    static std::vector<std::string> opStr = {

        "", "add", "sub", "mul", "div", ">", ">=", "<", "<=", "==", "!=", "&&", "||"

    };

    UScriptValuePtr left;

    UScriptValuePtr right;



    INTERPRETER_LOGI(inter, local, "BinaryExpression::Execute ");

    if (left_ != nullptr) {

        left = left_->Execute(inter, local);

    }



    if (action_ == OR_OPERATOR && left != nullptr && left->IsTrue()) {

        INTERPRETER_LOGE(inter, local, "BinaryExpression::Execute left:%s %s",

            UScriptValue::ScriptToString(left).c_str(), opStr[action_].c_str());

        return std::make_shared<IntegerValue>(1);

    }

    if (right_ != nullptr) {

        right = right_->Execute(inter, local);

    }

    if (left != nullptr && right != nullptr) {

        UScriptValuePtr value = left->Computer(action_, right);

        INTERPRETER_LOGI(inter, local, "BinaryExpression::Execute left:%s %s right:%s result:%s",

            UScriptValue::ScriptToString(left).c_str(), opStr[action_].c_str(),

            UScriptValue::ScriptToString(right).c_str(), UScriptValue::ScriptToString(value).c_str());

        return value;

    }

    return std::make_shared<ErrorValue>(USCRIPT_ERROR_INTERPRET);

}



UScriptValuePtr FunctionCallExpression::Execute(ScriptInterpreter &inter, UScriptContextPtr local)

{

    UScriptValuePtr v;

    INTERPRETER_LOGD(inter, local, "FunctionCallExpression::Execute %s ", functionName_.c_str());



    if (inter.IsNativeFunction(functionName_)) {

        return inter.ExecuteNativeFunc(local, functionName_, params_);

    }

    return inter.ExecuteFunction(local, functionName_, params_);

}



BinaryExpression::~BinaryExpression()

{

    delete left_;

    delete right_;

}

AssignExpression::~AssignExpression()

{

    delete this->expression_;

}

FunctionCallExpression::~FunctionCallExpression()

{

    delete params_;

}

} // namespace Uscript