#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 ForeachNode : AstNode
    {
        object result = null;
        private AstNode InitializeAst;
        private AstNode ConditionAst;
        private AstNode UpdateLoopAst;
        private AstNode LoopBody;


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

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

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

                
            }
            else
            {
                //刻み値の指定は別で対処。
                
                //AddChild(NodeUseType.ValueRead , "NullreturnValue" , null);
                
            }
            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;
            var CounterIncParseTree = thread.App.Parser.Parse(loopCounterName + "++");

            //条件値を取得し、評価式を作成
            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  , CounterIncParseTree);
                        continue;
                    }
                }
                Update(thread , CounterIncParseTree);
            } 

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


        private void Update(ScriptThread thread  , ParseTree CounterIncParseTree)
        {
            ////インクリメントの結果、評価結果が表示されないようにするため、
            ////保存しておく
            //var outputSaveInt = thread.App.OutputBuffer.Length;

            //インクリメント実施
            thread.App.Evaluate(CounterIncParseTree , thread.CurrentScope);

            ////出力内容を戻す
            //thread.App.OutputBuffer.Remove(outputSaveInt , thread.App.OutputBuffer.Length - outputSaveInt);
        }

        private bool TestCondition(ScriptThread thread , ParseTree Test)
        {
            ////インクリメントの結果、評価結果が表示されないようにするため、
            ////保存しておく
            //var outputSaveInt = thread.App.OutputBuffer.Length;
            
            //条件式評価
            var result = (bool)thread.App.Evaluate(Test , thread.CurrentScope);

            ////出力内容を戻す
           //thread.App.OutputBuffer.Remove(outputSaveInt , thread.App.OutputBuffer.Length - outputSaveInt);.

            return result;
        }

    }//class

}//namespace
