// Created with MonoDevelop
//
//    Copyright (C) 2010 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.Drawing;
using System.Linq;

using Gtk;

using Hiperscan.NPlotEngine.Overlay;
using Hiperscan.NPlotEngine.Overlay.Annotation;
using Hiperscan.NPlotEngine.Overlay.Selection;
using Hiperscan.Spectroscopy;

using Rectangle = System.Drawing.Rectangle;


namespace Hiperscan.NPlotEngine
{

    public partial class NPlotEngine
    {
        public double XMin
        {
            get { return this.xaxis1.WorldMin; }
            set
            {
                lock (this)
                {
                    if (this.__background_data_sets.Count > 0 && this.lock_axes)
                    {
                        double rel_change = (value - this.xaxis1.WorldMin) / (this.xaxis1.WorldMax - this.xaxis1.WorldMin);
                        if (!double.IsNaN(rel_change))
                            this.xaxis2.WorldMin += (this.xaxis2.WorldMax - this.xaxis2.WorldMin) * rel_change;
                    }
                    this.xaxis1.WorldMin = value;
                    this.xaxis_changed = true;
                }
            }
        }

        public double XMax
        {
            get { return this.xaxis1.WorldMax; }
            set
            {
                lock (this)
                {
                    if (this.__background_data_sets.Count > 0 && this.lock_axes)
                    {
                        double rel_change = (value - this.xaxis1.WorldMax) / (this.xaxis1.WorldMax - this.xaxis1.WorldMin);
                        if (!double.IsNaN(rel_change))
                            this.xaxis2.WorldMax += (this.xaxis2.WorldMax - this.xaxis2.WorldMin) * rel_change;
                    }
                    this.xaxis1.WorldMax = value;
                    this.xaxis_changed = true;
                }
            }
        }

        public double YMin
        {
            get 
            {
                if (this.use_second_axes && this.__background_data_sets.Count == 0)
                    return this.yaxis2.WorldMin;
                else
                    return this.yaxis1.WorldMin; 
            }
            set
            {
                lock (this)
                {
                    if (this.__background_data_sets.Count > 0 && this.lock_axes)
                    {
                        double rel_change = (value - this.yaxis1.WorldMin) / (this.yaxis1.WorldMax - this.yaxis1.WorldMin);
                        if (double.IsNaN(rel_change) == false)
                            this.yaxis2.WorldMin += (this.yaxis2.WorldMax - this.yaxis2.WorldMin) * rel_change;
                    }

                    if (this.use_second_axes && this.__background_data_sets.Count == 0)
                        this.yaxis2.WorldMin = value;
                    else
                        this.yaxis1.WorldMin = value;

                    this.yaxis_changed = true;
                }
            }
        }

        public double YMax
        {
            get 
            {
                if (this.use_second_axes && this.__background_data_sets.Count == 0)
                    return this.yaxis2.WorldMax;
                else
                    return this.yaxis1.WorldMax; 
            }
            set
            {
                lock (this)
                {
                    if (this.__background_data_sets.Count > 0 && this.lock_axes)
                    {
                        double rel_change = (value - this.yaxis1.WorldMax) / (this.yaxis1.WorldMax - this.yaxis1.WorldMin);
                        if (double.IsNaN(rel_change) == false)
                            this.yaxis2.WorldMax += (this.yaxis2.WorldMax - this.yaxis2.WorldMin) * rel_change;
                    }

                    if (this.use_second_axes && this.__background_data_sets.Count == 0)
                        this.yaxis2.WorldMax = value;
                    else
                        this.yaxis1.WorldMax = value;

                    this.yaxis_changed = true;
                }
            }
        }

        public double GlobalXMin { get; private set; }
        public double GlobalXMax { get; private set; }
        public double GlobalYMin { get; private set; }
        public double GlobalYMax { get; private set; }

        public double ExtendedGlobalXMin
        {
            get
            {
                double x_min = this.GlobalXMin - (this.GlobalXMax - this.GlobalXMin) * this.XExtendFactor;
                double x_max = this.GlobalXMax + (this.GlobalXMax - this.GlobalXMin) * this.XExtendFactor;
                if (x_max-x_min < this.x_min_range)
                    x_min -= this.x_min_range/2.0;
                return x_min;
            }
        }
        
