/*
 * 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.
 */
#ifndef USCRIPT_CONTEXT_H
#define USCRIPT_CONTEXT_H

#include <map>
#include <memory>
#include <string>
#include <vector>
#include "script_instruction.h"
#include "script_manager.h"

namespace Uscript {
class ScriptInterpreter;
class UScriptValue;
class UScriptInterpretContext;

using UScriptValuePtr = std::shared_ptr<UScriptValue>;
using UScriptContextPtr = std::shared_ptr<UScriptInterpretContext>;

class UScriptValue {
public:
    enum UScriptValueType {
        VALUE_TYPE_INTEGER = UScriptContext::PARAM_TYPE_INTEGER,
        VALUE_TYPE_FLOAT = UScriptContext::PARAM_TYPE_FLOAT,
        VALUE_TYPE_STRING = UScriptContext::PARAM_TYPE_STRING,
        VALUE_TYPE_ERROR,
        VALUE_TYPE_LIST,
        VALUE_TYPE_RETURN,
    };

    explicit UScriptValue(UScriptValueType type) : type_(type) {}
    virtual ~UScriptValue() {}

    UScriptValueType GetValueType() const
    {
        return type_;
    }

    virtual bool IsTrue() const
    {
        return false;
    }

    virtual UScriptValuePtr Computer(int32_t action, UScriptValuePtr value);

    virtual std::string ToString();

    static std::string ScriptToString(UScriptValuePtr value);

    static UScriptValuePtr GetRightCompluteValue(UScriptValuePtr rightValue);

private:
    UScriptValueType type_;
};

class IntegerValue : public UScriptValue {
public:
    explicit IntegerValue(int32_t value) : UScriptValue(UScriptValue::VALUE_TYPE_INTEGER), value_(value) {}
    ~IntegerValue() override {}

    bool IsTrue() const override
    {
        return value_ != 0;
    }

    int32_t GetValue() const
    {
        return value_;
    }

    UScriptValuePtr Computer(int32_t action, UScriptValuePtr value) override;
    std::string ToString() override;

private:
    int32_t value_;
};

class FloatValue : public UScriptValue {
public:
    explicit FloatValue(float value) : UScriptValue(UScriptValue::VALUE_TYPE_FLOAT), value_(value) {}
    ~FloatValue() override {}

    bool IsTrue() const override
    {
        return value_ != 0;
    }

    float GetValue() const
    {
        return value_;
    }

    UScriptValuePtr Computer(int32_t action, UScriptValuePtr value) override;
    std::string ToString() override;

private:
    bool ComputerEqual(const UScriptValuePtr rightValue);
    float value_;
};

class StringValue : public UScriptValue {
public:
    explicit StringValue(std::string value) : UScriptValue(UScriptValue::VALUE_TYPE_STRING),
        value_(std::move(value)) {}
    ~StringValue() override {}

    bool IsTrue() const override
    {
        return !value_.empty();
    }

    std::string GetValue() const
    {
        return value_;
    }

    UScriptValuePtr Computer(int32_t action, UScriptValuePtr value) override;

    UScriptValuePtr ComputerReturn(int32_t action, UScriptValuePtr rightValue, UScriptValuePtr defReturn) const;

    std::string ToString() override;

private:
    int32_t ComputerLogic(UScriptValuePtr rightValue) const;
    std::string value_;
};

class ReturnValue : public UScriptValue {
public:
    ReturnValue() : UScriptValue(UScriptValue::VALUE_TYPE_LIST) {}
    ~ReturnValue() override
    {
        values_.clear();
    }

    bool IsTrue() const override
    {
        return false;
    }

    UScriptValuePtr Computer(int32_t action, UScriptValuePtr value) override;
    std::string ToString() override;

    void AddValue(const UScriptValuePtr value);
    void AddValues(const std::vector<UScriptValuePtr> values);
    std::vector<UScriptValuePtr> GetValues() const;

private:
    std::vector<UScriptValuePtr> values_ {};
};

class ErrorValue : public UScriptValue {
public:
    explicit ErrorValue(int32_t retCode) : UScriptValue(UScriptValue::VALUE_TYPE_ERROR), retCode_(retCode) {}
    ~ErrorValue() override {}

    // 对于返回值，true表示返回ok
    bool IsTrue() const override
    {
        return retCode_ == USCRIPT_SUCCESS;
    }

    int32_t GetValue() const
    {
        return retCode_;
    }

    int32_t SetValue(const int32_t code)
    {
        return retCode_ = code;
    }
    UScriptValuePtr Computer(int32_t action, UScriptValuePtr value) override
    {
        return nullptr;
    }

    std::string ToString() override;

private:
    int32_t retCode_ = 0;
};

/**
 * 脚本指令上下文，用来在执行脚本指令时，传递输入、输出参数
 */
class UScriptInstructionContext : public UScriptContext {
public:
    UScriptInstructionContext() {}

    virtual ~UScriptInstructionContext() {}

    int32_t PushParam(int32_t value) override;
    int32_t PushParam(float value) override;
    int32_t PushParam(const std::string &value) override;
    int32_t GetParamCount() override;
    ParamType GetParamType(int32_t index) override;
    int32_t GetParam(int32_t index, int32_t &value) override;
    int32_t GetParam(int32_t index, float &value) override;
    int32_t GetParam(int32_t index, std::string &value) override;

    int32_t AddInputParam(UScriptValuePtr value);

    std::vector<UScriptValuePtr> GetOutVar() const
    {
        return outParam_;
    }

private:
    template<class T, class TWapper> int32_t GetOutputValue(int32_t index, T &value);
    template<class T, class TWapper> int32_t GetParam(int32_t index, T &value);

    std::vector<UScriptValuePtr> innerParam_ {};
    std::vector<UScriptValuePtr> outParam_ {};
};

/**
 * 脚本解析中的上下文，保存每一层的变量
 */
class UScriptInterpretContext {
public:
    explicit UScriptInterpretContext(bool top = false);

    virtual ~UScriptInterpretContext()
    {
        localVariables_.clear();
    }

    UScriptValuePtr FindVariable(const ScriptInterpreter &inter, std::string id);
    void UpdateVariable(const ScriptInterpreter &inter, std::string id, UScriptValuePtr value);
    void UpdateVariables(const ScriptInterpreter &inter,
        UScriptValuePtr value,
        std::vector<std::string> ids,
        size_t& startIndex);

    uint32_t GetContextId() const
    {
        return contextId_;
    }

    bool IsTop() const
    {
        return top_;
    }

private:
    uint32_t contextId_ = 0;
    bool top_ = false;
    std::map<std::string, UScriptValuePtr> localVariables_ {};
};
} // namespace Uscript
#endif // USCRIPT_CONTEXT_H
