using System;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Windows;
using System.Windows.Controls;
using C1.C1Zip;
using C1.Silverlight.Data;
using C1.Silverlight.DataGrid;
using C1_MDSL.DataServiceReference;

namespace C1_MDSL
{
    public partial class Page : UserControl
    {
        //------------------------------------------------------------------------
        #region ** fields

        DataSet _ds = null;

        #endregion

        //------------------------------------------------------------------------
        #region ** ctor

        public Page()
        {
            InitializeComponent();
            LoadData();
        }

        #endregion

        //------------------------------------------------------------------------
        #region ** event handlers

        // update details view when master selection changes
        private void _gridCategories_SelectionChanged(object sender, EventArgs e)
        {
            // get category id selected in Categories table
            Category category = _gridCategories.SelectedItem as Category;
            if (category != null)
            {
                // new linq query
                _gridProducts.ItemsSource =
                    from dr
                    in _ds.Tables["Products"].Rows
                    where (int)dr["CategoryID"] == category.CategoryID
                    select dr.GetRowView();
            }
            else
            {
                DataRowView drv = _gridCategories.SelectedItem as DataRowView;
                if (drv != null)
                {
                    // change the filter in the default view
                    // (detail grid will update automatically)
                    DataView dv = _ds.Tables["Products"].DefaultView;
                    dv.RowFilter = string.Format("CategoryID = {0}", drv.GetData("CategoryID"));
                    _gridProducts.ItemsSource = dv;
                }
            }
        }

        // initialize a new category row (adding a row through the C1DataGrid built-in functions)
        private void _gridCategories_BeginningNewRow(object sender, DataGridBeginningNewRowEventArgs e)
        {
            DataRowView rowView = e.Item as DataRowView;
            if (rowView != null)
            {
                rowView.SetData("CategoryName", "New category");
                rowView.SetData("Description", "This is a new category...");
                rowView.SetData("Picture", new byte[0]);
            }
        }

        // initialize a new product row (adding a row through the C1DataGrid built-in functions)
        private void _gridProducts_BeginningNewRow(object sender, DataGridBeginningNewRowEventArgs e)
        {
            DataRowView drv = _gridCategories.SelectedItem as DataRowView;
            DataRowView rowView = e.Item as DataRowView;
            rowView.SetData("CategoryID", drv.GetData("CategoryID"));
            rowView.SetData("ProductName", "New product");
            rowView.SetData("SupplierID", 1);
            rowView.SetData("QuantityPerUnit", "1");
            rowView.SetData("Discontinued", false);
        }

        // setting a row (deleting a row through the C1DataGrid built-in functions)
        private void _gridCategories_DeletingRows(object sender, DataGridDeletingRowsEventArgs e)
        {
            foreach (DataGridRow row in e.DeletedRows)
            {
                DataRowView rowView = row.DataItem as DataRowView;
                rowView.SetData("Picture", new byte[0]);
            }
        }

        private void _gridCategories_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            if (e.Property.Name == "Picture")
            {
                e.Cancel = true;
            }
        }

        // add a new row directly to the data source
        private void _btnAdd_Click(object sender, RoutedEventArgs e)
        {
            DataTable dt = _ds.Tables["Categories"];
            DataRow newRow = dt.NewRow();
            newRow["CategoryName"] = "New category";
            newRow["Description"] = "This is a new category...";
            dt.Rows.Add(newRow);
        }

        // delete a row directly from the data source
        private void _btnRemove_Click(object sender, RoutedEventArgs e)
        {
            DataRowView drv = _gridCategories.SelectedItem as DataRowView;
            if (drv != null)
            {
                DataRow dr = drv.GetRow();
                dr.Delete();
            }
        }

        // commit changes to server
        private void _btnCommit_Click(object sender, RoutedEventArgs e)
        {
            SaveData();
        }

        #endregion

        //------------------------------------------------------------------------
        #region ** get data from server

        const bool COMPRESS = true;
        private void LoadData()
        {
            // invoke service
            var svc = GetDataService();
            svc.GetDataCompleted += svc_GetDataCompleted;
            svc.GetDataAsync("Categories,Products", COMPRESS);
        }

