// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2020 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.Linq;
using System.Reflection;
using Hiperscan.Common.I18n;
using Hiperscan.Unix;

namespace Hiperscan.GtkExtensions.Widgets
{
    public class EnumComboBox<T>
    {
        public event EventHandler SelectionChanged = null;

        public EnumComboBox(Gtk.ComboBox combo_box, bool sort_items = false)
        {
            if ((this.CurrentItem is Enum) == false)
                throw new ArgumentException(string.Format("Type must be an Enum. Type was {0}", this.CurrentItem.GetType().ToString()));


            this.EnumType = this.CurrentItem.GetType();

            if (combo_box == null)
                this.GtkComboBox = new Gtk.ComboBox();
            else
                this.GtkComboBox = combo_box;

            if (this.GtkComboBox.Model == null)
            {
                this.GtkStore = new Gtk.ListStore(typeof(String));
                Gtk.CellRendererText cell_txt = new Gtk.CellRendererText();
                this.GtkComboBox.PackStart(cell_txt, false);
                this.GtkComboBox.SetAttributes(cell_txt, "text", 0);
                this.GtkComboBox.Model = this.GtkStore;
            }
            else
            {
                this.GtkStore = (Gtk.ListStore)this.GtkComboBox.Model;
            }

            this.InitI18NStrings(sort_items);
            this.InitI18NPluralStrings();
            this.FillComboBox();
            this.ChangeSelection();
            this.GtkComboBox.Changed += this.OnComboBoxChanged;
        }

        private void InitI18NStrings(bool sort_items)
        {
            List<string> values = new List<string>();

            foreach (Enum enumeration in this.EnumType.GetEnumValues())
                values.Add(enumeration.xGetI18nString());

            if (sort_items == true)
                values.Sort();

            foreach (string value in values)
                this.StringValues.Add(value.xParseToI18nEnum(this.EnumType), value);
        }

        private void InitI18NPluralStrings()
        {
            try
            {
                foreach (Enum enumeration in this.StringValues.Keys)
                    this.PluralStringValues.Add(enumeration, enumeration.xGetI18nString(true));
                    
            }
            catch(Exception)
            {
                this.PluralStringValues.Clear();
            }
        }

        private void FillComboBox()
        {
            if (this.IsPlural == true && this.PluralStringValues.Count == 0)
            {
                string message = String.Format(Catalog.GetString("Plural string isn't supported by {0}. Please use the I18NEnumGetPluralString-Attribute."),
                                               this.EnumType);

                throw new Exception(message);
            }

            this.GtkIsUpdating = true;
            this.GtkStore.Clear();

            if (this.IsPlural)
            {
                foreach (string value in this.PluralStringValues.Values)
                    this.GtkComboBox.AppendText(value);
            }
            else
            {
                foreach (string value in this.StringValues.Values)
                    this.GtkComboBox.AppendText(value);
            }

            this.GtkIsUpdating = false;
        }

        private void ChangeComboBoxEntries()
        {
            List<string> values = new List<string>(this.StringValues.Values);
            if (this.IsPlural)
                values = new List<string>(this.PluralStringValues.Values);

            for (int i = 0; i < values.Count; i++)
            {
                this.GtkStore.IterNthChild(out Gtk.TreeIter iter, i);
                this.GtkStore.SetValue(iter, 0, values[i]);
            }
        }
            
        private void OnComboBoxChanged(object sender, EventArgs e)
        {
            if (this.GtkIsUpdating == true)
                return;

            this.current_item = this.GtkComboBox.ActiveText.xParseToI18nEnum(this.EnumType);
            this.SelectionChanged?.Invoke(this, EventArgs.Empty);
        }

        private void ChangeSelection()
        {
            this.GtkIsUpdating = true;

            int index = -1;
            foreach(Enum enumeration in this.StringValues.Keys)
            {
                index++;
                if (enumeration.Equals(this.current_item))
                    break;
            }

            this.GtkComboBox.Active = index;

            this.GtkIsUpdating = false;
        }

        private bool is_plural = false;
        public bool IsPlural
        {
            private get
            {
                return this.is_plural;
            }
            set
            {
                if (this.is_plural == value)
                    return;

                this.is_plural = value;
                this.ChangeComboBoxEntries();
            }
        }

        private object current_item = default(T);
        public T CurrentItem
        {
             get
            {
                return (T) this.current_item;
            }

            set
            {
                if (((T)this.current_item).Equals(value))
                    return;

                this.current_item = value;
                this.ChangeSelection();
            }
        }

        public Gtk.ComboBox GtkComboBox { get; private set; }
        private Gtk.ListStore GtkStore { get; set; }
        private bool GtkIsUpdating { get; set; } = false;

        private Type EnumType { get; set; }
        private Dictionary<Enum, string> StringValues { get; set; } = new Dictionary<Enum, string>();
        private Dictionary<Enum, string> PluralStringValues { get; set; } = new Dictionary<Enum, string>();
    }
}
