/*
 * Copyright (c) Contributors, http://opensimulator.org/
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSim Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.IO;
using System.Collections.Generic;
using Tools;

namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
{
    public class CSCodeGenerator : ICodeConverter
    {
        private SYMBOL m_astRoot = null;
        private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> m_positionMap;
        private int m_indentWidth = 4;  // for indentation
        private int m_braceCount;       // for indentation
        private int m_CSharpLine;       // the current line of generated C# code
        private int m_CSharpCol;        // the current column of generated C# code

        /// <summary>
        /// Creates an 'empty' CSCodeGenerator instance.
        /// </summary>
        public CSCodeGenerator()
        {
            ResetCounters();
        }

        /// <summary>
        /// Get the mapping between LSL and C# line/column number.
        /// </summary>
        /// <returns>Dictionary\<KeyValuePair\<int, int\>, KeyValuePair\<int, int\>\>.</returns>
        public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> PositionMap
        {
            get { return m_positionMap; }
        }

        /// <summary>
        /// Get the mapping between LSL and C# line/column number.
        /// </summary>
        /// <returns>SYMBOL pointing to root of the abstract syntax tree.</returns>
        public SYMBOL ASTRoot
        {
            get { return m_astRoot; }
        }

        /// <summary>
        /// Resets various counters and metadata.
        /// </summary>
        private void ResetCounters()
        {
            m_braceCount = 0;
            m_CSharpLine = 0;
            m_CSharpCol = 1;
            m_positionMap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
            m_astRoot = null;
        }

        /// <summary>
        /// Generate the code from the AST we have.
        /// </summary>
        /// <param name="script">The LSL source as a string.</param>
        /// <returns>String containing the generated C# code.</returns>
        public string Convert(string script)
        {
            ResetCounters();
            Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true));
            // Obviously this needs to be in a try/except block.
            LSL2CSCodeTransformer codeTransformer = new LSL2CSCodeTransformer(p.Parse(script));
            m_astRoot = codeTransformer.Transform();

            string retstr = String.Empty;

            // standard preamble
            //retstr = GenerateLine("using OpenSim.Region.ScriptEngine.Common;");
            //retstr += GenerateLine("using System.Collections.Generic;");
            //retstr += GenerateLine("");
            //retstr += GenerateLine("namespace SecondLife");
            //retstr += GenerateLine("{");
            m_braceCount++;
            //retstr += GenerateIndentedLine("public class Script : OpenSim.Region.ScriptEngine.Common");
            //retstr += GenerateIndentedLine("{");
            m_braceCount++;

            // line number
            m_CSharpLine += 3;

            // here's the payload
            retstr += GenerateLine();
            foreach (SYMBOL s in m_astRoot.kids)
                retstr += GenerateNode(s);

            // close braces!
            m_braceCount--;
            //retstr += GenerateIndentedLine("}");
            m_braceCount--;
            //retstr += GenerateLine("}");

            return retstr;
        }

        /// <summary>
        /// Recursively called to generate each type of node. Will generate this
        /// node, then all it's children.
        /// </summary>
        /// <param name="s">The current node to generate code for.</param>
        /// <returns>String containing C# code for SYMBOL s.</returns>
        private string GenerateNode(SYMBOL s)
        {
            string retstr = String.Empty;

            // make sure to put type lower in the inheritance hierarchy first
            // ie: since IdentArgument and ExpressionArgument inherit from
            // Argument, put IdentArgument and ExpressionArgument before Argument
            if (s is GlobalFunctionDefinition)
                retstr += GenerateGlobalFunctionDefinition((GlobalFunctionDefinition) s);
            else if (s is GlobalVariableDeclaration)
                retstr += GenerateGlobalVariableDeclaration((GlobalVariableDeclaration) s);
            else if (s is State)
                retstr += GenerateState((State) s);
            else if (s is CompoundStatement)
                retstr += GenerateCompoundStatement((CompoundStatement) s);
            else if (s is Declaration)
                retstr += GenerateDeclaration((Declaration) s);
            else if (s is Statement)
                retstr += GenerateStatement((Statement) s);
            else if (s is ReturnStatement)
                retstr += GenerateReturnStatement((ReturnStatement) s);
            else if (s is JumpLabel)
                retstr += GenerateJumpLabel((JumpLabel) s);
            else if (s is JumpStatement)
                retstr += GenerateJumpStatement((JumpStatement) s);
            else if (s is StateChange)
                retstr += GenerateStateChange((StateChange) s);
            else if (s is IfStatement)
                retstr += GenerateIfStatement((IfStatement) s);
            else if (s is WhileStatement)
                retstr += GenerateWhileStatement((WhileStatement) s);
            else if (s is DoWhileStatement)
                retstr += GenerateDoWhileStatement((DoWhileStatement) s);
            else if (s is ForLoop)
                retstr += GenerateForLoop((ForLoop) s);
            else if (s is ArgumentList)
                retstr += GenerateArgumentList((ArgumentList) s);
            else if (s is Assignment)
                retstr += GenerateAssignment((Assignment) s);
            else if (s is BinaryExpression)
                retstr += GenerateBinaryExpression((BinaryExpression) s);
            else if (s is ParenthesisExpression)
                retstr += GenerateParenthesisExpression((ParenthesisExpression) s);
            else if (s is UnaryExpression)
                retstr += GenerateUnaryExpression((UnaryExpression) s);
            else if (s is IncrementDecrementExpression)
                retstr += GenerateIncrementDecrementExpression((IncrementDecrementExpression) s);
            else if (s is TypecastExpression)
                retstr += GenerateTypecastExpression((TypecastExpression) s);
            else if (s is FunctionCall)
                retstr += GenerateFunctionCall((FunctionCall) s);
            else if (s is VectorConstant)
                retstr += GenerateVectorConstant((VectorConstant) s);
            else if (s is RotationConstant)
                retstr += GenerateRotationConstant((RotationConstant) s);
            else if (s is ListConstant)
                retstr += GenerateListConstant((ListConstant) s);
            else if (s is Constant)
                retstr += GenerateConstant((Constant) s);
            else if (s is IdentDotExpression)
                retstr += Generate(((IdentDotExpression) s).Name + "." + ((IdentDotExpression) s).Member, s);
            else if (s is IdentExpression)
                retstr += Generate(((IdentExpression) s).Name, s);
            else if (s is IDENT)
                retstr += Generate(((TOKEN) s).yytext, s);
            else
            {
                foreach (SYMBOL kid in s.kids)
                    retstr += GenerateNode(kid);
            }

            return retstr;
        }

        /// <summary>
        /// Generates the code for a GlobalFunctionDefinition node.
        /// </summary>
        /// <param name="gf">The GlobalFunctionDefinition node.</param>
        /// <returns>String containing C# code for GlobalFunctionDefinition gf.</returns>
        private string GenerateGlobalFunctionDefinition(GlobalFunctionDefinition gf)
        {
            string retstr = String.Empty;

            // we need to separate the argument declaration list from other kids
            List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
            List<SYMBOL> remainingKids = new List<SYMBOL>();

            foreach (SYMBOL kid in gf.kids)
                if (kid is ArgumentDeclarationList)
                    argumentDeclarationListKids.Add(kid);
                else
                    remainingKids.Add(kid);

            retstr += GenerateIndented(String.Format("{0} {1}(", gf.ReturnType, gf.Name), gf);

            // print the state arguments, if any
            foreach (SYMBOL kid in argumentDeclarationListKids)
                retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);

            retstr += GenerateLine(")");

            foreach (SYMBOL kid in remainingKids)
                retstr += GenerateNode(kid);

            return retstr;
        }

        /// <summary>
        /// Generates the code for a GlobalVariableDeclaration node.
        /// </summary>
        /// <param name="gv">The GlobalVariableDeclaration node.</param>
        /// <returns>String containing C# code for GlobalVariableDeclaration gv.</returns>
        private string GenerateGlobalVariableDeclaration(GlobalVariableDeclaration gv)
        {
            string retstr = String.Empty;

            foreach (SYMBOL s in gv.kids)
            {
                retstr += Indent();
                retstr += GenerateNode(s);
                retstr += GenerateLine(";");
            }

            return retstr;
        }

        /// <summary>
        /// Generates the code for a State node.
        /// </summary>
        /// <param name="s">The State node.</param>
        /// <returns>String containing C# code for State s.</returns>
        private string GenerateState(State s)
        {
            string retstr = String.Empty;

            foreach (SYMBOL kid in s.kids)
                if (kid is StateEvent)
                    retstr += GenerateStateEvent((StateEvent) kid, s.Name);

            return retstr;
        }

        /// <summary>
        /// Generates the code for a StateEvent node.
        /// </summary>
        /// <param name="se">The StateEvent node.</param>
        /// <param name="parentStateName">The name of the parent state.</param>
        /// <returns>String containing C# code for StateEvent se.</returns>
        private string GenerateStateEvent(StateEvent se, string parentStateName)
        {
            string retstr = String.Empty;

            // we need to separate the argument declaration list from other kids
            List<SYMBOL> argumentDeclarationListKids = new List<SYMBOL>();
            List<SYMBOL> remainingKids = new List<SYMBOL>();

            foreach (SYMBOL kid in se.kids)
                if (kid is ArgumentDeclarationList)
                    argumentDeclarationListKids.Add(kid);
                else
                    remainingKids.Add(kid);

            // "state" (function) declaration
            retstr += GenerateIndented(String.Format("public void {0}_event_{1}(", parentStateName, se.Name), se);

            // print the state arguments, if any
            foreach (SYMBOL kid in argumentDeclarationListKids)
                retstr += GenerateArgumentDeclarationList((ArgumentDeclarationList) kid);

            retstr += GenerateLine(")");

            foreach (SYMBOL kid in remainingKids)
                retstr += GenerateNode(kid);

            return retstr;
        }

        /// <summary>
        /// Generates the code for an ArgumentDeclarationList node.
        /// </summary>
        /// <param name="adl">The ArgumentDeclarationList node.</param>
        /// <returns>String containing C# code for ArgumentDeclarationList adl.</returns>
        private string GenerateArgumentDeclarationList(ArgumentDeclarationList adl)
        {
            string retstr = String.Empty;

            int comma = adl.kids.Count - 1; // tells us whether to print a comma

            foreach (Declaration d in adl.kids)
            {
                retstr += Generate(String.Format("{0} {1}", d.Datatype, d.Id), d);
                if (0 < comma--)
                    retstr += Generate(", ");
            }

            return retstr;
        }

        /// <summary>
        /// Generates the code for an ArgumentList node.
        /// </summary>
        /// <param name="al">The ArgumentList node.</param>
        /// <returns>String containing C# code for ArgumentList al.</returns>
        private string GenerateArgumentList(ArgumentList al)
        {
            string retstr = String.Empty;

            int comma = al.kids.Count - 1;  // tells us whether to print a comma

            foreach (SYMBOL s in al.kids)
            {
                retstr += GenerateNode(s);
                if (0 < comma--)
                    retstr += Generate(", ");
            }

            return retstr;
        }

        /// <summary>
        /// Generates the code for a CompoundStatement node.
        /// </summary>
        /// <param name="cs">The CompoundStatement node.</param>
        /// <returns>String containing C# code for CompoundStatement cs.</returns>
        private string GenerateCompoundStatement(CompoundStatement cs)
        {
            string retstr = String.Empty;

            // opening brace
            retstr += GenerateIndentedLine("{");
            m_braceCount++;

            foreach (SYMBOL kid in cs.kids)
                retstr += GenerateNode(kid);

            // closing brace
            m_braceCount--;
            retstr += GenerateIndentedLine("}");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a Declaration node.
        /// </summary>
        /// <param name="d">The Declaration node.</param>
        /// <returns>String containing C# code for Declaration d.</returns>
        private string GenerateDeclaration(Declaration d)
        {
            return Generate(String.Format("{0} {1}", d.Datatype, d.Id), d);
        }

        /// <summary>
        /// Generates the code for a Statement node.
        /// </summary>
        /// <param name="s">The Statement node.</param>
        /// <returns>String containing C# code for Statement s.</returns>
        private string GenerateStatement(Statement s)
        {
            string retstr = String.Empty;
            bool printSemicolon = true;

            retstr += Indent();

            if (0 < s.kids.Count)
            {
                // Jump label prints its own colon, we don't need a semicolon.
                printSemicolon = !(s.kids.Top is JumpLabel);

                foreach (SYMBOL kid in s.kids)
                    retstr += GenerateNode(kid);
            }

            if (printSemicolon)
                retstr += GenerateLine(";");

            return retstr;
        }

        /// <summary>
        /// Generates the code for an Assignment node.
        /// </summary>
        /// <param name="a">The Assignment node.</param>
        /// <returns>String containing C# code for Assignment a.</returns>
        private string GenerateAssignment(Assignment a)
        {
            string retstr = String.Empty;

            retstr += GenerateNode((SYMBOL) a.kids.Pop());
            retstr += Generate(String.Format(" {0} ", a.AssignmentType), a);
            foreach (SYMBOL kid in a.kids)
                retstr += GenerateNode(kid);

            return retstr;
        }

        /// <summary>
        /// Generates the code for a ReturnStatement node.
        /// </summary>
        /// <param name="rs">The ReturnStatement node.</param>
        /// <returns>String containing C# code for ReturnStatement rs.</returns>
        private string GenerateReturnStatement(ReturnStatement rs)
        {
            string retstr = String.Empty;

            retstr += Generate("return ", rs);

            foreach (SYMBOL kid in rs.kids)
                retstr += GenerateNode(kid);

            return retstr;
        }

        /// <summary>
        /// Generates the code for a JumpLabel node.
        /// </summary>
        /// <param name="jl">The JumpLabel node.</param>
        /// <returns>String containing C# code for JumpLabel jl.</returns>
        private string GenerateJumpLabel(JumpLabel jl)
        {
            return Generate(String.Format("{0}:\n", jl.LabelName), jl);
        }

        /// <summary>
        /// Generates the code for a JumpStatement node.
        /// </summary>
        /// <param name="js">The JumpStatement node.</param>
        /// <returns>String containing C# code for JumpStatement js.</returns>
        private string GenerateJumpStatement(JumpStatement js)
        {
            return Generate(String.Format("goto {0}", js.TargetName), js);
        }

        /// <summary>
        /// Generates the code for an IfStatement node.
        /// </summary>
        /// <param name="ifs">The IfStatement node.</param>
        /// <returns>String containing C# code for IfStatement ifs.</returns>
        private string GenerateIfStatement(IfStatement ifs)
        {
            string retstr = String.Empty;

            retstr += GenerateIndented("if (", ifs);
            retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
            retstr += GenerateLine(")");

            // CompoundStatement handles indentation itself but we need to do it
            // otherwise.
            bool indentHere = ifs.kids.Top is Statement;
            if (indentHere) m_braceCount++;
            retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
            if (indentHere) m_braceCount--;

            if (0 < ifs.kids.Count) // do it again for an else
            {
                retstr += GenerateIndentedLine("else", ifs);

                indentHere = ifs.kids.Top is Statement;
                if (indentHere) m_braceCount++;
                retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
                if (indentHere) m_braceCount--;
            }

            return retstr;
        }

        /// <summary>
        /// Generates the code for a StateChange node.
        /// </summary>
        /// <param name="sc">The StateChange node.</param>
        /// <returns>String containing C# code for StateChange sc.</returns>
        private string GenerateStateChange(StateChange sc)
        {
            return Generate(String.Format("state(\"{0}\")", sc.NewState), sc);
        }

        /// <summary>
        /// Generates the code for a WhileStatement node.
        /// </summary>
        /// <param name="ws">The WhileStatement node.</param>
        /// <returns>String containing C# code for WhileStatement ws.</returns>
        private string GenerateWhileStatement(WhileStatement ws)
        {
            string retstr = String.Empty;

            retstr += GenerateIndented("while (", ws);
            retstr += GenerateNode((SYMBOL) ws.kids.Pop());
            retstr += GenerateLine(")");

            // CompoundStatement handles indentation itself but we need to do it
            // otherwise.
            bool indentHere = ws.kids.Top is Statement;
            if (indentHere) m_braceCount++;
            retstr += GenerateNode((SYMBOL) ws.kids.Pop());
            if (indentHere) m_braceCount--;

            return retstr;
        }

        /// <summary>
        /// Generates the code for a DoWhileStatement node.
        /// </summary>
        /// <param name="dws">The DoWhileStatement node.</param>
        /// <returns>String containing C# code for DoWhileStatement dws.</returns>
        private string GenerateDoWhileStatement(DoWhileStatement dws)
        {
            string retstr = String.Empty;

            retstr += GenerateIndentedLine("do", dws);

            // CompoundStatement handles indentation itself but we need to do it
            // otherwise.
            bool indentHere = dws.kids.Top is Statement;
            if (indentHere) m_braceCount++;
            retstr += GenerateNode((SYMBOL) dws.kids.Pop());
            if (indentHere) m_braceCount--;

            retstr += GenerateIndented("while (", dws);
            retstr += GenerateNode((SYMBOL) dws.kids.Pop());
            retstr += GenerateLine(");");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a ForLoop node.
        /// </summary>
        /// <param name="fl">The ForLoop node.</param>
        /// <returns>String containing C# code for ForLoop fl.</returns>
        private string GenerateForLoop(ForLoop fl)
        {
            string retstr = String.Empty;

            retstr += GenerateIndented("for (", fl);

            // for ( x = 0 ; x < 10 ; x++ )
            //       ^^^^^^^
            retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop());
            retstr += Generate("; ");
            // for ( x = 0 ; x < 10 ; x++ )
            //               ^^^^^^^^
            retstr += GenerateNode((SYMBOL) fl.kids.Pop());
            retstr += Generate("; ");
            // for ( x = 0 ; x < 10 ; x++ )
            //                        ^^^^^
            retstr += GenerateForLoopStatement((ForLoopStatement) fl.kids.Pop());
            retstr += GenerateLine(")");

            // CompoundStatement handles indentation itself but we need to do it
            // otherwise.
            bool indentHere = fl.kids.Top is Statement;
            if (indentHere) m_braceCount++;
            retstr += GenerateNode((SYMBOL) fl.kids.Pop());
            if (indentHere) m_braceCount--;

            return retstr;
        }

        /// <summary>
        /// Generates the code for a ForLoopStatement node.
        /// </summary>
        /// <param name="fls">The ForLoopStatement node.</param>
        /// <returns>String containing C# code for ForLoopStatement fls.</returns>
        private string GenerateForLoopStatement(ForLoopStatement fls)
        {
            string retstr = String.Empty;

            int comma = fls.kids.Count - 1;  // tells us whether to print a comma

            foreach (SYMBOL s in fls.kids)
            {
                retstr += GenerateNode(s);
                if (0 < comma--)
                    retstr += Generate(", ");
            }

            return retstr;
        }

        /// <summary>
        /// Generates the code for a BinaryExpression node.
        /// </summary>
        /// <param name="be">The BinaryExpression node.</param>
        /// <returns>String containing C# code for BinaryExpression be.</returns>
        private string GenerateBinaryExpression(BinaryExpression be)
        {
            string retstr = String.Empty;

            retstr += GenerateNode((SYMBOL) be.kids.Pop());
            retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol), be);
            foreach (SYMBOL kid in be.kids)
                retstr += GenerateNode(kid);

            return retstr;
        }

        /// <summary>
        /// Generates the code for a UnaryExpression node.
        /// </summary>
        /// <param name="ue">The UnaryExpression node.</param>
        /// <returns>String containing C# code for UnaryExpression ue.</returns>
        private string GenerateUnaryExpression(UnaryExpression ue)
        {
            string retstr = String.Empty;

            retstr += Generate(ue.UnarySymbol, ue);
            retstr += GenerateNode((SYMBOL) ue.kids.Pop());

            return retstr;
        }

        /// <summary>
        /// Generates the code for a ParenthesisExpression node.
        /// </summary>
        /// <param name="pe">The ParenthesisExpression node.</param>
        /// <returns>String containing C# code for ParenthesisExpression pe.</returns>
        private string GenerateParenthesisExpression(ParenthesisExpression pe)
        {
            string retstr = String.Empty;

            retstr += Generate("(");
            foreach (SYMBOL kid in pe.kids)
                retstr += GenerateNode(kid);
            retstr += Generate(")");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a IncrementDecrementExpression node.
        /// </summary>
        /// <param name="ide">The IncrementDecrementExpression node.</param>
        /// <returns>String containing C# code for IncrementDecrementExpression ide.</returns>
        private string GenerateIncrementDecrementExpression(IncrementDecrementExpression ide)
        {
            string retstr = String.Empty;

            if (0 < ide.kids.Count)
            {
                IdentDotExpression dot = (IdentDotExpression) ide.kids.Top;
                retstr += Generate(String.Format("{0}", ide.PostOperation ? dot.Name + "." + dot.Member + ide.Operation : ide.Operation + dot.Name + "." + dot.Member), ide);
            }
            else
                retstr += Generate(String.Format("{0}", ide.PostOperation ? ide.Name + ide.Operation : ide.Operation + ide.Name), ide);

            return retstr;
        }

        /// <summary>
        /// Generates the code for a TypecastExpression node.
        /// </summary>
        /// <param name="te">The TypecastExpression node.</param>
        /// <returns>String containing C# code for TypecastExpression te.</returns>
        private string GenerateTypecastExpression(TypecastExpression te)
        {
            string retstr = String.Empty;

            // we wrap all typecasted statements in parentheses 
            retstr += Generate(String.Format("({0}) (", te.TypecastType), te);
            retstr += GenerateNode((SYMBOL) te.kids.Pop());
            retstr += Generate(")");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a FunctionCall node.
        /// </summary>
        /// <param name="fc">The FunctionCall node.</param>
        /// <returns>String containing C# code for FunctionCall fc.</returns>
        private string GenerateFunctionCall(FunctionCall fc)
        {
            string retstr = String.Empty;

            retstr += Generate(String.Format("{0}(", fc.Id), fc);

            foreach (SYMBOL kid in fc.kids)
                retstr += GenerateNode(kid);

            retstr += Generate(")");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a Constant node.
        /// </summary>
        /// <param name="c">The Constant node.</param>
        /// <returns>String containing C# code for Constant c.</returns>
        private string GenerateConstant(Constant c)
        {
            string retstr = String.Empty;

            // Supprt LSL's weird acceptance of floats with no trailing digits
            // after the period. Turn float x = 10.; into float x = 10.0;
            if ("LSL_Types.LSLFloat" == c.Type)
            {
                int dotIndex = c.Value.IndexOf('.') + 1;
                if (0 < dotIndex && (dotIndex == c.Value.Length || !Char.IsDigit(c.Value[dotIndex])))
                    c.Value = c.Value.Insert(dotIndex, "0");
            }

            // need to quote strings
            if ("LSL_Types.LSLString" == c.Type)
                retstr += Generate("\"");
            retstr += Generate(c.Value, c);
            if ("LSL_Types.LSLString" == c.Type)
                retstr += Generate("\"");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a VectorConstant node.
        /// </summary>
        /// <param name="vc">The VectorConstant node.</param>
        /// <returns>String containing C# code for VectorConstant vc.</returns>
        private string GenerateVectorConstant(VectorConstant vc)
        {
            string retstr = String.Empty;

            retstr += Generate(String.Format("new {0}(", vc.Type), vc);
            retstr += GenerateNode((SYMBOL) vc.kids.Pop());
            retstr += Generate(", ");
            retstr += GenerateNode((SYMBOL) vc.kids.Pop());
            retstr += Generate(", ");
            retstr += GenerateNode((SYMBOL) vc.kids.Pop());
            retstr += Generate(")");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a RotationConstant node.
        /// </summary>
        /// <param name="rc">The RotationConstant node.</param>
        /// <returns>String containing C# code for RotationConstant rc.</returns>
        private string GenerateRotationConstant(RotationConstant rc)
        {
            string retstr = String.Empty;

            retstr += Generate(String.Format("new {0}(", rc.Type), rc);
            retstr += GenerateNode((SYMBOL) rc.kids.Pop());
            retstr += Generate(", ");
            retstr += GenerateNode((SYMBOL) rc.kids.Pop());
            retstr += Generate(", ");
            retstr += GenerateNode((SYMBOL) rc.kids.Pop());
            retstr += Generate(", ");
            retstr += GenerateNode((SYMBOL) rc.kids.Pop());
            retstr += Generate(")");

            return retstr;
        }

        /// <summary>
        /// Generates the code for a ListConstant node.
        /// </summary>
        /// <param name="lc">The ListConstant node.</param>
        /// <returns>String containing C# code for ListConstant lc.</returns>
        private string GenerateListConstant(ListConstant lc)
        {
            string retstr = String.Empty;

            retstr += Generate(String.Format("new {0}(", lc.Type), lc);

            foreach (SYMBOL kid in lc.kids)
                retstr += GenerateNode(kid);

            retstr += Generate(")");

            return retstr;
        }

        /// <summary>
        /// Prints a newline.
        /// </summary>
        /// <returns>A newline.</returns>
        private string GenerateLine()
        {
            return GenerateLine("");
        }

        /// <summary>
        /// Prints text, followed by a newline.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <returns>String s followed by newline.</returns>
        private string GenerateLine(string s)
        {
            return GenerateLine(s, null);
        }

        /// <summary>
        /// Prints text, followed by a newline.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <param name="sym">Symbol being generated to extract original line
        /// number and column from.</param>
        /// <returns>String s followed by newline.</returns>
        private string GenerateLine(string s, SYMBOL sym)
        {
            string retstr = Generate(s, sym) + "\n";

            m_CSharpLine++;
            m_CSharpCol = 1;

            return retstr;
        }

        /// <summary>
        /// Prints text.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <returns>String s.</returns>
        private string Generate(string s)
        {
            return Generate(s, null);
        }

        /// <summary>
        /// Prints text.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <param name="sym">Symbol being generated to extract original line
        /// number and column from.</param>
        /// <returns>String s.</returns>
        private string Generate(string s, SYMBOL sym)
        {
            if (null != sym)
                m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));

            m_CSharpCol += s.Length;

            return s;
        }

        /// <summary>
        /// Prints text correctly indented, followed by a newline.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <returns>Properly indented string s followed by newline.</returns>
        private string GenerateIndentedLine(string s)
        {
            return GenerateIndentedLine(s, null);
        }

        /// <summary>
        /// Prints text correctly indented, followed by a newline.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <param name="sym">Symbol being generated to extract original line
        /// number and column from.</param>
        /// <returns>Properly indented string s followed by newline.</returns>
        private string GenerateIndentedLine(string s, SYMBOL sym)
        {
            string retstr = GenerateIndented(s, sym) + "\n";

            m_CSharpLine++;
            m_CSharpCol = 1;

            return retstr;
        }

        /// <summary>
        /// Prints text correctly indented.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <returns>Properly indented string s.</returns>
        //private string GenerateIndented(string s)
        //{
        //    return GenerateIndented(s, null);
        //}
        // THIS FUNCTION IS COMMENTED OUT TO SUPPRESS WARNINGS

        /// <summary>
        /// Prints text correctly indented.
        /// </summary>
        /// <param name="s">String of text to print.</param>
        /// <param name="sym">Symbol being generated to extract original line
        /// number and column from.</param>
        /// <returns>Properly indented string s.</returns>
        private string GenerateIndented(string s, SYMBOL sym)
        {
            string retstr = Indent() + s;

            if (null != sym)
                m_positionMap.Add(new KeyValuePair<int, int>(m_CSharpLine, m_CSharpCol), new KeyValuePair<int, int>(sym.Line, sym.Position));

            m_CSharpCol += s.Length;

            return retstr;
        }

        /// <summary>
        /// Prints correct indentation.
        /// </summary>
        /// <returns>Indentation based on brace count.</returns>
        private string Indent()
        {
            string retstr = String.Empty;

            for (int i = 0; i < m_braceCount; i++)
                for (int j = 0; j < m_indentWidth; j++)
                {
                     retstr += " ";
                     m_CSharpCol++;
                }

            return retstr;
        }
    }
}