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 ReportServer.ReportsService;
using System.ServiceModel;
using C1.Silverlight;
using C1.Silverlight.Data;
using System.IO;
using System.Collections.ObjectModel;
using C1.Silverlight.Extended;
using System.Globalization;
using System.Reflection;

namespace ReportServer
{
    public partial class MainPage : UserControl
    {
        ReportsServiceSoapClient _reportsClient = new ReportsServiceSoapClient(
            new BasicHttpBinding { MaxReceivedMessageSize = 3 * 1024 * 1024 },
            new EndpointAddress(Extensions.GetAbsoluteUri("ReportsService.asmx")));
        object parametersPath;

        public MainPage()
        {
            InitializeComponent();

            // configure C1PropertyGrid used to edit report parameters
            parameters.AvailableEditors.Insert(0, new DateEditor());
            parameters.PropertySort = PropertySort.NoSort;

            // configure report format ComboBox
            format.ItemsSource = new[] { "PDF", "MHTML" };
            format.SelectedIndex = 0;
            format.SelectedItemChanged += OnSelectedItemChanged;

            // render report when user selects an item from the report ComboBox
            reports.SelectedItemChanged += OnSelectedItemChanged;

            // populate report list
            _reportsClient.ListReportsAsync();
            _reportsClient.ListReportsCompleted += OnListReportsCompleted;
            _reportsClient.GetReportCompleted += OnGetReportCompleted;
        }

        // show reports available on the server, or resource-based reports if
        // the server is not available
        void OnListReportsCompleted(object sender, ListReportsCompletedEventArgs e)
        {
            listingReports.Visibility = Visibility.Collapsed;
            if (e.Error != null)
            {
                C1MessageBox.Show("You'll need to change the server references to point to your own SqlServer. See the readme for more information.");

                // no report server, load local reports
                var prefix = "ReportServer.Reports.";
                var suffix = ".pdf";
                reports.ItemsSource = Assembly.GetExecutingAssembly().GetManifestResourceNames()
                    .Where(name => name.StartsWith(prefix) && name.EndsWith(suffix))
                    .Select(name => name.Substring(prefix.Length, name.Length - prefix.Length - suffix.Length));
                reports.SelectedIndex = 0;
                loadingReport.Visibility = Visibility.Collapsed;
            }
            else
            {
                
                // show reports available on the server 
                format.Visibility = Visibility.Visible;
                reports.DisplayMemberPath = "Name";
                reports.ItemsSource = e.Result;
                reports.SelectedIndex = Math.Min(e.Result.Length, 4);
            }
        }

        // render a report when the user selects it in the ComboBox
        void OnSelectedItemChanged(object sender, PropertyChangedEventArgs<object> e)
        {
            var report = reports.SelectedItem as ReportDefinition;
            if (report != null)
            {
                // show progress bar and go get the report from the server
                loadingReport.Visibility = Visibility.Visible;
                _reportsClient.GetReportAsync(report.Path, format.SelectedItem.ToString(), new ParameterValue[] {}, report.Path);
            }
            else
            {
                var resource = reports.SelectedItem as string;
                if (resource != null)
                {
                    // hide progress bar and show the report from resource stream
                    parametersPanel.Visibility = Visibility.Collapsed;
                    var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ReportServer.Reports." + resource + ".pdf");
                    ReportViewer.LoadDocument(stream);
                }
            }
        }

        // done rendering report, hide progress bar and update parameters
        void OnGetReportCompleted(object sender, GetReportCompletedEventArgs e)
        {
            // hide progress bar
            loadingReport.Visibility = Visibility.Collapsed;

            // handle any errors
            if (e.Error != null)
            {
                C1MessageBox.Show(e.Error.Message, "Error", C1MessageBoxIcon.Error);
                return;
            }

            // display the report
            ReportViewer.LoadDocument(new MemoryStream(e.Result.Data));

            // show report parameters in C1PropertyGrid if they are available
            parametersPanel.Visibility = e.Result.Parameters.Length > 0 
                ? Visibility.Visible 
                : Visibility.Collapsed;

            // update report parameters only if necessary
            if (object.Equals(e.UserState, parametersPath)) return;

            // prepare to update report parameters
            parameters.PropertyAttributes.Clear();
            parametersPath = e.UserState;
            
            // create a C1DataTable to hold the report parameters
            var dataTable = new DataTable();
            foreach (var parameter in e.Result.Parameters)
            {
                dataTable.Columns.Add(new DataColumn
                {
                    DataType = GetType(parameter.Type),
                    ColumnName = parameter.Name,
                    AllowDBNull = false,
                });
                
                parameters.PropertyAttributes.Add(new PropertyAttribute
                {
                    MemberName = parameter.Name,
                    DisplayName = parameter.Prompt,
                    Editor = parameter.ValidValues != null && parameter.ValidValues.Length > 0 ? 
                        new ComboEditor(parameter.ValidValues) : null,
                });
            }

            // create a DataRow to hold the parameter values for the current report
            var row = dataTable.NewRow();
            foreach (var parameter in e.Result.Parameters)
            {
                row[parameter.Name] = Parse(parameter.Type, parameter.DefaultValues[0]);
            }

            // assign report parameters to C1PropertyGrid so user can see and edit them
            parameters.SelectedObject = row.GetRowView();
        }

        // re=render the current report with different parameters
        void Reload(object sender, EventArgs args)
        {
            // create parameter list
            var report = reports.SelectedItem as ReportDefinition;
            var parameterValues = new List<ParameterValue>();
            var dataRowView = parameters.SelectedObject as DataRowView;
            if (dataRowView != null)
            {
                var columns = dataRowView.GetRow().Table.Columns;
                var values = dataRowView.GetRow().ItemArray;
                for (int i = 0; i < columns.Count && i < values.Length; ++i)
                {
                    if (values[i] != null)
                    {
                        parameterValues.Add(new ParameterValue
                        {
                            Name = columns[i].ColumnName,
                            Value = values[i].ToString()
                        });
                    }
                }
            }

            // render the report using the new parameters
            if (report != null)
            {
                loadingReport.Visibility = Visibility.Visible;
                _reportsClient.GetReportAsync(report.Path, format.SelectedItem.ToString(), parameterValues.ToArray(), report.Path);
            }
        }

        private object Parse(ParameterTypeEnum type, string value)
        {
            DateTime d;
            if (type == ParameterTypeEnum.DateTime && DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out d))
            {
                return d;
            }
            return value;
        }
        static Type GetType(ParameterTypeEnum type)
        {
            return type == ParameterTypeEnum.DateTime ? 
                typeof(DateTime) : typeof(string);
        }
    }
}
