/*

 * 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_STATEMENT_H

#define USCRIPT_STATEMENT_H



#include <vector>

#include "script_context.h"

#include "script_param.h"



namespace Uscript {

class UScriptStatementList;

class UScriptExpression;



class UScriptStatementResult {

public:

    enum StatementResultType {

        STATEMENT_RESULT_TYPE_NORMAL = 1,

        STATEMENT_RESULT_TYPE_BREAK,

        STATEMENT_RESULT_TYPE_CONTINUE,

        STATEMENT_RESULT_TYPE_RTN,

        STATEMENT_RESULT_TYPE_ERROR

    };



public:

    UScriptStatementResult() {}

    ~UScriptStatementResult() {}



    UScriptStatementResult(StatementResultType type, UScriptValuePtr value) : type_(type), value_(value) {}



    StatementResultType GetResultType() const

    {

        return type_;

    }



    void SetResultType(const StatementResultType type)

    {

        type_ = type;

    }



    UScriptValuePtr GetResultValue() const

    {

        return value_;

    }



    void SetResultValue(const UScriptValuePtr value)

    {

        value_ = value;

    }



    void SetError(const int32_t error)

    {

        error_ = error;

    }



    int32_t GetError() const

    {

        return error_;

    }



    void UpdateStatementResult(UScriptValuePtr value);

    static std::string ScriptToString(UScriptStatementResult *result);



private:

    StatementResultType type_ = STATEMENT_RESULT_TYPE_NORMAL;

    UScriptValuePtr value_ = nullptr;

    int32_t error_ = 0;

};



class UScriptStatement {

public:

    enum StatementType {

        STATEMENT_TYPE_EXPRESSION = 1,

        STATEMENT_TYPE_IF,

        STATEMENT_TYPE_FOR,

        STATEMENT_TYPE_WHILE,

        STATEMENT_TYPE_BREAK,

        STATEMENT_TYPE_CONTINUE,

        STATEMENT_TYPE_RTN

    };



    explicit UScriptStatement(UScriptStatement::StatementType type) : type_(type) {}



    virtual ~UScriptStatement() {}



    static UScriptStatement* CreateStatement(UScriptStatement::StatementType type);

    // ExpressionStatement

    static UScriptStatement* CreateExpressionStatement(UScriptExpression *expression);

    // IFStatement

    static UScriptStatement* CreateIfStatement(UScriptExpression *condition,

        UScriptStatementList *list1,

        UScriptStatementList *list2 = nullptr,

        UScriptStatement* nextIfState = nullptr);

    // FORStatement

    static UScriptStatement* CreateForStatement(UScriptExpression *before,

        UScriptExpression *condition,

        UScriptExpression *after,

        UScriptStatementList *list);

    // while

    static UScriptStatement* CreateWhileStatement(UScriptExpression *condition, UScriptStatementList *list);



    virtual UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) = 0;



    StatementType GetType() const

    {

        return type_;

    }



private:

    enum StatementType type_;

};



class UScriptStatementCtrl : public UScriptStatement {

public:

    explicit UScriptStatementCtrl(UScriptStatement::StatementType type) : UScriptStatement(type) {}

    ~UScriptStatementCtrl() override {}

    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) override;

};



class UScriptExpressionStatement : public UScriptStatement {

public:

    explicit UScriptExpressionStatement(UScriptExpression *expression)

        : UScriptStatement(STATEMENT_TYPE_EXPRESSION), expression_(expression) {}

    ~UScriptExpressionStatement() override;

    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) override;

private:

    UScriptExpression* expression_;

};



class UScriptIfStatement : public UScriptStatement {

public:

    UScriptIfStatement(UScriptExpression *expression, UScriptStatementList *statements)

        : UScriptStatement(STATEMENT_TYPE_IF), expression_(expression), trueStatements_(statements)  {}

    ~UScriptIfStatement() override;

    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) override;



    void AddFalseStatementList(UScriptStatementList *statements)

    {

        falseStatements_ = statements;

    }



    void AddNextStatement(UScriptIfStatement *statement)

    {

        nextStatement_ = statement;

    }

private:

    UScriptExpression* expression_ = nullptr;

    UScriptStatementList* trueStatements_ = nullptr;

    UScriptStatementList* falseStatements_ = nullptr;

    UScriptIfStatement* nextStatement_ = nullptr;

};



class UScriptForStatement : public UScriptStatement {

public:

    UScriptForStatement(UScriptExpression *before, UScriptExpression *condition, UScriptExpression *after,

        UScriptStatementList *statements) : UScriptStatement(STATEMENT_TYPE_FOR), before_(before),

        condition_(condition), after_(after), statements_(statements) {}



    ~UScriptForStatement() override;

    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) override;



private:

    UScriptExpression* before_ = nullptr;

    UScriptExpression* condition_ = nullptr;

    UScriptExpression* after_ = nullptr;

    UScriptStatementList* statements_ = nullptr;

};



class UScriptWhileStatement : public UScriptStatement {

public:

    UScriptWhileStatement(UScriptExpression *condition, UScriptStatementList *statements)

        : UScriptStatement(STATEMENT_TYPE_WHILE), condition_(condition), statements_(statements) {}



    ~UScriptWhileStatement() override;

    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) override;



private:

    UScriptExpression *condition_ = nullptr;

    UScriptStatementList *statements_ = nullptr;

};



class UScriptReturnStatement : public UScriptStatement {

public:

    UScriptReturnStatement() : UScriptStatement(STATEMENT_TYPE_RTN) {}



    ~UScriptReturnStatement() override;

    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context) override;



    void AddParams(ScriptParams* params)

    {

        params_ = params;

    }



    static UScriptReturnStatement* CreateStatement(ScriptParams *params);



private:

    ScriptParams* params_ {};

};



class UScriptStatementList {

public:

    UScriptStatementList() {}



    ~UScriptStatementList();



    static UScriptStatementList* CreateInstance(UScriptStatement *statement);

    void AddScriptStatement(UScriptStatement *statement);



    UScriptStatementResult Execute(ScriptInterpreter &interpreter, UScriptContextPtr context);



    static UScriptStatementResult DoExecute(ScriptInterpreter &inter, UScriptContextPtr context,

        UScriptStatementList *statements);



private:

    std::vector<UScriptStatement*> statements_ = {};

};

} // namespace Uscript

#endif // USCRIPT_STATEMENT_H