// CurrentSpectra.cs created with MonoDevelop
// User: klose at 15:55 02.07.2010
// CVS release: $Id: CurrentSpectra.cs,v 1.4 2011-04-19 12:36:57 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  Thomas Klose, 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.Collections.Generic;
using System.Drawing;

using Hiperscan.NPlotEngine;
using Hiperscan.Spectroscopy;


namespace Hiperscan.QuickStep.Figure
{

    public class CurrentSpectra : Hiperscan.Extensions.ICurrentSpectra
    {
        private readonly List<Dictionary<string,Spectrum>> spectra_history;

        private int current_entry;
        
        private const int max_history_count = 30;
        
        public event Extensions.CurrentSpectraChangedHandler Changed;
        
        public delegate void LastEntryReachedHandler(CurrentSpectra current_spectra);
        public event         LastEntryReachedHandler LastEntryReached;
        
        public delegate void FirstEntryReachedHandler(CurrentSpectra current_spectra);
        public event         FirstEntryReachedHandler FirstEntryReached;
        
        public delegate void UndoPossibleHandler(CurrentSpectra current_spectra);
        public event         UndoPossibleHandler UndoPossible;
        
        public delegate void RedoPossibleHandler(CurrentSpectra current_spectra);
        public event         RedoPossibleHandler RedoPossible;
        
        
        public CurrentSpectra(PlotInfo plot_info)
        {
            this.PlotInfo = plot_info;
            this.spectra_history = new List<Dictionary<string, Spectrum>>
            {
                new Dictionary<string, Spectrum>()
            };
            this.current_entry = 0;
        }
        
        public CurrentSpectra(CurrentSpectra current)
        {
            this.PlotInfo = current.PlotInfo;
            
            lock (current)
            {
                this.spectra_history = new List<Dictionary<string,Spectrum>>();
                
                foreach (var spectra in current.HistoryEntries)
                {
                    this.spectra_history.Add(new Dictionary<string,Spectrum>(spectra));
                }

                this.current_entry = current.CurrentHistoryEntry;
            }
        }
        
        public void NotifyChanges()
        {
            lock (this)
            {
                if (this.spectra_history.Count == 1)
                {
                    this.FirstEntryReached?.Invoke(this);
                    this.LastEntryReached?.Invoke(this);
                    this.Changed?.Invoke(this, false);
                    return;
                }
                
                if (this.current_entry == this.spectra_history.Count-1 &&
                    this.LastEntryReached != null)
                {
                    this.LastEntryReached(this);
                }
                else
                {
                    this.RedoPossible?.Invoke(this);
                }

                if (this.current_entry ==  0 && this.FirstEntryReached != null)
                    this.FirstEntryReached(this);
                else
                    this.UndoPossible?.Invoke(this);

                this.Changed?.Invoke(this, false);
            }
        }
        
        public void Add(string id, Spectrum spectrum, bool notify)
        {
            lock (this)
            {
                this.SetAttributes(spectrum);
                this.spectra_history[this.current_entry].Add(id, spectrum);
                
                if (notify)
                    this.NotifyChanges();
            }
        }
        
        public bool Contains(string id)
        {
            lock (this)
                return this.spectra_history[this.current_entry].ContainsKey(id);
        }
        
        public void Remove(string id, bool notify)
        {
            lock (this)
            {
                this.spectra_history[this.current_entry].Remove(id);

                if (notify)
                    this.NotifyChanges();
            }
        }
        
        public void Clear()
        {
            lock (this)
            {
                this.spectra_history[this.current_entry].Clear();
                this.NotifyChanges();
            }
        }
        
        public Spectrum GetByLabel(string label)
        {
            lock (this)
            {
                foreach (var spectrum in this.spectra_history[this.current_entry].Values)
                {
                    if (spectrum.Label == label)
                        return spectrum;
                }
                return null;
            }
        }
        
        public int Count
        {
            get
            { 
                lock (this)
                    return this.spectra_history[this.current_entry].Count;
            }
        }
        
        public int CurrentHistoryEntry
        {
            get
            {
                lock (this)
                    return this.current_entry;
            }
        }
        
        public Dictionary<string,Spectrum>.ValueCollection Values
        {
            get
            {
                lock (this)
                    return this.spectra_history[this.current_entry].Values;
            }
        }
        
        public List<Dictionary<string,Spectrum>> HistoryEntries
        {
            get
            {
                lock (this)
                    return this.spectra_history;
            }
        }
        
