using System;
using System.Windows;
using System.Reflection;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Tasks;
using ESRI.ArcGIS.Client.Geometry;

namespace GeoDashboard
{
    /// <summary>
    /// Represents a demographic information source available as an ESRI service.
    /// </summary>
    public class InfoSource : BaseObject
    {
        const string URL_FORMAT = "http://services.arcgisonline.com:80/ArcGIS/rest/services/Demographics/{0}/MapServer";

        /// <summary>
        /// Initializes a new instance of an <see cref="InfoSource"/>.
        /// </summary>
        public InfoSource(ViewModel vm, string name, string shortUrl)
        {
            ViewModel = vm;
            Name = name;
            ShortUrl = shortUrl;
            Dictionary = new Dictionary<string, InfoValue>();
            List = new List<InfoValue>();
        }

        /// <summary>
        /// Gets or sets the ViewModel that owns this <see cref="InfoSource"/>
        /// </summary>
        public ViewModel ViewModel { get; set; }
        /// <summary>
        /// Gets or sets the name of this <see cref="InfoSource"/>.
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Gets the base URL for this <see cref="InfoSource"/>.
        /// </summary>
        public string ShortUrl{ get; set; }
        /// <summary>
        /// Gets the url used to retrieve data for this item.
        /// </summary>
        public string GetTileUrl()
        {
            return string.Format(URL_FORMAT, ShortUrl);
        }
        /// <summary>
        /// Gets the Url used to perform queries for this item.
        /// </summary>
        public string GetQueryUrl()
        {
            var urlTiles = GetTileUrl();
            return string.Format("{0}/{1}", urlTiles, (int)ViewModel.InfoScale);
        }
        /// <summary>
        /// Gets a value that indicates this source has listeners and should be updated.
        /// </summary>
        public bool HasListeners { get; set; }
        /// <summary>
        /// Gets a dictionary with detail information for this <see cref="InfoSource"/>.
        /// </summary>
        public Dictionary<string, InfoValue> Dictionary { get; set; }
        /// <summary>
        /// Gets a list with detailed histogram information for this <see cref="InfoSource"/>.
        /// </summary>
        public IList<InfoValue> List { get; set; }
        /// <summary>
        /// Gets a list with summary histogram information for this <see cref="InfoSource"/>.
        /// </summary>
        public IList<InfoValue> ShortList { get; set; }
        /// <summary>
        /// Perform a new query based on the current extent and update all values in the Dictionary.
        /// </summary>
        public void UpdateValues()
        {
            // create query
            var query = new Query();

            // specify the fields we want
            foreach (var iv in Dictionary)
            {
                query.OutFields.Add(iv.Key);
            }

            // specify the geometry to use for this spatial query 
            // (instead of a WHERE clause)
            query.Geometry = ViewModel.Extent.GetCenter();

            // execute query
            var url = GetQueryUrl();
            var queryTask = new QueryTask(url);
            queryTask.ExecuteCompleted += (s, e) =>
            {
                if (e.FeatureSet != null &&
                    e.FeatureSet.Features.Count > 0 &&
                    e.FeatureSet.Features[0].Attributes.Count > 0)
                {
                    var atts = e.FeatureSet.Features[0].Attributes;
                    ViewModel.SelectedLocation = string.Format("{0}, {1}", atts["NAME"], atts["ST_ABBREV"]);
                    foreach (var kv in atts)
                    {
                        // get information name, key, and value
                        var iv = Dictionary[kv.Key];
                        var value = kv.Value;

                        // convert value to numeric if possible
                        double dbl;
                        if (double.TryParse(value.ToString(), out dbl))
                        {
                            value = dbl;
                        }

                        // save result
                        iv.Value = value;
                    }

                    // update summary info
                    if (ShortList != null)
                    {
                        foreach (InfoValue iv in ShortList)
                        {
                            double value = 0;
                            foreach (var key in iv.Key.Split(' '))
                            {
                                if (Dictionary[key].Value is double)
                                {
                                    value += (double)Dictionary[key].Value;
                                }
                            }
                            iv.Value = value;
                        }
                    }

                    // update Percent, PercentMax on lists
                    UpdatePercent(List);
                    UpdatePercent(ShortList);
                }
                else
                {
                    // no data available: clear values
                    ViewModel.SelectedLocation = null;
                    foreach (var iv in Dictionary.Values)
                    {
                        iv.Value = null;
                    }
                }
            };
            queryTask.Failed += (s, e) =>
            {
                // no data available? clear values...
                MessageBox.Show(e.Error.Message);
            };

            // start query
            queryTask.ExecuteAsync(query);
        }

        // update Percent and PercentMax values for variables on a list
        void UpdatePercent(IList<InfoValue> list)
        {
            if (list != null)
            {
                // get sum, max
                double sum = 0, max = 0;
                for (int i = 0; i < list.Count; i++)
                {
                    var val = (double)Convert.ChangeType(list[i].Value, typeof(double), null);
                    sum += val;
                    if (i == 0 || val > max)
                    {
                        max = val;
                    }
                }

                // calculate percentages
                foreach (var iv in list)
                {
                    var val = (double)Convert.ChangeType(iv.Value, typeof(double), null);
                    iv.Percent = sum == 0 ? 0 : val / sum;
                    iv.PercentMax = max == 0 ? 0 : val / max;
                }
            }
        }
    }
}
