using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.ApplicationModel.Resources.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using System.Collections.Specialized;
using C1.Xaml.Chart;

// The data model defined by this file serves as a representative example of a strongly-typed
// model that supports notification when members are added, removed, or modified.  The property
// names chosen coincide with data bindings in the standard item templates.
//
// Applications may use this model as a starting point and build on it, or discard it entirely and
// replace it with something appropriate to their needs.

namespace SalesDashboard.Data
{
    /// <summary>
    /// Generic item data model.
    /// </summary>
    public class SampleDataItem : SalesDashboard.Common.BindableBase
    {
        public SampleDataItem()
        {

        }

        public SampleDataItem(String uniqueId, String type, String category, String partner, Region region, DateTime saleDate, double saleAmount)
        {
            this._uniqueId = uniqueId;
            this._type = type;
            this._category = category;
            this._partner = partner;
            this._region = region;
            this._saleAmount = saleAmount;
            this._saleDate = saleDate;
        }

        private string _uniqueId = string.Empty;
        public string UniqueId
        {
            get { return this._uniqueId; }
            set { this.SetProperty(ref this._uniqueId, value); }
        }

        private string _type = string.Empty;
        public string Type
        {
            get { return this._type; }
            set { this.SetProperty(ref this._type, value); }
        }

        private string _category = string.Empty;
        public string Category
        {
            get { return this._category; }
            set { this.SetProperty(ref this._category, value); }
        }

        private string _partner = string.Empty;
        public string Partner
        {
            get { return this._partner; }
            set { this.SetProperty(ref this._partner, value); }
        }

        private Region _region = null;
        public Region Region
        {
            get { return this._region; }
            set { this.SetProperty(ref this._region, value); }
        }

        private DateTime _saleDate;
        public DateTime SaleDate
        {
            get { return this._saleDate; }
            set { this.SetProperty(ref this._saleDate, value); }
        }

        private double _saleAmount;
        public double SaleAmount
        {
            get { return this._saleAmount; }
            set { this.SetProperty(ref this._saleAmount, value); }
        }
    }

    /// <summary>
    /// Creates a collection of groups and items with hard-coded content.
    /// 
    /// SampleDataSource initializes with placeholder data rather than live production
    /// data so that sample data is provided at both design-time and run-time.
    /// </summary>
    public class SampleDataSource : SalesDashboard.Common.BindableBase
    {
        private static SampleDataSource _sampleDataSource = new SampleDataSource();
        private DateTime startDate = new DateTime(2009, 1, 1);
        private int numPoints = 730;
        private string[] types = new string[] { "Console", "Desktop", "Phone", "Tablet", "TV" };
        private string[] categories = new string[] { "Desktop", "Mobile", "Other" };
        private string[] partners = new string[] { "Company A", "Company V", "Company O", "Company P", "Company Y", "Corp A" };
        private string[] quarters = new string[] { "Q1", "Q2", "Q3", "Q4" };
        private List<Region> regions = new List<Region>();

        private ObservableCollection<SampleDataItem> _allSales = new ObservableCollection<SampleDataItem>();
        public ObservableCollection<SampleDataItem> AllSales
        {
            get { return this._allSales; }
        }

        #region Total Sales
        public double TotalSales
        {
            get
            {
                double runningTotal = 0;
                foreach(SampleDataItem item in _allSales)
                {
                    runningTotal += item.SaleAmount;
                }
                return runningTotal;
            }
        }

        public double TotalSales2009
        {
            get
            {
                double runningTotal = 0;
                foreach (SampleDataItem item in _allSales)
                {
                    if(item.SaleDate.Year == 2009)
                        runningTotal += item.SaleAmount;
                }
                return runningTotal;
            }
        }

        public double TotalSales2010
        {
            get
            {
                double runningTotal = 0;
                foreach (SampleDataItem item in _allSales)
                {
                    if (item.SaleDate.Year == 2010)
                        runningTotal += item.SaleAmount;
                }
                return runningTotal;
            }
        }
        #endregion

        #region Sales by Type
        private double _totalSalesConsole;
        public double TotalSalesConsole
        {
            get
            {
                return _totalSalesConsole;
            }
        }

        private double _totalSalesDesktop;
        public double TotalSalesDesktop
        {
            get
            {
                return _totalSalesDesktop;
            }
        }

        private double _totalSalesPhone;
        public double TotalSalesPhone
        {
            get
            {
                return _totalSalesPhone;
            }
        }

        private double _totalSalesTablet;
        public double TotalSalesTablet
        {
            get
            {
                return _totalSalesTablet;
            }
        }

        private double _totalSalesTV;
        public double TotalSalesTV
        {
            get
            {
                return _totalSalesTV;
            }
        }
        #endregion

        #region Sales by Partner
        public ObservableCollection<double> SalesByPartner
        {
            get
            {
                // group all sales by partner
                IEnumerable<IGrouping<string, SampleDataItem>> query = from sale in _allSales
                                                                       group sale by sale.Partner;
                ObservableCollection<double> values = new ObservableCollection<double>();
                double runningTotal = 0;
                foreach (var group in query)
                {
                    runningTotal = 0;
                    foreach (SampleDataItem item in group)
                    {
                        runningTotal += item.SaleAmount;
                    }
                    values.Add(runningTotal);
                }
                return values;
            }
        }

        public List<string> Partners
        {
            get
            {
                return partners.ToList<string>();
            }
        }
        #endregion

