#region License
/* **********************************************************************************
 * Copyright (c) Roman Ivantsov
 * This source code is subject to terms and conditions of the MIT License
 * for Irony. A copy of the license can be found in the License.txt file
 * at the root of this distribution. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * MIT License.
 * You must not remove this notice from this software.
 * **********************************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Irony.Ast;
using Irony.Parsing;

namespace Irony.Interpreter.Ast
{

    public class ForNode : AstNode
    {
        object result = null;
        private AstNode InitializeAst;
        private AstNode ConditionAst;
        private AstNode opt_stepAst;
        private AstNode LoopBody;




        //var ForStmt_init = new NonTerminal("ForStmt_init");
        //var ForStmt_endvalue = new NonTerminal("ForStmt_endvalue");
        //var ForStmt_opt_step = new NonTerminal("ForStmt_opt_step"); 
        //var ForStmt_body = new NonTerminal("ForStmt_body", typeof(ExtStatementListNode));

        //ForStmt.Rule = ForStmt_init + ForStmt_endvalue + ForStmt_opt_step + ForStmt_body;

        //ForStmt_init.Rule = "for" + AssignmentStmt;
        //ForStmt_endvalue.Rule = "to" +  Expr;
        //ForStmt_opt_step.Rule = "step" + Expr + NewLine | NewLine;
        //ForStmt_body.Rule = Block + "next";

        public override void Init(AstContext context , ParseTreeNode treeNode)
        {
            base.Init(context , treeNode);
            //treenodeのchildnodeが２つあるとき、返値用のASTノードを追加
            if(treeNode.ChildNodes != null)
            {
                if(treeNode.ChildNodes.Count == 6)
                {
                    //仮変数＝初期値ノードをASTに追加
                    InitializeAst = AddChild(NodeUseType.ValueReadWrite, "ItelatorInit", treeNode.ChildNodes[1]);

                    //終了値ノードをASTに追加
                    ConditionAst = AddChild(NodeUseType.ValueReadWrite, "LoopEndValue", treeNode.ChildNodes[3]);

                    //刻み値  EmptyStatementNodeならchildnode==0
                    if(treeNode.ChildNodes[4].ChildNodes.Count == 0)
                    {
                        opt_stepAst = null;
                    }
                    else
                    {
                        opt_stepAst = AddChild(NodeUseType.ValueReadWrite, "stepValue", treeNode.ChildNodes[4]);
                    }

                    //ループボディをASTに追加　　　　.CallTarget？？
                    LoopBody = AddChild(NodeUseType.Unknown, "LoopBody", treeNode.ChildNodes[5]);
                }

                AsString = "for";
            }



        }

        object LockObject = new object();
        protected override object DoEvaluate(ScriptThread thread)
        {

            //return、break、Continueを受け取る
            resultWithReturnType resWithRet = null;

            thread.CurrentNode = this;  //standard prolog

            //ループカウンタ初期化
            InitializeAst.Evaluate(thread);

            //変数名を取得し、インクリメントの式を作成する
            var loopCounterName = ((IdentifierNode)InitializeAst.ChildNodes[0]).Symbol;
            
            ParseTree CounterUpdateParseTree;
            //if(opt_stepAst == null)
            if(opt_stepAst == null)
            {
                CounterUpdateParseTree = thread.App.Parser.Parse(loopCounterName + "++");
            }
            else
            {
                CounterUpdateParseTree = thread.App.Parser.Parse(loopCounterName + "+=" + opt_stepAst.Evaluate(thread));
            }


            //条件値を取得し、評価式を作成
            var endValue = ConditionAst.Evaluate(thread);
            var Test = thread.App.Parser.Parse(loopCounterName + "<=" + endValue);


            //ループ処理
            while ( TestCondition(thread , Test) )
            {
                //ブロック実行
                result = LoopBody.Evaluate(thread);

                //Break,Continueで途中で抜けた場合、resultWithReturnTypeが戻ってくる。
                resWithRet = result as resultWithReturnType;
                if (resWithRet != null)
                {
                    if (resWithRet.retType == ReturnType.EXIT_LOOP )
                    {
                        //１階層上に戻る
                        result = resWithRet.resultObj;
                        break;
                    }

                    if ( resWithRet.retType == ReturnType.EXIT_FUNCTION)
                    {
                        //１階層上に戻るが、関数を抜けるまでフラグを立てたままにする。
                        break;
                    }

                    if (resWithRet.retType == ReturnType.CONTINUE_LOOP)
                    {
                        Update(thread  , CounterUpdateParseTree);
                        continue;
                    }
                }
                Update(thread , CounterUpdateParseTree);
            } 

            thread.CurrentNode = Parent; //standard epilog
            return result;
        }//method


        private void Update(ScriptThread thread  , ParseTree CounterIncParseTree)
        {
            //インクリメント実施
            thread.App.Evaluate(CounterIncParseTree , thread.CurrentScope);
        }

        private bool TestCondition(ScriptThread thread , ParseTree Test)
        {
            //条件式評価
            return (bool)thread.App.Evaluate(Test, thread.CurrentScope);
        }

    }//class

}//namespace
