using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.FeatureService;
using ESRI.ArcGIS.Client.Toolkit.Utilities;
using System.Windows.Data;
using C1.Silverlight.FlexGrid;

namespace EsriMapSamples
{
    [System.ComponentModel.Description("FlexGrid can be bound to a FeatureLayer to give you more features such as filtering, conditional formatting and merged column headers.")]
    public partial class FeatureDataGrid : UserControl
    {
        FeatureLayerInfo featureLayerInfo;
        private Type objectType;  
        private Dictionary<string, Type> fieldInfo = null;
        private Dictionary<string, object[]> rangeDomainInfo = null;
        private bool gridUpdating = false;
        private bool graphicsUpdating = false;
        Row mergedHeaderRow = new Row();

        public FeatureDataGrid()
        {
            InitializeComponent();

            FeatureLayer featureLayer = MyMap.Layers["California"] as FeatureLayer;
            featureLayer.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(featureLayer_PropertyChanged);
            featureLayer.UpdateCompleted += new EventHandler(featureLayer_UpdateCompleted);

            // set selection mode
            c1FlexGrid1.SelectionMode = C1.Silverlight.FlexGrid.SelectionMode.ListBox;
            c1FlexGrid1.SelectionChanged +=new EventHandler<CellRangeEventArgs>(c1FlexGrid1_SelectionChanged);

            // apply cell factory
            c1FlexGrid1.CellFactory = new ConditionalCellFactory();
            c1FlexGrid1.SelectionBackground = new SolidColorBrush(Colors.Yellow);

            // initialize filter
            var filter = new C1FlexGridFilter(c1FlexGrid1);

            c1FlexGrid1.LoadedRows += new EventHandler(c1FlexGrid1_LoadedRows);

            c1FlexGrid1.AllowMerging = AllowMerging.ColumnHeaders;
            c1FlexGrid1.ColumnHeaders.Rows.Insert(0, mergedHeaderRow);
            
            // create merged column headers
            for (int i = 0; i < 1; i++)
            {
                mergedHeaderRow[i] = "Basic Info";
            }
            mergedHeaderRow[1] = "Size";
            for (int i = 2; i < 5; i++)
            {
                mergedHeaderRow[i] = "Population";
            }
            for (int i = 5; i < 13; i++)
            {
                mergedHeaderRow[i] = "Ethnicity";
            }
            for (int i = 13; i < 15; i++)
            {
                mergedHeaderRow[i] = "Gender";
            }
            for (int i = 15; i < 22; i++)
            {
                mergedHeaderRow[i] = "Age";
            }
            for (int i = 22; i < 25; i++)
            {
                mergedHeaderRow[i] = "Median Age";
            }
            mergedHeaderRow.AllowMerging = true;
            mergedHeaderRow.HorizontalAlignment = HorizontalAlignment.Center;
        }

        void c1FlexGrid1_LoadedRows(object sender, EventArgs e)
        {
            foreach (Column c in c1FlexGrid1.Columns)
            {
                c1FlexGrid1.ColumnHeaders[1, c] = c.ColumnName;
                //c1FlexGrid1.ColumnHeaders[1, c] = c.Header; // custom header text (not set)
            }

            // autosize column width
            c1FlexGrid1.AutoSizeColumn(0, 2);
        }

        private void featureLayer_UpdateCompleted(object sender, EventArgs e)
        {
            FeatureLayer featureLayer = MyMap.Layers["California"] as FeatureLayer;
            Dictionary<string, Field> fieldProps = null;
            string uniqueID = null;

            if (featureLayer != null)
            {
                featureLayerInfo = featureLayer.LayerInfo;
                if (featureLayerInfo != null)
                {
                    fieldInfo = FieldDomainUtils.SetFieldInfo(featureLayerInfo, out rangeDomainInfo, out fieldProps);
                    uniqueID = featureLayerInfo.ObjectIdField;

                    //Binding binding = new Binding();
                    //binding.Source = new PagedCollectionView(featureLayer.Graphics.ToDataSource(fieldInfo, rangeDomainInfo, fieldProps, uniqueID, out objectType));
                    //c1FlexGrid1.SetBinding(C1FlexGrid.ItemsSourceProperty, binding);

                    c1FlexGrid1.ItemsSource = new PagedCollectionView(featureLayer.Graphics.ToDataSource(fieldInfo, rangeDomainInfo, fieldProps, uniqueID, out objectType));
                }
            }
        }

        void featureLayer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SelectionCount")
            {
                UpdateFlexGridSelection();
                UpdateFlexGridPosition();
            }
        }