        public Dictionary<string,Spectrum>.KeyCollection Keys
        {
            get
            {
                lock (this)
                    return this.spectra_history[this.current_entry].Keys;
            }
        }

        public List<string> ZOrderKeys
        {
            get
            {
                var z_order = new List<KeyValuePair<string,int>>();

                lock (this)
                {
                    foreach (Spectrum spectrum in this.Values)
                    {
                        z_order.Add(new KeyValuePair<string,int>(spectrum.Id, spectrum.ZOrder));
                    }
                    z_order.Sort((lhs, rhs) => lhs.Value.CompareTo(rhs.Value));

                    var keys = new List<string>();
                    foreach (KeyValuePair<string,int> kv in z_order)
                    {
                        keys.Add(kv.Key);
                    }

                    return keys;
                }
            }
        }
        
        public bool ContainsLabel(string label)
        {
            lock (this)
            {
                bool found = false;
                
                foreach (var spectrum in this.spectra_history[this.current_entry].Values)
                {
                    if (spectrum.Label == label)
                    {
                        found = true;
                        break;
                    }
                }
                return found;
            }
        }
        
        private void SetAttributes(Spectrum spectrum)
        {
            if (spectrum.SpectrumType == SpectrumType.Preview)
            {
                spectrum.Color = Color.Aqua;
            }
            else
            {
                ColorPicker.Clear();
                lock (this)
                {
                    foreach (Spectrum s in this.Values)
                    {
                        if (s.Color != Color.Empty)
                            ColorPicker.Existing(s.Color);
                        
                        if (s.Id == spectrum.Id)
                        {
                            bool sav_default = spectrum.DefaultStyle;
                            spectrum.Color = s.Color;
                            spectrum.LineStyle = s.LineStyle;
                            spectrum.LineWidth = s.LineWidth;
                            spectrum.DefaultStyle = sav_default;
                            return;
                        }
                    }
                }
                if (spectrum.Color == Color.Empty)
                {
                    bool sav_default = spectrum.DefaultStyle;
                    spectrum.Color = ColorPicker.New();
                    spectrum.DefaultStyle = sav_default;
                }
            }
        }                
        
        public Spectrum GetById(string id)
        {
            return this[id];
        }
        
        public Spectrum this[string id]
        {
            get
            {
                lock (this)
                    return this.spectra_history[this.current_entry][id];
            }
            set
            { 
                lock (this)
                {
                    this.SetAttributes(value);
                    value.ZOrder = this.spectra_history[this.current_entry][id].ZOrder;
                    this.spectra_history[this.current_entry][id] = value;
                }
            }
        }
        
        public void AddHistoryEntry()
        {
            lock (this)
            {
                if (this.current_entry < this.spectra_history.Count-1)
                {
                    int count = this.spectra_history.Count - this.current_entry - 1;
                    this.spectra_history.RemoveRange(this.current_entry+1, count);
                }
                
                if (this.spectra_history.Count == max_history_count)
                {
                    this.spectra_history.RemoveAt(0);
                    --this.current_entry;
                }

                this.spectra_history.Add(new Dictionary<string,Spectrum>(this.spectra_history[this.current_entry]));
                
                Dictionary<string,Spectrum> prev_entry = this.spectra_history[this.spectra_history.Count-2];
                if (prev_entry.ContainsKey("9_preview"))
                    prev_entry.Remove("9_preview");
                
                ++this.current_entry;
            }
        }
        
        public void Undo()
        {
            lock (this)
            {
                if (this.current_entry > 0)
                {
                    if (this.spectra_history[this.current_entry].ContainsKey("9_preview"))
                        this.spectra_history[this.current_entry].Remove("9_preview");
                    --this.current_entry;
                }
                
                this.NotifyChanges();
            }
        }
        
        public void Redo()
        {
            lock (this)
            {
                if (this.current_entry < this.spectra_history.Count-1)
                    ++this.current_entry;
                
                this.NotifyChanges();
            }
        }

        public override int GetHashCode ()
        {
            int hash = 0;

            foreach (Spectrum spectrum in this.Values)
            {
                hash ^= spectrum.GetHashCode();
            }

            return hash;
        }

        public bool UndoIsPossible => this.current_entry != 0;
        public bool RedoIsPossible => this.current_entry != this.spectra_history.Count - 1;

        public PlotInfo PlotInfo { get; }
    }
}