        #region Sales by Category
        public List<double> SalesByCategory
        {
            get
            {
                // group all sales by category
                IEnumerable<IGrouping<string, SampleDataItem>> query = from sale in _allSales
                                                                       group sale by sale.Category;
                List<double> values = new List<double>();
                double runningTotal = 0;
                foreach (var group in query)
                {
                    runningTotal = 0;
                    foreach (SampleDataItem item in group)
                    {
                        runningTotal += item.SaleAmount;
                    }
                    values.Add(runningTotal);
                }
                return values;
            }
        }

        public List<string> Categories
        {
            get
            {
                return categories.ToList<string>();
            }
        }
        #endregion

        #region Sales by Region
        public List<BubbleSeries> SalesByRegion
        {
            get
            {
                // group all sales by category
                IEnumerable<IGrouping<Region, SampleDataItem>> query = from sale in _allSales
                                                                       group sale by sale.Region;
                List<BubbleSeries> values = new List<BubbleSeries>();
                double runningTotal = 0;
                foreach (var region in query)
                {
                    runningTotal = 0;
                    BubbleSeries bs = new BubbleSeries();
                    bs.SizeValues = new DoubleCollection();
                    bs.Values = new DoubleCollection();
                    bs.XValues = new DoubleCollection();
                    //get lat/long for series
                    SampleDataItem firstItem = region.First<SampleDataItem>();
                    bs.XValues.Add(firstItem.Region.Longitude);
                    bs.Values.Add(firstItem.Region.Latitude);
                    bs.Label = firstItem.Region.Name;
                    // calculate total for group
                    foreach (SampleDataItem item in region)
                    {
                        runningTotal += item.SaleAmount;
                    }
                    bs.SizeValues.Add(runningTotal);
                    values.Add(bs);
                }
                return values;
            }
        }

        #endregion

        #region Sales by Quarter
        public List<DataSeries> SalesByQuarter
        {
            get
            {
                // group all sales by year
                IEnumerable<IGrouping<int, SampleDataItem>> yearGroup = from sale in _allSales
                                                                        group sale by sale.SaleDate.Year;

                List<DataSeries> dataSeriesList = new List<DataSeries>();
                
                // group each year by month
                foreach (var year in yearGroup)
                {
                    IEnumerable<IGrouping<int, SampleDataItem>> monthGroup = from sale in year
                                                                             group sale by sale.SaleDate.Month;

                    // group months into quarters
                    double runningTotal = 0;
                    DataSeries ds = new DataSeries();
                    ds.Values = new DoubleCollection();

                    //get year for the label
                    SampleDataItem firstItem = year.First<SampleDataItem>();
                    ds.Label = firstItem.SaleDate.Year.ToString();

                    int counter = 1;
                    foreach (var month in monthGroup)
                    {
                        foreach(SampleDataItem item in month)
                        {
                            runningTotal += item.SaleAmount;
                        }
                        if (counter == 3 || counter == 6 || counter == 9 || counter == 12)
                        {
                            ds.Values.Add(runningTotal);
                            runningTotal = 0;
                        }
                        counter += 1;
                    }
                    dataSeriesList.Add(ds);
                }
                return dataSeriesList;
            }
        }

        public List<string> Quarters
        {
            get
            {
                return quarters.ToList<string>();
            }
        }
        #endregion
  
        public SampleDataSource()
        {
            InitializeRegions();

            Random rnd = new Random();
            for(int i = 0; i < numPoints; i++)
            {
                _allSales.Add(new SampleDataItem(Guid.NewGuid().ToString(), 
                    types[rnd.Next(types.Length)], 
                    categories[rnd.Next(categories.Length)], 
                    partners[rnd.Next(partners.Length)], 
                    regions[rnd.Next(regions.Count)],
                    startDate.AddDays(i), 
                    rnd.Next(0, i))); 
            }

            CalculateTotalSales();
        }

        private void InitializeRegions()
        {
            regions.Add(new Region() { Name = "Spain", Latitude = 40.6986, Longitude = -3.2949 });
            regions.Add(new Region() { Name = "USA", Latitude = 40.4230, Longitude = -98.7372 });
            regions.Add(new Region() { Name = "Brazil", Latitude = -15.6778, Longitude = -47.4384 });
            regions.Add(new Region() { Name = "Uruguay", Latitude = -33.0000, Longitude = -56.0000 });
            regions.Add(new Region() { Name = "Russia", Latitude = 54.8270, Longitude = 55.0423 });
            regions.Add(new Region() { Name = "China", Latitude = 32.9043, Longitude = 110.4677 });
            regions.Add(new Region() { Name = "Japan", Latitude = 35.4112, Longitude = 135.8337 });

        }

        private void CalculateTotalSales()
        {
            
            _totalSalesConsole = 0;
            _totalSalesDesktop = 0;
            _totalSalesPhone = 0;
            _totalSalesTablet = 0;
            _totalSalesTV = 0;
            foreach (SampleDataItem item in _allSales)
            {
                if (item.Type.Equals("TV"))
                    _totalSalesTV += item.SaleAmount;
                else if (item.Type.Equals("Desktop"))
                    _totalSalesDesktop += item.SaleAmount;
                else if (item.Type.Equals("Console"))
                    _totalSalesConsole += item.SaleAmount;
                else if (item.Type.Equals("Tablet"))
                    _totalSalesTablet += item.SaleAmount;
                else if (item.Type.Equals("Phone"))
                    _totalSalesPhone += item.SaleAmount;
            }
        }
    }

    public class Region
    {
        public string Name { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }
}
