/// <summary>
 /// Creates a parser to parse configuration path.
 /// </summary>
 /// <param name="configPath">The file to parse</param>
 public ServerConfigurationParser(string configPath)
 {
     this.configPath = configPath;
     DefinedSections["__global__"]    = new ConfigSection();
     DefinedConstants["FHTTPVERSION"] = new ConfigVariable()
     {
         DataType = ConfigVariableDataType.Integer, Name = "FHTTPVERSION", Value = 1
     };
 }
        /// <summary>
        /// Checks if a given variable is given a null value.
        /// </summary>
        /// <param name="path">The path to use.</param>
        /// <returns></returns>
        public bool IsNull(string path)
        {
            ConfigVariable v = GetVariable(path);

            if ((v == null) || (v.DataType != ConfigVariableDataType.Null))
            {
                return(false);
            }
            return(v.Value.GetType().IsAssignableFrom(typeof(ConfigNullValue)));
        }
        /// <summary>
        /// Returns a boolean from the specified path. Default value is false if not found.
        /// </summary>
        /// <param name="path">The path to use.</param>
        /// <returns></returns>
        public bool GetBool(string path)
        {
            ConfigVariable v = GetVariable(path);

            if ((v == null) || (v.DataType != ConfigVariableDataType.Boolean))
            {
                return(false);
            }
            return((bool)v.Value);
        }
        /// <summary>
        /// Returns a string from the specified path.
        /// </summary>
        /// <param name="path">The path to use.</param>
        /// <returns></returns>
        public string GetString(string path)
        {
            ConfigVariable v = GetVariable(path);

            if ((v == null) || (v.DataType != ConfigVariableDataType.String))
            {
                return("");
            }
            return((string)v.Value);
        }
        /// <summary>
        /// Returns an integer from the specified path.
        /// </summary>
        /// <param name="path">The path to use.</param>
        /// <returns></returns>
        public int GetInteger(string path)
        {
            ConfigVariable v = GetVariable(path);

            if ((v == null) || (v.DataType != ConfigVariableDataType.Integer))
            {
                return(-1);
            }
            return((int)v.Value);
        }
 /// <summary>
 /// Defines a constant variable.
 /// </summary>
 /// <param name="name">The name of the constant to define</param>
 /// <param name="dataType">The data type to use</param>
 /// <param name="value">The value of the constant</param>
 /// <exception cref="ConstantAlreadyDefinedException"></exception>
 public void DefineConstant(string name, ConfigVariableDataType dataType, object value, int trackingLineNumber = -1)
 {
     CheckIdentifier(name, trackingLineNumber);
     if (DefinedConstants.ContainsKey(name))
     {
         throw new ConstantAlreadyDefinedException(name, trackingLineNumber);
     }
     DefinedConstants[name] = new ConfigVariable()
     {
         Name     = name,
         DataType = dataType,
         Value    = value
     };
 }
 /// <summary>
 /// Adds a variable to the specified section.
 /// </summary>
 /// <param name="variable">The variable object to add.</param>
 /// <param name="section">The section to add the variable to (leave empty for global section).</param>
 /// <exception cref="SectionNotFoundException"></exception>
 /// <exception cref="InvalidIdentifierNameException"></exception>
 public void AddVariable(ConfigVariable variable, string section = "")
 {
     CheckIdentifier(variable.Name);
     if (section == "")
     {
         DefinedSections["__global__"].Variables[variable.Name] = variable;
     }
     else
     {
         if (!DefinedSections.ContainsKey("__global__." + section))
         {
             throw new SectionNotFoundException(section);
         }
         DefinedSections["__global__." + section].Variables[variable.Name] = variable;
     }
 }
        /// <summary>
        /// Defines a variable with the specified name and value. If the variable already exists, it will be overwritten.
        /// </summary>
        /// <param name="name">The name of the variable.</param>
        /// <param name="value">The value of the variable. Only primitive types are supported, others are converted using ToString().</param>
        /// <param name="section">The section to place the variable in. Must be a valid section or a SectionNotFoundException will be thrown.</param>
        /// <exception cref="SectionNotFoundException"></exception>
        public void DefineVariable(string name, object value, string section = "")
        {
            ConfigVariable cv = new ConfigVariable()
            {
                Name = name
            };
            var valueType = value.GetType();

            if (valueType.IsAssignableFrom(typeof(string)))
            {
                cv.DataType = ConfigVariableDataType.String;
            }
            else if (valueType.IsAssignableFrom(typeof(int)))
            {
                cv.DataType = ConfigVariableDataType.Integer;
            }
            else if (valueType.IsAssignableFrom(typeof(bool)))
            {
                cv.DataType = ConfigVariableDataType.Boolean;
            }
            else if (valueType == null)
            {
                cv.DataType = ConfigVariableDataType.Null;
            }
            else
            {
                cv.DataType = ConfigVariableDataType.Unknown;
            }
            switch (cv.DataType)
            {
            default:
                cv.Value = value;
                break;

            case ConfigVariableDataType.Null:
                cv.Value = new ConfigNullValue();
                break;

            case ConfigVariableDataType.Unknown:
                cv.DataType = ConfigVariableDataType.String;
                cv.Value    = value.ToString();
                break;
            }
            AddVariable(cv, section);
        }
        /// <summary>
        /// Parse and evaluate text.
        /// </summary>
        /// <param name="data">The lines to parse</param>
        /// <param name="ctx">The context to run under (global is __global__)</param>
        private void _Parse(string[] data, string ctx = "__global__")
        {
            foreach (var rl in data)
            {
                lineNumber++;
                var ln = rl.Trim();
                if (ln == "")
                {
                    continue;
                }
                else if (ln.StartsWith("//"))
                {
                    continue;
                }
                foreach (var c in DefinedConstants)
                {
                    if (currentReadMode != ConfigReadMode.MultiLineComment)
                    {
                        ln = ln.Replace("$" + c.Key + "$", c.Value.Value.ToString());
                    }
                }
                foreach (var c in DefinedSections[ctx].Variables)
                {
                    if (currentReadMode != ConfigReadMode.MultiLineComment)
                    {
                        ln = ln.Replace("$" + c.Key + "$", c.Value.Value.ToString());
                    }
                }
                switch (currentReadMode)
                {
                default:
                    //What read mode is this?!
                    break;

                case ConfigReadMode.Normal:
                    if (ln.StartsWith("/*"))
                    {
                        currentReadMode = ConfigReadMode.MultiLineComment;
                        continue;
                    }
                    else if (ln.StartsWith("const "))
                    {
                        // DEBUG only Console.WriteLine("Found const, lets parse it");
                        var v = ParseVar(ln.Substring(6).Trim());
                        DefineConstant(v.Name, v.DataType, v.Value, lineNumber);
                    }
                    else if (ln.StartsWith("section "))
                    {
                        string sname = ln.Split(' ')[1].Trim();
                        if (sname.Contains("."))
                        {
                            throw new InvalidIdentifierNameException(lineNumber, sname);
                        }
                        currentReadMode = ConfigReadMode.SectionDef;
                        currentSName    = sname;
                        blockDepth      = 1;
                        continue;
                    }
                    else if (ln.StartsWith("include "))
                    {
                        if (!ln.Contains("\""))
                        {
                            continue;
                        }
                        string fileName = ln.Split('"')[1].Trim();
                        if (!Path.IsPathRooted(fileName))
                        {
                            fileName = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(configPath)), fileName);
                        }
                        if (!File.Exists(fileName))
                        {
                            throw new IncludeNotFoundException(fileName, lineNumber);
                        }
                        var p = new ServerConfigurationParser(fileName);
                        p.DefinedConstants = DefinedConstants;
                        p.DefinedSections  = DefinedSections;
                        p.Parse(ctx);
                    }
                    else if (ln.Contains(" "))      //Probably a property
                    {
                        ConfigVariable var = ParseVar(ln.Trim());
                        DefinedSections[ctx].Variables[var.Name] = var;
                    }
                    break;

                case ConfigReadMode.SectionDef:
                    if (ln.EndsWith("{"))
                    {
                        blockDepth++;
                    }
                    else if (ln.EndsWith("}"))
                    {
                        blockDepth--;
                    }
                    if (blockDepth == 0)    //Finished searching section
                    {
                        var ctxedName = ctx + "." + currentSName;
                        DefinedSections[ctxedName] = new ConfigSection();
                        currentReadMode            = ConfigReadMode.Normal;
                        var p = new ServerConfigurationParser(null);
                        p.DefinedConstants = DefinedConstants;
                        p.DefinedSections  = DefinedSections;
                        p.lineNumber       = lineNumber;
                        p.configPath       = configPath;
                        p._Parse(currentSectionBody.ToArray(), ctxedName);
                        currentSName = "";
                        currentSectionBody.Clear();
                        continue;
                    }
                    currentSectionBody.Add(ln);
                    break;

                case ConfigReadMode.MultiLineComment:
                    if (ln.EndsWith("*/"))
                    {
                        currentReadMode = ConfigReadMode.Normal;
                        continue;
                    }
                    break;
                }
            }
        }