#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 ItelatorAst;
        private AstNode collectionAst;
        private AstNode UpdateLoopAst;
        private AstNode LoopBody;


        public override void Init(AstContext context , ParseTreeNode treeNode)
        {
            base.Init(context , treeNode);


            //  0     1  2  3
            //foreach xx in collection
            //  body 4
            //next   5

            //treenodeのchildnodeが２つあるとき、返値用のASTノードを追加
            if (treeNode.ChildNodes != null && treeNode.ChildNodes.Count >= 4)
            {

                //仮変数 をASTに追加
                ItelatorAst = AddChild(NodeUseType.ValueReadWrite, "Itelator", treeNode.ChildNodes[1]);
                

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

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

                
            }
            else
            {
                //刻み値の指定は別で対処。
                
                //AddChild(NodeUseType.ValueRead , "NullreturnValue" , null);
                
            }
            AsString = "foreach";
        }

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

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

            thread.CurrentNode = this;  //standard prolog



            //コレクションを取得
            var col = collectionAst.Evaluate(thread) ;
            var colIenumerator = col as System.Collections.IEnumerable; //IEnumerator<object>;

            if(colIenumerator == null)
            {
                throw new ArgumentException("foreach に指定されたコレクションがIEnumerableではありません");
            }

            //仮引数を宣言し、nullで初期化
            ItelatorAst.DoSetValue(thread, null);


            //ループ処理
            foreach(var item in colIenumerator)
            {
                ////値をセット
                //var bind = thread.Bind(iteletorname, BindingRequestFlags.ExistingOrNew | BindingRequestFlags.Write);
                //bind.SetValueRef(thread, item);

                ItelatorAst.DoSetValue(thread, item);

                //ブロック実行
                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)
                    {
                        continue;
                    }
                }
            } 

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




        bool IsGenericsIEnumerable(Type type)
        {
            return type
                .GetInterfaces()
                .Any(t => t.IsGenericType &&
                    t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
        }




    }//class

}//namespace