        public double ExtendedGlobalXMax
        {
            get
            {
                double x_min = this.GlobalXMin - (this.GlobalXMax - this.GlobalXMin) * this.XExtendFactor;
                double x_max = this.GlobalXMax + (this.GlobalXMax - this.GlobalXMin) * this.XExtendFactor;
                if (x_max-x_min < this.x_min_range)
                    x_max += this.x_min_range/2.0;
                return x_max;
            }
        }

        public double ExtendedGlobalYMin
        {
            get
            {
                double y_min = this.GlobalYMin - (this.GlobalYMax - this.GlobalYMin) * this.YExtendFactor;
                double y_max = this.GlobalYMax + (this.GlobalYMax-this.GlobalYMin) * this.YExtendFactor;
                if (y_max-y_min < this.YMinRange)
                    y_min -= this.YMinRange / 2.0;
                return y_min;
            }
        }
        
        public double ExtendedGlobalYMax
        {
            get
            {
                double y_min = this.GlobalYMin - (this.GlobalYMax -this.GlobalYMin) * this.YExtendFactor;
                double y_max = this.GlobalYMax + (this.GlobalYMax - this.GlobalYMin) * this.YExtendFactor;
                if (y_max-y_min < this.YMinRange)
                    y_max += this.YMinRange / 2.0;
                return y_max;
            }
        }

        public string XLabel
        {
            get 
            { 
                if (this.use_second_axes)
                    return this.xaxis2.Label;
                else
                    return this.xaxis1.Label;  
            }
            set    
            { 
                lock (this)
                {
                    if (this.use_second_axes)
                        this.xaxis2.Label = value;
                    else
                        this.xaxis1.Label = value; 

                    this.xaxis_changed = true;
                }
            }
        }

        public string YLabel
        {
            get 
            { 
                if (this.use_second_axes)
                    return this.yaxis2.Label;
                else
                    return this.yaxis1.Label;  
            }
            set    
            { 
                lock (this)
                {
                    if (this.use_second_axes)
                        this.yaxis2.Label = value;
                    else
                        this.yaxis1.Label = value; 

                    this.yaxis_changed = true;
                }
            }
        }

        public bool IsEmpty
        {
            get
            { 
                lock (this)
                    return (this.__data_sets.Any() == false);
            }
        }
        
        public bool IsValid
        {
            get
            {
                lock (this)
                    return (double.IsNaN(this.XMin)      == false &&
                            double.IsNaN(this.XMax)      == false &&
                            double.IsNaN(this.YMin)      == false &&
                            double.IsNaN(this.YMax)      == false &&
                            double.IsInfinity(this.XMin) == false &&
                            double.IsInfinity(this.XMax) == false &&
                            double.IsInfinity(this.YMin) == false &&
                            double.IsInfinity(this.YMax) == false);
            }
        }

        public float DefaultLineWidth { get; set; } = 2f;
        public double XExtendFactor   { get; set; } = 0.0;
        public double YExtendFactor   { get; set; } = 0.02;
        public double YMinRange       { get; set; } = 0.001;

        public int PhysicalXMin
        {
            get { return this.plotsurface.PhysicalXAxis1Cache.PhysicalMin.X; }
        }
        
        public int PhysicalXMax
        {
            get { return this.plotsurface.PhysicalXAxis1Cache.PhysicalMax.X; }
        }
        
        public int PhysicalYMin
        {
            get { return this.plotsurface.PhysicalYAxis1Cache.PhysicalMin.Y; }
        }
        
        public int PhysicalYMax
        {
            get { return this.plotsurface.PhysicalYAxis1Cache.PhysicalMax.Y; }
        }

        public RubberBandSelection Selection
        {
            get { return this.selection;  }
            set 
            { 
                if (this.disable_selections == false)
                    this.selection = value; 
            }
        }
        
