// Created with MonoDevelop
//
//    Copyright (C) 2018 Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.



using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using Hiperscan.Unix;

namespace Hiperscan.Common
{
    public class OptionSection : Section
    {
        public OptionSection(Parser parser) : base()
        {
            this.SearchArguments(parser);
            this.CheckArguments();
            this.Description = Catalog.GetString("Options:");
        }

        public void Parse(List<PredefinedOption> args)
        {
            this.Parameters.Sort(new PriorityComparer());
            foreach (IOption item in this.Parameters)
                item.Parse(args);
        }

        public override void ShowMenu()
        {
            base.ShowMenu();

            if (String.IsNullOrEmpty(this.Description) == false)
                Console.WriteLine(this.Description + "\n");
  
            this.Parameters.Sort(new MenuComparer());
            foreach (IOption menu in this.Parameters)
                menu.WriteMenu();

            Console.WriteLine();
            Console.WriteLine();
        }

        protected override bool IsEmpty
        {
            get
            {
                return base.IsEmpty && this.Parameters.Count == 0;
            }
        }

        protected void CheckArguments()
        {
            this.CheckSetNames();
            this.CheckLongNames();
            this.CheckShortNames();
            this.CheckUniqueNames();
            this.CheckParamerName();
        }

        private void CheckSetNames()
        {
            foreach (IOption item in this.Parameters)
            {
                if (String.IsNullOrEmpty(item.ShortName) == true &&
                    String.IsNullOrEmpty(item.LongName) == true)
                    throw new Exception(String.Format(Catalog.GetString("For property {0}: 'ShortName' and 'LongName' are not set"),
                                                      item.Identifier));
            }
        }

        private void CheckShortNames()
        {
            Regex short_regex = new Regex(@"^-([a-zA-Z0-9]|\?){1}$");
            foreach (IOption item in this.Parameters)
            {
                if (String.IsNullOrEmpty(item.ShortName) == false)
                {
                    if (short_regex.Match(item.ShortName).Success == false)
                    {
                        throw new Exception(String.Format(Catalog.GetString("For property {0}: 'ShortName' has invalid format"),
                                                          item.Identifier));
                    }
                }
            }
        }

        private void CheckLongNames()
        {
            Regex long_regex = new Regex(@"^--[a-z0-9]+(-[a-z0-9]+)*$");
            foreach (IOption item in this.Parameters)
            {
                if (String.IsNullOrEmpty(item.LongName) == false)
                {
                    if (long_regex.Match(item.LongName).Success == false)
                    {
                        throw new Exception(String.Format(Catalog.GetString("For property {0}: 'LongName' has invalid format"),
                                                          item.Identifier));
                    }
                }
            }
        }

        private void CheckUniqueNames()
        {
            Dictionary<string, string> read_parameter_names = new Dictionary<string, string>();
            foreach (IOption item in this.Parameters)
            {
                if (String.IsNullOrEmpty(item.ShortName) == false)
                {
                    if (read_parameter_names.ContainsKey(item.ShortName) == false)
                    {
                        read_parameter_names.Add(item.ShortName, item.Identifier);
                    }
                    else
                    {
                        throw new Exception(String.Format(Catalog.GetString("For property {0}: 'ShortName' is already used by property {1}"),
                                                          item.Identifier,
                                                          read_parameter_names[item.ShortName]));
                    }
                }

                if (String.IsNullOrEmpty(item.LongName) == false)
                {
                    if (read_parameter_names.ContainsKey(item.LongName) == false)
                    {
                        read_parameter_names.Add(item.LongName, item.Identifier);
                    }
                    else
                    {
                        throw new Exception(String.Format(Catalog.GetString("For property {0}: 'LongName' is already used by property {1}"),
                                                          item.Identifier,
                                                          read_parameter_names[item.LongName]));
                    }
                }
            }
        }

        private void CheckParamerName()
        {
            Regex paramter_regex = new Regex(@"^[A-Z]+.*$");
            foreach (IOption item in this.Parameters)
            {
                if (item.HasParameter == false)
                    continue;

                if (String.IsNullOrEmpty(item.ParameterName) == true)
                    throw new Exception(String.Format(Catalog.GetString("For property {0}: 'ParameterName' is not set"),
                                                      item.Identifier));

                if (paramter_regex.Match(item.ParameterName).Success == false)
                {
                    throw new Exception(String.Format(Catalog.GetString("For property {0}: 'ParameterName' has invalid format"),
                                                      item.Identifier));
                }
            }
        }

        private void CheckExplanations()
        {
            foreach (IOption item in this.Parameters)
            {
                if (String.IsNullOrEmpty(item.Explanation) == true)
                    throw new Exception(String.Format(Catalog.GetString("For property {0}: 'Explanation' is not set"),
                                                      item.Identifier));
            }
        }

        private void SearchArguments(Parser parser)
        {
            if (this.Parameters == null)
            {
                this.Parameters  = new List<IOption>();

                PropertyInfo[] properties = parser.GetType().GetProperties(BindingFlags.Public | 
                                                                         BindingFlags.NonPublic |
                                                                            BindingFlags.Instance);
                foreach (PropertyInfo property in properties)
                {
                    if (property.PropertyType.GetInterface(typeof(IOption).ToString()) == null)
                        continue;

                    IOption item = (IOption)property.GetValue(parser, null);
                    if (item == null)
                        throw new NullReferenceException(String.Format(Catalog.GetString("Argument {0} is not set"), property.Name));

                    if (item.Disabled == true)
                        continue;

                    item.Identifier = property.Name;
                    this.Parameters.Add(item);
                }
            }
        }

        public List<IOption> Parameters {get; private set;}
    }
}