        private void UpdateFlexGridSelection()
        {
            if (!graphicsUpdating)
            {
                gridUpdating = true;

                GraphicsLayer graphicsLayer = MyMap.Layers["California"] as GraphicsLayer;
                if (graphicsLayer != null)
                {

                    var selectedItemCount = c1FlexGrid1.SelectedItems.Count;
                    for (int i = selectedItemCount - 1; i >= 0; i--)
                    {
                        var g = DataSourceCreator.GetGraphicSibling(c1FlexGrid1.SelectedItems[i]);
                        if (g != null && !g.Selected)
                        {
                            c1FlexGrid1.SelectedItems.RemoveAt(i);
                        }
                    }

                    var selectedGraphicsCount = graphicsLayer.SelectedGraphics.Count<Graphic>();
                    for (int i = 0; i < selectedGraphicsCount; i++)
                    {
                        var correspondingGridRow = GetCorrespondingGridRow(c1FlexGrid1, graphicsLayer.SelectedGraphics.ElementAt<Graphic>(i));
                        if (!c1FlexGrid1.SelectedItems.Contains(correspondingGridRow))
                        {
                            c1FlexGrid1.SelectedItems.Add(correspondingGridRow);
                        }
                    }
                }

                gridUpdating = false;
            }
        }

        private void UpdateFlexGridPosition()
        {
            GraphicsLayer graphicsLayer = MyMap.Layers["California"] as GraphicsLayer;
            if (graphicsLayer != null)
            {
                if (graphicsLayer.SelectedGraphics.Count<Graphic>() > 0)
                {
                    Graphic g = graphicsLayer.SelectedGraphics.Last<Graphic>();
                    object correspondingGridRow = GetCorrespondingGridRow(c1FlexGrid1, g);

                    if (correspondingGridRow != null)
                    {
                        c1FlexGrid1.ScrollIntoView((c1FlexGrid1.ItemsSource as PagedCollectionView).IndexOf(correspondingGridRow), 0);
                    }
                }
            }
        }

        private void c1FlexGrid1_SelectionChanged(object sender, CellRangeEventArgs e)
        {
            if (!gridUpdating)
            {
                graphicsUpdating = true;
                FeatureLayer featureLayer = MyMap.Layers["California"] as FeatureLayer;
                if (featureLayer != null)
                {

                    var selectedGraphicsCount = featureLayer.SelectedGraphics.Count<Graphic>();
                    for (int i = selectedGraphicsCount - 1; i >= 0; i--)
                    {
                        var correspondingGridRow = GetCorrespondingGridRow(c1FlexGrid1, featureLayer.SelectedGraphics.ElementAt<Graphic>(i));
                        if (!c1FlexGrid1.SelectedItems.Contains(correspondingGridRow))
                        {
                            featureLayer.SelectedGraphics.ElementAt<Graphic>(i).Selected = false;
                        }
                    }

                    var selectedItemsCount = c1FlexGrid1.SelectedItems.Count;
                    for (int i = 0; i < selectedItemsCount; i++)
                    {
                        var g = DataSourceCreator.GetGraphicSibling(c1FlexGrid1.SelectedItems[i]);
                        if (g != null && !g.Selected)
                            g.Selected = true;
                    }
                }
                graphicsUpdating = false;
            }
        }

        // Gets the corresponding row in C1FlexGrid for the graphic.
        private object GetCorrespondingGridRow(C1FlexGrid grid, Graphic graphic)
        {
            if (graphic != null && grid.ItemsSource != null)
            {
                foreach (var item in grid.ItemsSource)
                    if (DataSourceCreator.GetGraphicSibling(item) == graphic)
                        return item;
            }
            
            return null;
        }

        private void FeatureLayer_MouseLeftButtonDown(object sender, GraphicMouseButtonEventArgs e)
        {
            // required for map selection
            e.Graphic.Selected = !e.Graphic.Selected;
        }
    }

    /// <summary>
    /// Custom cell factory class for the C1FlexGrid. This class applies a custom
    /// green background to cells with values over 50000.
    /// </summary>
    public class ConditionalCellFactory : CellFactory
    {
        public override FrameworkElement CreateCell(C1FlexGrid grid, CellType cellType, CellRange range)
        {
            // let base class to most of the work
            var cell = base.CreateCell(grid, cellType, range);
            
            // apply green background if necessary
            if (cellType == CellType.Cell)
            {
                var cellValue = grid[range.Row, range.Column];
                if (cellValue is double && (double)cellValue > 50000)
                {
                    var border = cell as Border;
                    var tb = border.Child as TextBlock;
                    tb.Foreground = _greenBrush;
                    //border.Background = _greenBrush;
                }
                else if (cellValue is double && (double)cellValue < 100)
                {
                    var border = cell as Border;
                    var tb = border.Child as TextBlock;
                    tb.Foreground = _redBrush;
                    //border.Background = _redBrush;
                }
            }

            // done
            return cell;
        }
        static Brush _greenBrush = new SolidColorBrush(Color.FromArgb(0xff, 0, 150, 0));
        static Brush _redBrush = new SolidColorBrush(Color.FromArgb(0xff, 255, 0, 0));
    }
}
