using PlantBox.Client.Models;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

using Microcharts;
using Entry = Microcharts.Entry;
using PlantBox.Client.Extensions;
using PlantBox.Client.ViewModels;
using PlantBox.Client.Resources;

namespace PlantBox.Client.Forms.Plant
{
	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class HistoricPage : ContentPage
	{
        private readonly SKColor _humidityColor;
        private readonly SKColor _humidityColorSecondary;
        private readonly SKColor _luminosityColor;
        private readonly SKColor _luminosityColorSecondary;
        private readonly SKColor _temperatureColor;
        private readonly SKColor _temperatureColorSecondary;

        public HistoricPage(PlantViewModel viewModel)
		{
			InitializeComponent();
            BindingContext = viewModel;

            // Colors
            _humidityColor = SKColor.Parse(((Color)Application.Current.Resources["HumidityColor"]).GetHexString());
            _humidityColorSecondary = SKColor.Parse(((Color)Application.Current.Resources["HumidityColorSecondary"]).GetHexString());

            _luminosityColor = SKColor.Parse(((Color)App.Current.Resources["LuminosityColor"]).GetHexString());
            _luminosityColorSecondary = SKColor.Parse(((Color)Application.Current.Resources["LuminosityColorSecondary"]).GetHexString());

            _temperatureColor = SKColor.Parse(((Color)Application.Current.Resources["TemperatureColor"]).GetHexString());
            _temperatureColorSecondary = SKColor.Parse(((Color)Application.Current.Resources["TemperatureColorSecondary"]).GetHexString());

            // Picker
            var durations = new List<Duration>()
            {
                new Duration(1, Interval.Hourly),
                new Duration(1, Interval.Daily),
                new Duration(2, Interval.Daily),
                new Duration(1, Interval.Weekly),
                new Duration(2, Interval.Monthly),
                new Duration(3, Interval.Monthly),
                new Duration(6, Interval.Monthly),
                new Duration(1, Interval.Yearly),
                new Duration(2, Interval.Yearly),
                new Duration(3, Interval.Yearly)
            };

            Picker.ItemsSource = durations;
            Picker.SelectedIndex = 0;
        }

        private void Picker_SelectedIndexChanged(object sender, EventArgs e)
        {
            DisplayCharts();
        }

        private void DisplayCharts()
        {
            var charts = new NamedChart[]
            {
                new NamedChart(CreateChart(CaptorType.Humidity, _humidityColor, _humidityColorSecondary), Locale.Humidity),
                new NamedChart(CreateChart(CaptorType.Luminosity, _luminosityColor, _luminosityColorSecondary), Locale.Luminosity),
                new NamedChart(CreateChart(CaptorType.Temperature, _temperatureColor, _temperatureColorSecondary), Locale.Temperature)
            };

            BindableLayout.SetItemsSource(layout, charts);
        }

        private Chart CreateChart(CaptorType captorType, SKColor begin, SKColor end)
        {
            var chart = new LineChart()
            {
                LabelTextSize = 14,
                Margin = 10,
                LineAreaAlpha = 50,
                LineMode = LineMode.Straight
            };
            var entries = CreateEntries(captorType, begin, end);

            chart.Entries = entries;
            return chart;
        }

        private Entry[] CreateEntries(CaptorType captorType, SKColor begin, SKColor end)
        {
            PlantViewModel viewModel = (PlantViewModel)BindingContext;
            Historic historic = viewModel.Historic;
            HistoricEntries historicEntries;
            uint multiplier = viewModel.HistoricDuration.Multiplier;
            uint count;
            float step;
            string labelFormat;
            string valueFormat;

            switch (viewModel.HistoricDuration.Interval)
            {
                case Interval.Hourly:
                    historicEntries = historic.MinutelyHistoric;
                    count = 12;
                    step = 2;
                    labelFormat = "HH:mm";
                    break;
                case Interval.Daily:
                    historicEntries = historic.HourlyHistoric;
                    count = 24;
                    step = 3;
                    labelFormat = "HH:mm";
                    break;
                case Interval.Weekly:
                    historicEntries = historic.DailyHistoric;
                    count = 7;
                    step = 1;
                    labelFormat = "ddd";
                    break;
                case Interval.Monthly:
                    historicEntries = historic.DailyHistoric;
                    count = 31;
                    step = 4;
                    labelFormat = "dd/MM";
                    break;
                case Interval.Yearly:
                    historicEntries = historic.MonthlyHistoric;
                    count = 12;
                    step = multiplier == 1 ? 1 : 1.5f;
                    labelFormat = multiplier == 1 ? "MMM" : "MM/yy";
                    break;
                default:
                    throw new InvalidOperationException();
            }

            count *= multiplier;
            step  *= multiplier;

            HistoricEntry[] values;

            switch (captorType)
            {
                case CaptorType.Humidity:
                    values = historicEntries.Humidities;
                    valueFormat = "{0:F1} %";
                    break;
                case CaptorType.Luminosity:
                    values = historicEntries.Luminosities;
                    valueFormat = "{0:F0} lux";
                    break;
                case CaptorType.Temperature:
                    values = historicEntries.Temperatures;
                    valueFormat = "{0:F1} °C";
                    break;
                default:
                    throw new InvalidOperationException();
            }

            // Take values
            values = values.Reverse().Take((int)count).ToArray();
            Entry[] entries = new Entry[values.Length / (int)step];
            SKColor[] colors = CreateGradient(begin, end, values.Length / (int)step).Reverse().ToArray();

            int index = 0;
            for (uint i = 0; index < entries.Length; i += (uint)step)
            {
                var entry = values[i];
                double value = entry.Value != 0 ? entry.Value : 0.0001;

                entries[index] = new Entry((float)value)
                {
                    Label = entry.Date.ToString(labelFormat),
                    ValueLabel = string.Format(valueFormat, value),
                    Color = colors[index]
                };

                index++;
            }

            return entries.Reverse().ToArray();
        }

        private SKColor[] CreateGradient(SKColor begin, SKColor end, int count)
        {
            var colors = new SKColor[count];

            byte currentRed = begin.Red;
            byte deltaRed = (byte)((end.Red - begin.Red) / count);

            byte currentGreen = begin.Green;
            byte deltaGreen = (byte)((end.Green - begin.Green) / count);

            byte currentBlue = begin.Blue;
            byte deltaBlue = (byte)((end.Blue - begin.Blue) / count);

            for (int i = 0; i < count; i++)
            {
                colors[i] = new SKColor(currentRed, currentGreen, currentBlue);

                currentRed += deltaRed;
                currentGreen += deltaGreen;
                currentBlue += deltaBlue;
            }

            return colors;
        }
    }
}