using System;
using System.Collections.Generic;
using System.Linq;
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 System.Reflection;
using System.ServiceModel;
using StockPortfolio.localhost;
using C1.Silverlight.Legacy;
using C1.Silverlight.Data;
using C1.Silverlight.Chart;
using C1.Silverlight;

namespace StockPortfolio
{
    public partial class Page : UserControl
    {
        // ** fields
        PortfolioSet      _portfolio;
        C1.Silverlight.Legacy.C1Menu            _mnuPortfolio;
        C1Label           _lblStatus, _lblCompanyName, _lblNotes, _lblBriefTitle, _lblBriefText;
        SearchBox         _cmbSymbol;
        C1CheckBox        _chkTransactions;
        C1StackPanel      _btnGroup;
        C1Button          _btnBuy, _btnSell, _btnWatch, _btnRead;
        C1DataGrid        _grdMain;
        C1DatePicker      _dtDate;
        C1NumericTextBox  _ntShares, _ntPrice, _ntCommission;
        C1TextBox         _txtNotes;
        int               _briefIndex = -1;
        StockInfoServiceClient _stockInfoService;
        
        public Page()
        {
            // Required to initialize variables
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            // use scroll bar xaml from this assembly
            C1ControlBase.DefaultAssembly = Assembly.GetExecutingAssembly();

            // load (or create) portfolio
            Portfolio.ActivePortfolioChanged += Portfolio_ActivePortfolioChanged;
            Portfolio.CurrentStockInfoUpdated += Portfolio_CurrentStockInfoUpdated;
            Portfolio.SummaryUpdated += Portfolio_SummaryUpdated;

            // animate spinner while waiting for Portfolio services
            Portfolio.ServiceStarted += Portfolio_ServiceStarted;
            Portfolio.ServiceEnded += Portfolio_ServiceEnded;

            // add controls to the form

            // -- status label
            _lblStatus = new C1Label();
            _lblStatus.ReplacePlaceHolder(lblStatus, false);

            // -- show transactions/summary
            _chkTransactions = new C1CheckBox();
            _chkTransactions.ReplacePlaceHolder(chkTransactions, false);
            _chkTransactions.HorizontalAlignment = HorizontalAlignment.Right;
            _chkTransactions.Checked = true;
            _chkTransactions.CheckedChanged += _chkTransactions_CheckedChanged;

            // -- symbol search box
            _cmbSymbol = new SearchBox();
            _cmbSymbol.ReplacePlaceHolder(cmbSymbol, false);
            _cmbSymbol.Items = PortfolioSet.Symbols;
            _cmbSymbol.TextWidth = 250;

            // show popup above the control
            _cmbSymbol.Popup.Position = PopupPosition.Above;

            // configure popup color
            C1ListBox lb = _cmbSymbol.Popup.Content as C1ListBox;
            lb.Border.Fill = new SolidColorBrush(Color.FromArgb(255, 230, 230, 250));

            // -- date/shares/price/commission/notes
            _lblCompanyName = new C1Label();
            _lblCompanyName.ReplacePlaceHolder(lblCompanyName, false);
            _lblNotes = new C1Label();
            _lblNotes.ReplacePlaceHolder(lblNotes, false);

            _dtDate = new C1DatePicker();
            _dtDate.ReplacePlaceHolder(dtDate, false);
            _dtDate.DropDownPosition = PopupPosition.Above;
            _dtDate.Format = "d";
            _dtDate.TextBlock.FontFamily = lblStatus.FontFamily;
            _dtDate.TextBlock.FontSize = lblStatus.FontSize;

            _ntShares = new C1NumericTextBox();
            _ntShares.ReplacePlaceHolder(udShares, false);
            _ntShares.Format = "0";

            _ntPrice = new C1NumericTextBox();
            _ntPrice.ReplacePlaceHolder(udPrice, false);
            _ntPrice.Format = "c";

            _ntCommission = new C1NumericTextBox();
            _ntCommission.ReplacePlaceHolder(udCommission, false);
            _ntCommission.Format = "c";

            _txtNotes = new C1TextBox();
            _txtNotes.ReplacePlaceHolder(txtNotes, false);

            // set font and hook up input controls
            foreach (UIElement v in ctlGroup.Children)
            {
                C1TextBox tb = v as C1TextBox;
                if (tb != null)
                {
                    tb.FontFamily = lblStatus.FontFamily;
                    tb.FontSize = lblStatus.FontSize;
                    tb.Padding = new Thickness(3, 0, 3, 0);
                    tb.VerticalAlignment = VerticalAlignment.Center;
                    tb.GotFocus += tb_GotFocus;
                    tb.TextChanged += tb_TextChanged;
                }
            }

            // -- buy/sell/watch buttons
            _btnBuy = new SPButton();
            _btnBuy.ReplacePlaceHolder(btnBuy, false);
            _btnBuy.Click += _btnBuySellWatch_Click;

            _btnSell = new SPButton();
            _btnSell.ReplacePlaceHolder(btnSell, false);
            _btnSell.Click += _btnBuySellWatch_Click;

            _btnWatch = new SPButton();
            _btnWatch.ReplacePlaceHolder(btnWatch, false);
            _btnWatch.Click += _btnBuySellWatch_Click;

            _btnGroup = new C1StackPanel();
            _btnGroup.ReplacePlaceHolder(btnGroup, true);
            _btnGroup.Padding = new Thickness(4);
            _btnGroup.ChildSpacing = new Size(3, 3);
            _btnGroup.AutoSize = AutoSize.Both;
            UpdateButtonState();

            // -- index accordion
            InitializeAccordion();

            // -- news feed button and labels
            _lblBriefTitle = new C1Label();
            _lblBriefTitle.ReplacePlaceHolder(lblBriefTitle, false);
            _lblBriefText = new C1Label();
            _lblBriefText.ReplacePlaceHolder(lblBriefText, false);
            _btnRead = new SPButton();
            _btnRead.ReplacePlaceHolder(btnRead, false);
            _btnRead.Click += _btnRead_Click;
            _btnRead_Click(this, EventArgs.Empty);

            // -- data grid
            _grdMain = new C1DataGrid();
            _grdMain.ReplacePlaceHolder(grdMain, false);

            // populate menu used to select portfolios
            _mnuPortfolio = new SPMenu();
            _mnuPortfolio.HorizontalContentAlignment = HorizontalAlignment.Left;
            _mnuPortfolio.Padding = new Thickness(12, 0, 12, 0);
            _mnuPortfolio.ReplacePlaceHolder(btnPortfolio, false);
            _mnuPortfolio.ItemClicked += _mnuPortfolio_ItemClicked;
            UpdatePortfolioMenu();

            // show active portfolio
            InitializeGrid();
            _grdMain.DataSource = Portfolio.Transactions;

            InitializeChart();
            ShowChartStory.Completed += ShowChartStory_Completed;

            // hook up keyboard and zoom managers
            //FocusManager.Initialize(this);
            ZoomManager.Initialize(this.LayoutRoot);
        }