        private void svc_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
        {
            // handle errors
            if (e.Error != null)
            {
                _tbStatus.Text = "Error downloading data...";
                return;
            }

            // parse data stream from server (DataSet as XML)
            _tbStatus.Text = string.Format("Got data, {0:n0} kBytes", e.Result.Length / 1024);
            MemoryStream ms = new MemoryStream(e.Result);
            _ds = new DataSet();
            if (COMPRESS)
            {
                // read compressed data
                using (C1ZStreamReader zr = new C1ZStreamReader(ms))
                    _ds.ReadXml(zr);
            }
            else
            {
                // read uncompressed data
                _ds.ReadXml(ms);
            }

            // got the data, bind controls to it
            BindData();
        }
        
        private void BindData()
        {
            // get the tables we want
            DataTable dtCategories = _ds.Tables["Categories"];
            DataTable dtProducts = _ds.Tables["Products"];

            // populate categories grid
            int method = 3;
            switch (method)
            {
                // traditional (hand-made class)
                case 0:
                    _gridCategories.ItemsSource =
                        from dr
                        in dtCategories.Rows
                        select new Category(dr);
                    break;

                // full anonymous class
                case 1:
                    _gridCategories.ItemsSource =
                        from dr
                        in dtCategories.Rows
                        select dr.GetRowView();
                    break;

                // partial anonymous class
                case 2:
                    _gridCategories.ItemsSource =
                        from dr
                        in dtCategories.Rows
                        select dr.GetRowView("CategoryID", "CategoryName", "Description");
                    break;

                // directly to table
                case 3:
                    _gridCategories.ItemsSource = dtCategories.DefaultView;
                    break;
            }

            // populate products categories grid
            _gridProducts.ItemsSource = dtProducts.DefaultView;
        }

        private DataServiceSoapClient GetDataService()
        {
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.MaxBufferSize = 500000;
            binding.MaxReceivedMessageSize = 500000;
            binding.Security.Mode = BasicHttpSecurityMode.None;

            // get absolute service addres
            Uri uri = C1.Silverlight.Extensions.GetAbsoluteUri("DataService.asmx");
            // return new service client
            return new DataServiceSoapClient(binding, new EndpointAddress(uri));
        }

        #endregion

        //------------------------------------------------------------------------
        #region ** save modified data back to server

        private void SaveData()
        {
            if (_ds != null)
            {
                // get changes of each type
                byte[] dtAdded = GetChanges(DataRowState.Added);
                byte[] dtModified = GetChanges(DataRowState.Modified);
                byte[] dtDeleted = GetChanges(DataRowState.Deleted);

                // invoke service
                var svc = new DataServiceSoapClient();
                svc.UpdateDataCompleted += svc_UpdateDataCompleted;
                svc.UpdateDataAsync(dtAdded, dtModified, dtDeleted);
            }
        }

        private byte[] GetChanges(DataRowState state)
        {
            DataSet ds = _ds.GetChanges(state);
            if (ds != null)
            {
                MemoryStream ms = new MemoryStream();
                ds.WriteXml(ms);
                return ms.ToArray();
            }
            return null;
        }

        private void svc_UpdateDataCompleted(object sender, UpdateDataCompletedEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.Result))
            {
                throw new Exception("Error updating data on the server: " + e.Result);
            }
            _tbStatus.Text = string.Format("Changes accepted by server.");
            _ds.AcceptChanges();
        }

        #endregion

    }

    // helper class to show how DataRowView objects work
    public class Category
    {
        DataRow _dr;
        public Category(DataRow dr)
        {
            _dr = dr;
        }
        public int CategoryID
        {
            get { return (int)_dr["CategoryID"]; }
            set { _dr["CategoryID"] = value; }
        }
        public string CategoryName
        {
            get { return (string)_dr["CategoryName"]; }
            set { _dr["CategoryName"] = value; }
        }
        public string Description
        {
            get { return (string)_dr["Description"]; }
            set { _dr["Description"] = value; }
        }
    }
}