        public HorizontalSelection HSelection
        {
            get { return this.horizontal_selection;  }
            set
            { 
                if (this.disable_selections == false)
                    this.horizontal_selection = value; 
            }
        }
        
        public VerticalSelection VSelection
        {
            get { return this.vertical_selection;  }
            set 
            { 
                if (this.disable_selections == false)
                    this.vertical_selection = value; 
            }
        }
        
        public List<CrossHair> Markers
        {
            get 
            {
                lock (this.markers)
                    return this.markers;  
            }
        }

        public List<Annotation> Annotations
        {
            get 
            { 
                lock (this.annotations)
                    return this.annotations;
            }
        }
        
        public List<DataSetSelection> DataSetSelections
        {
            get 
            { 
                // DrawOverlays may block the data set selections
                lock (this.data_set_selections)
                    return this.data_set_selections;
            }
        }

        public Rectangle Bounds
        {
            get { return new System.Drawing.Rectangle(0, 0, this.plotsurface.Width, this.plotsurface.Height); }
        }
        
        public Gdk.Color BackgroundColor
        {
            get { return this.plotsurface.BackgroundColor;  }
            set { this.plotsurface.BackgroundColor = value; }
        }
        
        public Color PlotBackColor
        {
            set { this.plotsurface.PlotBackColor = value; }
        }
        
        public string Title
        {
            get { return this.plotsurface.Title;  }
            set { this.plotsurface.Title = value; }
        }
        
        public Dictionary<string,DataSet> DataSets
        {
            get
            {
                lock (this)
                    return this.__data_sets;
            }
            set
            {
                lock (this)
                {
                    this.__data_sets = value;
                    this.data_sets_changed = true;
                }
            }
        }

        public Widget GtkWidget
        {
            get { return this.plotsurface; }
        }
        
        public bool IsDragging
        {
            get { return this.is_dragging; }
            set 
            {
                this.is_dragging = value;
                
                if (this.is_dragging)
                    this.GtkWidget.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Fleur);
                else
                    this.GtkWidget.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Tcross);
            }
        }
        
        public bool DisableSelections
        {
            get { return this.disable_selections;  }
            set 
            {
                this.disable_selections = value;
                if (value)
                {
                    this.selection = null;
                    this.vertical_selection   = null;
                    this.horizontal_selection = null;
                }
            }
        }
        
        public double EstimatedLegendHeight
        {
            get
            { 
                if (this.legend == null)
                    return 0.0;

                return this.plotsurface.EstimatedLegendHeight(this.legend.Font); 
            }
        }
        
        public int Width
        {
            get { return this.plotsurface.Width; }
        }
        
        public int Height
        {
            get { return this.plotsurface.Height; }
        }
        
        public bool LegendVisible
        {
            get { return this.legend_visible;  }
            set 
            { 
                bool need_update = (value != this.legend_visible);
                if (value)
                    this.plotsurface.Legend = this.legend;
                else
                    this.plotsurface.Legend = null;
                this.legend_visible = value; 
                if (need_update)
                    this.refresh_is_pending = true;
                this.plotsurface.CheckLegendSize();
            }
        }
        
        public int PlotTypeId
        {
            get { return this.plot_type_id;  }
            set { this.plot_type_id = value; }
        }
        
        public bool LockAxes
        {
            get { return this.lock_axes;  }
            set { this.lock_axes = value; }
        }
        
        public bool UseSecondAxes
        {
            get { return this.use_second_axes;  }
            set { this.use_second_axes = value; }
        }
        
        public Gdk.Pixbuf Thumbnail
        {
            get
            { 
                lock (this)
                    return this.__thumbnail_pixbuf;
            }
        }
        
        public bool ReverseXAxes
        {
            get { return this.reverse_x_axes;  }
            set 
            { 
                if (this.reverse_x_axes != value)
                {
                    this.reverse_x_axes = value;
                    this.xaxis_changed = true;

                    this.XAxesReversed?.Invoke(this, EventArgs.Empty);
                }
            }
        }
        
        public bool HasBackgroundPlot
        {
            get { return this.__background_data_sets != null && this.__background_data_sets.Count > 0; }
        }
    }
}