        #region Chart stuff

        CurrentStockInfo _selectedStockInfo;
        string _showSym = string.Empty;

        // toggle grid/chart views
        public void ShowChart(string symbol)
        {
            if (_stockInfoService == null)
            {
                BasicHttpBinding binding = new BasicHttpBinding();
                binding.MaxReceivedMessageSize = 2147483647; // int.MaxValue
                binding.MaxBufferSize = 2147483647; // int.MaxValue
                string url = GetAbsoluteUri("StockInfoService.svc");
                EndpointAddress address = new EndpointAddress(url);
                _stockInfoService = new StockInfoServiceClient(binding, address);
            }

            // get the current info for the selected stock
            // the async callback is where we'll transition to the chart page
            _stockInfoService.GetCurrentStockInfoCompleted += new EventHandler<GetCurrentStockInfoCompletedEventArgs>(_stockInfoService_GetCurrentStockInfoCompleted);
            _stockInfoService.GetCurrentStockInfoAsync(symbol);
        }
        void _stockInfoService_GetCurrentStockInfoCompleted(object sender, GetCurrentStockInfoCompletedEventArgs e)
        {
            _selectedStockInfo = e.Result;
            ShowChartStory.Begin();
        }
        void ShowChartStory_Completed(object sender, EventArgs e)
        {
            AddSymbol(_selectedStockInfo);
        }

