//////////////////////////////////////////////////////////////////////////
 public int GetTokenIndexAt(int Line, int Col)
 {
     for (int i = 0; i < Tokens.Count; i++)
     {
         ScriptToken Token = Tokens[i];
         if ((Token.StartLine < Line || (Token.StartLine == Line && Token.StartCol <= Col)) &&
             (Token.EndLine > Line || (Token.EndLine == Line && Token.EndCol >= Col)))
         {
             return(i);
         }
     }
     return(-1);
 }
        //////////////////////////////////////////////////////////////////////////
        public ScriptToken GetPrevToken(ScriptToken Token)
        {
            if (Token == null)
            {
                return(null);
            }
            int Index = Tokens.IndexOf(Token) - 1;

            if (Index < 0)
            {
                return(null);
            }
            else
            {
                return(Tokens[Index]);
            }
        }
        //////////////////////////////////////////////////////////////////////////
        public ScriptToken GetNearestTokenAt(int Line, int Col)
        {
            ScriptToken PrevToken = null;

            foreach (ScriptToken Token in Tokens)
            {
                if (PrevToken != null)
                {
                    if ((Token.StartLine > Line || (Token.StartLine == Line && Token.StartCol > Col)))
                    {
                        return(PrevToken);
                    }
                }
                PrevToken = Token;
            }
            return(PrevToken);
        }
        //////////////////////////////////////////////////////////////////////////
        private ScriptScope ParseEventHandler(ref List <ScriptToken> .Enumerator TokenEnum)
        {
            ScriptToken Token = TokenEnum.Current;

            if (Token == null)
            {
                return(null);
            }

            // setup handler
            ScriptEventHandler Handler = new ScriptEventHandler();

            Handler.Signature = Token.Value + " ";
            Handler.Line      = Token.OrigLine;
            Handler.Filename  = Token.Filename;

            // setup scope
            ScriptScope Scope = new ScriptScope();

            Scope.Owner     = Handler;
            Scope.StartLine = Token.StartLine;
            Scope.StartCol  = Token.StartCol;

            while (TokenEnum.MoveNext())
            {
                Token = TokenEnum.Current;
                if (Token.IsComment)
                {
                    continue;
                }

                if (Token.IsString)
                {
                    Handler.Signature += Token.Value;
                    Handler.Name       = Token.Value.Trim(new char[] { '\"' });
                    break;
                }
                else
                {
                    return(null);
                }
            }
            EventHandlers.Add(Handler);
            return(Scope);
        }
        //////////////////////////////////////////////////////////////////////////
        public int GetNearestLeftTokenIndexAt(int Line, int Col)
        {
            int PrevTokenIndex = -1;

            for (int i = 0; i < Tokens.Count; i++)
            {
                ScriptToken Token = Tokens[i];
                if (PrevTokenIndex >= 0)
                {
                    if ((Token.StartLine > Line || (Token.StartLine == Line && Token.StartCol >= Col)))
                    {
                        return(PrevTokenIndex);
                    }
                }
                PrevTokenIndex = i;
            }
            return(PrevTokenIndex);
        }
        //////////////////////////////////////////////////////////////////////////
        private void StoreToken(string TokenStr, ScriptToken.TokenType Type, int StartLine, int StartCol, int EndLine, int EndCol)
        {
            if (TokenStr == "")
            {
                return;
            }

            if (Type == ScriptToken.TokenType.Unknown)
            {
                if (IsKeyword(TokenStr))
                {
                    Type = ScriptToken.TokenType.Keyword;
                }
                else if (IsOperator(TokenStr))
                {
                    Type = ScriptToken.TokenType.Operator;
                }
                else if (char.IsNumber(TokenStr[0]))
                {
                    Type = ScriptToken.TokenType.Number;
                }
                else
                {
                    Type = ScriptToken.TokenType.Identifier;
                }
            }
            ScriptToken Token = new ScriptToken(TokenStr, Type, Filename, StartLine, StartCol, EndLine, EndCol);

            Tokens.Add(Token);

            // handle #includes
            if (Type == ScriptToken.TokenType.String && Tokens.Count > 1)
            {
                ScriptToken PrevToken = Tokens[Tokens.Count - 2];
                if (PrevToken.Type == ScriptToken.TokenType.Identifier && PrevToken.Value == "#include")
                {
                    String IncPath = FileManager.GetFullPath(Token.Value.Trim(new char[] { '\"' }));
                    if (IncPath != null)
                    {
                        ReadInclude(IncPath);
                    }
                }
            }
        }
        //////////////////////////////////////////////////////////////////////////
        public ScriptToken GetNextToken(ScriptToken Token)
        {
            if (Token == null)
            {
                return(null);
            }
            int Index = Tokens.IndexOf(Token);

            if (Index < 0)
            {
                return(null);
            }

            Index++;
            if (Index < Tokens.Count)
            {
                return(Tokens[Index]);
            }
            else
            {
                return(null);
            }
        }
        //////////////////////////////////////////////////////////////////////////
        private void StoreToken(string TokenStr, ScriptToken.TokenType Type, int StartLine, int StartCol, int EndLine, int EndCol)
        {
            if (TokenStr == "") return;

            if(Type==ScriptToken.TokenType.Unknown)
            {
                if (IsKeyword(TokenStr)) Type = ScriptToken.TokenType.Keyword;
                else if (IsOperator(TokenStr)) Type = ScriptToken.TokenType.Operator;
                else if (char.IsNumber(TokenStr[0])) Type = ScriptToken.TokenType.Number;
                else Type = ScriptToken.TokenType.Identifier;
            }
            ScriptToken Token = new ScriptToken(TokenStr, Type, Filename, StartLine, StartCol, EndLine, EndCol);
            Tokens.Add(Token);

            // handle #includes
            if(Type==ScriptToken.TokenType.String && Tokens.Count > 1)
            {
                ScriptToken PrevToken = Tokens[Tokens.Count - 2];
                if(PrevToken.Type==ScriptToken.TokenType.Identifier && PrevToken.Value=="#include")
                {
                    String IncPath = FileManager.GetFullPath(Token.Value.Trim(new char[] { '\"' }));
                    if (IncPath != null) ReadInclude(IncPath);
                }
            }
        }
        //////////////////////////////////////////////////////////////////////////
        public ScriptToken GetPrevToken(ScriptToken Token)
        {
            if (Token == null) return null;
            int Index = Tokens.IndexOf(Token) - 1;

            if (Index < 0) return null;
            else return Tokens[Index];
        }
        //////////////////////////////////////////////////////////////////////////
        public ScriptToken GetNextToken(ScriptToken Token)
        {
            if (Token == null) return null;
            int Index = Tokens.IndexOf(Token);
            if (Index < 0) return null;

            Index++;
            if (Index < Tokens.Count) return Tokens[Index];
            else return null;
        }
        //////////////////////////////////////////////////////////////////////////
        private bool Parse()
        {
            // cleanup
            GlobalScope   = new ScriptScope();
            Scopes        = new List <ScriptScope>();
            Functions     = new List <ScriptFunction>();
            EventHandlers = new List <ScriptEventHandler>();

            _Tokenizer = new ScriptTokenizer(Reader);
            if (!_Tokenizer.GetTokens())
            {
                return(false);
            }

            ScriptScope CurrentScope = GlobalScope;

            bool ReadingBlock = false;
            int  BlockNest    = 0;

            List <ScriptToken> .Enumerator TokenEnum = _Tokenizer.Tokens.GetEnumerator();
            while (TokenEnum.MoveNext())
            {
                ScriptToken Token = TokenEnum.Current;
                if (Token.IsComment)
                {
                    continue;
                }

                // functions
                if (Token.IsKeyword && (Token.Value == "function" || Token.Value == "method" || Token.Value == "external"))
                {
                    ScriptScope Scope = ParseFunction(ref TokenEnum);
                    if (Scope != null)
                    {
                        if (ReadingBlock)
                        {
                            CurrentScope.EndLine = Token.StartLine;
                            CurrentScope.EndCol  = Token.StartCol;
                        }

                        CurrentScope = Scope;
                        Scopes.Add(Scope);
                        ReadingBlock = true;
                        BlockNest    = 0;
                    }
                }

                // event handles
                else if (Token.IsKeyword && Token.Value == "on")
                {
                    ScriptScope Scope = ParseEventHandler(ref TokenEnum);
                    if (Scope != null)
                    {
                        if (ReadingBlock)
                        {
                            CurrentScope.EndLine = Token.StartLine;
                            CurrentScope.EndCol  = Token.StartCol;
                        }

                        CurrentScope = Scope;
                        Scopes.Add(Scope);
                        ReadingBlock = true;
                        BlockNest    = 0;
                    }
                }

                // variables
                else if (Token.IsKeyword && (Token.Value == "var" || Token.Value == "global" || Token.Value == "const"))
                {
                    ScriptVariable Var = ParseVariable(ref TokenEnum, false);
                    if (Var != null && Var.Name != "")
                    {
                        if (ReadingBlock && BlockNest > 0)
                        {
                            CurrentScope.Variables.Add(Var);
                        }
                        else
                        {
                            GlobalScope.Variables.Add(Var);
                        }
                    }
                }

                // curly braces
                else if (Token.IsOperator)
                {
                    if (Token.Value == "{" && ReadingBlock)
                    {
                        BlockNest++;
                    }
                    else if (Token.Value == "}" && ReadingBlock)
                    {
                        BlockNest--;
                        if (BlockNest <= 0)
                        {
                            ReadingBlock         = false;
                            CurrentScope.EndLine = Token.EndLine;
                            CurrentScope.EndCol  = Token.EndCol;
                        }
                    }
                    else if (Token.Value == ";" && ReadingBlock && BlockNest == 0)
                    {
                        ReadingBlock         = false;
                        CurrentScope.EndLine = Token.EndLine;
                        CurrentScope.EndCol  = Token.EndCol;
                    }
                }
            }

            return(true);
        }
        //////////////////////////////////////////////////////////////////////////
        private ScriptVariable ParseVariable(ref List <ScriptToken> .Enumerator TokenEnum, bool Param)
        {
            ScriptToken Token = TokenEnum.Current;

            if (Token == null)
            {
                return(null);
            }

            ScriptVariable Var = new ScriptVariable();

            Var.Line     = Token.OrigLine;
            Var.Filename = Token.Filename;

            if (Param)
            {
                Var.Type = ScriptVariableType.Parameter;
            }
            else
            {
                switch (Token.Value)
                {
                case "var":
                    Var.Type = ScriptVariableType.Var;
                    break;

                case "global":
                    Var.Type = ScriptVariableType.Global;
                    break;

                case "const":
                    Var.Type = ScriptVariableType.Const;
                    break;

                default:
                    Var.Type = ScriptVariableType.Unknown;
                    break;
                }
            }

            while (true)
            {
                if (!Token.IsComment)
                {
                    if (Token.IsIdentifier)
                    {
                        if (Var.Name == "")
                        {
                            Var.Name = Token.Value;
                        }
                        Var.Signature += Token.Value + " ";
                        if (Param)
                        {
                            break;
                        }
                    }
                    else if (Token.IsOperator && (Token.Value == ";" || Token.Value == ","))
                    {
                        break;
                    }
                    else if (Token.IsOperator && Param && Token.Value == ")")
                    {
                        break;
                    }

                    else
                    {
                        Var.Signature += Token.Value + " ";
                    }
                }


                if (!TokenEnum.MoveNext())
                {
                    return(null);
                }
                Token = TokenEnum.Current;
            }

            Var.Signature = Var.Signature.Trim();
            if (Var.Signature != "")
            {
                Var.ValidAfterLine = Token.EndLine;
                Var.ValidAfterCol  = Token.EndCol;

                return(Var);
            }
            else
            {
                return(null);
            }
        }
        //////////////////////////////////////////////////////////////////////////
        private ScriptScope ParseFunction(ref List <ScriptToken> .Enumerator TokenEnum)
        {
            ScriptToken Token = TokenEnum.Current;

            if (Token == null)
            {
                return(null);
            }

            // setup function
            ScriptFunction Func = new ScriptFunction();

            Func.Signature = Token.Value + " ";
            Func.Line      = Token.OrigLine;
            Func.Filename  = Token.Filename;
            switch (Token.Value)
            {
            case "function":
                Func.Type = ScriptFunctionType.Function;
                break;

            case "method":
                Func.Type = ScriptFunctionType.Method;
                break;

            case "external":
                Func.Type = ScriptFunctionType.External;
                break;

            default:
                Func.Type = ScriptFunctionType.Unknown;
                break;
            }

            // setup scope
            ScriptScope Scope = new ScriptScope();

            Scope.Owner     = Func;
            Scope.StartLine = Token.StartLine;
            Scope.StartCol  = Token.StartCol;

            bool ReadingName   = true;
            bool ReadingParams = false;

            while (TokenEnum.MoveNext())
            {
                Token = TokenEnum.Current;
                if (Token.IsComment)
                {
                    continue;
                }

                if (ReadingName)
                {
                    if (!Token.IsIdentifier)
                    {
                        if (Func.Type == ScriptFunctionType.External)
                        {
                            Func.Signature += Token.Value + " ";
                        }
                        else
                        {
                            return(null);
                        }
                    }
                    else
                    {
                        Func.Name       = Token.Value;
                        Func.Signature += Token.Value;

                        ReadingName = false;
                    }
                }
                else if (ReadingParams)
                {
                    if (Token.IsOperator && Token.Value == ")")
                    {
                        Func.Signature += Token.Value;
                        break;
                    }
                    else if (Token.IsOperator && Token.Value == ",")
                    {
                        Func.Signature += Token.Value + " ";
                    }
                    else
                    {
                        ScriptVariable Var = ParseVariable(ref TokenEnum, true);
                        if (Var == null)
                        {
                            return(null);
                        }
                        else
                        {
                            Func.Signature += Var.Signature;
                            Func.Params.Add(Var.Signature);

                            if (Var.Name != "")
                            {
                                Scope.Variables.Add(Var);
                            }

                            // hack because of unnamed external's parameters
                            Token = TokenEnum.Current;
                            if (Token.IsOperator && Token.Value == ")")
                            {
                                Func.Signature += Token.Value;
                                break;
                            }
                        }
                    }
                }
                else
                {
                    if (Token.IsOperator && Token.Value == "(")
                    {
                        ReadingParams   = true;
                        Func.Signature += Token.Value;
                    }
                    else
                    {
                        return(null);
                    }
                }
            }

            Functions.Add(Func);
            return(Scope);
        }