        #endregion

        void chartArea_MouseLeftButtonUp(object sender, MouseEventArgs e)
        {
            ShowGrid();
        }
        public void ShowGrid()
        {
            ShowGridStory.Begin();
        }

        // get/set portfolio set
        public PortfolioSet Portfolio
        {
            get { return PortfolioSet.Portfolio; }
        }

        // switch to the next message
        private void _btnRead_Click(object sender, EventArgs e)
        {
            _briefIndex = (_briefIndex + 1) % PortfolioSet.News.Count;
            string[] news = PortfolioSet.News[_briefIndex].Split('|');
            _lblBriefTitle.Text = news[0];
            _lblBriefText.Text = news[1];
        }

        // animate spinner while waiting for Portfolio services
        private void Portfolio_ServiceStarted(object sender, EventArgs e)
        {
            SpinnerStory.Begin();
        }
        private void Portfolio_ServiceEnded(object sender, EventArgs e)
        {
            SpinnerStory.Stop();
        }

        // activate a new portfolio, show portfolio editor
        private void _mnuPortfolio_ItemClicked(object sender, EventArgs e)
        {
            C1Label item = (C1Label)_mnuPortfolio.ClickedItem;
            if (item != null)
            {
                DataRow dr = item.Tag as DataRow;
                if (dr != null)
                {
                    _mnuPortfolio.Text = dr.GetString("Name");
                    Portfolio.ActivePortfolio = dr.GetInt("ID");
                }
                else
                {
                    // TODO: Show portfolio editor
                }
            }
        }

        // buy/sell/watch stocks
        private void _btnBuySellWatch_Click(object sender, EventArgs e)
        {
            // make sure transactions are visible
            _chkTransactions.Checked = true;

            // get price/quantity information
            int shares = 0;
            double price = 0;
            double commission = 0;
            if (object.Equals(sender, _btnBuy) || object.Equals(sender, _btnSell))
            {
                // get values to use when buying/selling
                shares = (int)_ntShares.Value;
                price = _ntPrice.Value;
                commission = _ntCommission.Value;

                // selling is like buying a negative qty of shares
                if (object.Equals(sender, _btnSell))
                {
                    shares = -shares;
                }
            }

            // create new row
            DataRow dr = Portfolio.Transactions.NewRow();

            // set the data
            dr["Portfolio"] = Portfolio.ActivePortfolio;
            dr["Symbol"] = _cmbSymbol.Text;
            dr["Last Price"] = 0;
            dr["Open"] = 0;
            dr["High"] = 0;
            dr["Low"] = 0;
            dr["Date"] = _dtDate.Value.Date;
            dr["Shares"] = shares;
            dr["Price"] = price;
            dr["Commission"] = commission;
            dr["Notes"] = _txtNotes.Text;

            // add to table
            Portfolio.Transactions.Rows.Add(dr);
        }

        // select all content when textboxes get the focus
        private void tb_GotFocus(object sender, EventArgs e)
        {
            C1TextBox tb = sender as C1TextBox;
            tb.SelectAll();
        }

        // update buy/sell/watch buttons when content changes
        private void tb_TextChanged(object sender, EventArgs e)
        {
            UpdateButtonState();
        }

        // update view when the user selects a different portfolio
        private void Portfolio_ActivePortfolioChanged(object sender, EventArgs e)
        {
            UpdateStatus();
        }

        // update portfolio menu
        private void UpdatePortfolioMenu()
        {
            // add one item per portfolio, set menu text
            ObservableVisualCollection items = _mnuPortfolio.Items;
            items.Clear();
            foreach (DataRow dr in Portfolio.Portfolios.Rows)
            {
                int id = dr.GetInt("ID");
                C1Label item = new C1Label();
                item.Text = dr.GetString("Name");
                item.Tag = dr;
                items.Add(item);
                if (id == Portfolio.ActivePortfolio)
                {
                    _mnuPortfolio.Text = item.Text;
                }
            }

            // add item to edit portfolio list
            items.Add("-");
            C1Label lbl = new C1Label();
            lbl.Text = "Create/Edit Portfolios...";
            lbl.Opacity = 0.5;
            lbl.Tag = null;
            items.Add(lbl);
        }

        // update status line when new portfolio info arrives from service
        private void Portfolio_CurrentStockInfoUpdated(object sender, EventArgs e)
        {
            UpdateStatus();
        }

        // update status and save portfolio when the summary is updated
        private void Portfolio_SummaryUpdated(object sender, EventArgs e)
        {
            UpdateStatus();
        }

        // toggle transactions/summary display
        private void _chkTransactions_CheckedChanged(object sender, EventArgs e)
        {
            // show transactions or summary
            _grdMain.DataSource = _chkTransactions.Checked
                ? Portfolio.Transactions
                : Portfolio.Summary;

            // show/hide columns that only appear in the transaction view
            _grdMain.BeginUpdate();
            foreach (string colName in "Remove,Date,Price,Commission,Notes".Split(','))
            {
                _grdMain.Columns[colName].Visible = _chkTransactions.Checked;
            }
            _grdMain.EndUpdate();

            // can't delete summary rows
            // TODO: add this property to the grid
            //this.dataGridView1.AllowUserToDeleteRows = chkTransactions.Checked;
        }

        // enable/disable buttons depending on input control state
        private void UpdateButtonState()
        {
            string companyName;
            if (_cmbSymbol.Items.TryGetValue(_cmbSymbol.Text, out companyName))
            {
                _lblCompanyName.Text = companyName;
                bool canBuy = _ntShares.Value > 0;
                EnableButton(_btnBuy, canBuy);
                EnableButton(_btnSell, canBuy);
                EnableButton(_btnWatch, !canBuy);
            }
            else
            {
                _lblCompanyName.Text = string.Empty;
                EnableButton(_btnBuy, false);
                EnableButton(_btnSell, false);
                EnableButton(_btnWatch, false);
            }
        }
        private void EnableButton(C1ControlBase ctl, bool enabled)
        {
            ctl.Enabled = enabled;
            ctl.Opacity = enabled ? 1 : 0.2;
        }

        // update status line
        private void UpdateStatus()
        {
            Portfolio.Save();

            DataRow pf = Portfolio.Portfolios.Rows.Find(Portfolio.ActivePortfolio);
            double value = pf.GetDouble("Market Value");
            double cost = pf.GetDouble("Cost Basis");
            double gain = pf.GetDouble("Gain");

            _lblStatus.Text = string.Format("Current portfolio value {0:$#,###}, gain {1:$#,###} ({2:p2}), last updated at {3}.",
                value,
                gain,
                cost != 0 ? gain / cost : 0,
                Portfolio.LastUpdate.ToShortTimeString());

            _lblStatus.Visibility = value != 0 ? Visibility.Visible : Visibility.Collapsed;
        }

        // initialize main grid
        private void InitializeGrid()
        {
            // configure grid
            _grdMain.ShowSelection = ShowSelection.Never;
            _grdMain.CellSpaceColor = Color.FromArgb(5, 255, 255, 255);
            _grdMain.CellSpacing = new Size(0.5, 3);
            _grdMain.SetBorder(Color.FromArgb(20, 255, 255, 255), Colors.Transparent, 0, 0);
            _grdMain.AutoGenerateColumns = false;
            _grdMain.CellTypeNormal = typeof(SPCell);

            // configure columns
            AddColumn("Symbol", HorizontalAlignment.Left, string.Empty, 150);
            AddColumn("Last Price", HorizontalAlignment.Right, "f2", 80);
            AddColumn("Chart", HorizontalAlignment.Center, string.Empty, 60);
            AddColumn("Remove", HorizontalAlignment.Center, string.Empty, 60);
            AddColumn("Open", HorizontalAlignment.Right, "f2", 80);
            AddColumn("High", HorizontalAlignment.Right, "f2", 80);
            AddColumn("Low", HorizontalAlignment.Right, "f2", 80);
            AddColumn("Date", HorizontalAlignment.Center, _dtDate.Format, 90);
            AddColumn("Shares", HorizontalAlignment.Right, "f0", 80);
            AddColumn("Price", HorizontalAlignment.Right, "c", 80);
            AddColumn("Commission", HorizontalAlignment.Right, "c0", 100);
            AddColumn("Cost Basis", HorizontalAlignment.Right, "c0", 100);
            AddColumn("Market Value", HorizontalAlignment.Right, "c0", 100);
            AddColumn("Gain", HorizontalAlignment.Right, "c0", 100);
            AddColumn("Gain%", HorizontalAlignment.Right, "p0", 80);
            AddColumn("Notes", HorizontalAlignment.Left, string.Empty, 200);
        }
        private GridColumn AddColumn(string name, HorizontalAlignment align, string format, int width)
        {
            GridColumn col = _grdMain.Columns.Add(name);
            col.HorizontalContentAlignment = align;
            col.Format = format;
            col.Width = width;
            return col;
        }

        // initialize market index accordion
        private void InitializeAccordion()
        {
            // symbols shown in the buttons
            Dictionary<string, string> indexSymbols = new Dictionary<string, string>();
            indexSymbols["!DOW"] = "Dow Jones";
            indexSymbols["!S&P500"] = "S&P 500";
            indexSymbols["!NASDAQ"] = "Nasdaq";

            // xaml charts to be reparented into the accordion
            Dictionary<string, Canvas> indexCharts = new Dictionary<string, Canvas>();
            indexCharts["!DOW"] = dowChart;
            indexCharts["!S&P500"] = spChart;
            indexCharts["!NASDAQ"] = nasdaqChart;
            foreach (Canvas chart in indexCharts.Values)
            {
                Canvas parent = (Canvas)chart.Parent;
                parent.Children.Remove(chart);
            }

            // create accordion
            C1.Silverlight.Legacy.C1Accordion ac = new C1.Silverlight.Legacy.C1Accordion();
            ac.BeginUpdate();
            ac.ReplacePlaceHolder(accordion, false);
            ac.Padding = new Thickness(10, 0, 10, 0);
            ac.Border.Fill = ac.Border.Stroke = null;

            // populate accordion
            foreach (string key in indexSymbols.Keys)
            {
                C1AccordionPanel p = new C1AccordionPanel(typeof(SPButton));
                p.ScrollBars = ScrollBars.None;
                p.Text = indexSymbols[key];
                p.Header.HorizontalContentAlignment = HorizontalAlignment.Left;
                p.Header.VerticalContentAlignment = VerticalAlignment.Center;

                C1StackPanel panel = new C1StackPanel();
                panel.VerticalContentAlignment = VerticalAlignment.Center;
                panel.HorizontalContentAlignment = HorizontalAlignment.Center;
                p.Items.Add(panel);

                Canvas chart = indexCharts[key];
                chart.Visibility = Visibility.Visible;
                panel.Children.Add(chart);

                ac.Panels.Add(p);
            }
            ac.SelectedPanel = ac.Panels[1];
            ac.EndUpdate();
        }

        // convert relative url into absolute
        public static string GetAbsoluteUri(string relativeUri)
        {
            // get current absolute Uri; this depends on where/how the app is deployed
            // e.g. "http://labs.componentone.com/silverlight/stockportfolio/foo.aspx"
            //      "http://localhost:1337/mysamples/stockportfolio/foo.aspx"
            Uri uri = System.Windows.Browser.HtmlPage.Document.DocumentUri;
            string uriString = uri.AbsoluteUri;

            // replace page name with relative service Uri
            int ls = uriString.LastIndexOf('/');
            return uriString.Substring(0, ls + 1) + relativeUri;
        }
    }
}
