using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;

namespace EasyTabParts
{
    class ZoneTabsEditorPart : EditorPart
    {

        #region Members and Constants

        // Child Controls
        private Table _tabNameTable;
        private TextBox[] _tabNameTB;
        private Table _webPartTable;
        private DropDownList _themeDropDownList;
        private TextBox _fontSizeTB;
        private CheckBox _reverseColorsCB;
        private CheckBox _boldCB;
        private DropDownList _zoneLayoutDropDownList;

        #endregion

        #region ApplyChanges - Logic to push ToolPart changes back to the web part

        // ApplyChanges - Override to update the web part with the
        // latest EditorPart control values
        public override bool ApplyChanges()
        {
            try
            {
                // First, get our associated tool part as a ZoneTabs class so we can
                // access its properties
                ZoneTabs ztWebPart = this.WebPartToEdit as ZoneTabs;

                if (ztWebPart != null)
                {
                    // The tab settings are in an XML structure.
                    // Build an empty XML structure: <tabs />
                    XmlDocument settingsXmlDoc = new XmlDocument();
                    XmlElement settingsXmlRoot =
                        settingsXmlDoc.CreateElement(ZoneTabConstants.XmlElementNameTabs);
                    settingsXmlDoc.AppendChild(settingsXmlRoot);

                    // Now create tab elements for each tab
                    XmlElement[] tabElements = new XmlElement[ZoneTabConstants.MaxTabs];

                    for (int i = 0; i < ZoneTabConstants.MaxTabs; i++)
                    {
                        string tabName = _tabNameTB[i].Text;
                        if (tabName != "")
                        {
                            tabElements[i] = AddTab(settingsXmlDoc, settingsXmlRoot, tabName);
                        }
                    }

                    // OK, now we have a <tab> element for each tab in use (non-blank name)
                    // Next, we want to create a child element for each disabled web part the
                    // tab will trigger. NOTE that although the user perceives ZoneTabs as
                    // showing web parts, it actually hides the unwanted web parts, so we
                    // only need to note the web parts that need hiding
                    for (int i = 0; i < _webPartTable.Rows.Count; i += 2)
                    {
                        string webPartTitle = _webPartTable.Rows[i].Cells[0].Text;
                        TableCell webPartCbCell = _webPartTable.Rows[i + 1].Cells[0];

                        for (int j = 0; j < webPartCbCell.Controls.Count; j++)
                        {
                            CheckBox cb = webPartCbCell.Controls[j] as CheckBox;
                            if (cb != null)
                            {
                                if (!cb.Checked && cb.Enabled)
                                {
                                    AddDisabledPartToTab(settingsXmlDoc, tabElements[j], webPartTitle);
                                }
                            }
                        }
                    }

                    // Now our XML is ready, push it and the rest of the properties
                    // up to the ZoneTabs web part
                    ztWebPart.ZoneTabXml = settingsXmlDoc.OuterXml;

                    // Next pass in the selected tab theme
                    foreach (EasyTabParts.TabList.TabThemeOption option in
                            Enum.GetValues(typeof(EasyTabParts.TabList.TabThemeOption)))
                    {
                        if (option.ToString() == _themeDropDownList.SelectedValue)
                        {
                            ztWebPart.TabTheme = option;
                        }
                    }

                    // Next pass in the zone layout and reverse color flags

                    if (_zoneLayoutDropDownList.SelectedValue ==
                            System.Web.UI.WebControls.Orientation.Horizontal.ToString())
                    {
                        ztWebPart.ZoneLayout =
                            System.Web.UI.WebControls.Orientation.Horizontal;
                    }
                    else
                    {
                        ztWebPart.ZoneLayout =
                            System.Web.UI.WebControls.Orientation.Vertical;
                    }

                    ztWebPart.ReverseColors = _reverseColorsCB.Checked;

                    // Do a quick change act on the zone to get it to change now
                    ztWebPart.Zone.LayoutOrientation = ztWebPart.ZoneLayout;

                    // Next pass in the font size and bold settings
                    int fontSize;
                    if (int.TryParse (_fontSizeTB.Text, out fontSize))
                    {
                        ztWebPart.FontSize = fontSize;
                    }
                    ztWebPart.Bold = _boldCB.Checked;
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        // Adds a <tab name="tabName"> element to the <tabs /> (root) element of our
        // XML structure
        private XmlElement AddTab(XmlDocument xmlDoc, XmlElement root, string tabName)
        {
            XmlElement tabElement = xmlDoc.CreateElement(ZoneTabConstants.XmlElementNameTab);
            XmlAttribute nameAttribute = xmlDoc.CreateAttribute(ZoneTabConstants.XmlAttributeNameName);
            nameAttribute.Value = tabName;
            tabElement.Attributes.Append(nameAttribute);
            root.AppendChild(tabElement);

            return (tabElement);
        }

        // Adds a <webPart title="Title" visible="false"> element to a <tab> element
        private void AddDisabledPartToTab(XmlDocument xmlDoc, XmlElement tabElement,
            string webPartTitle)
        {
            if (tabElement != null)
            {
                XmlElement webPartElement =
                    xmlDoc.CreateElement(ZoneTabConstants.XmlElementNameWebPart);
                XmlAttribute titleAttribute =
                    xmlDoc.CreateAttribute(ZoneTabConstants.XmlAttributeTitleName);
                XmlAttribute visibleAttribute =
                    xmlDoc.CreateAttribute(ZoneTabConstants.XmlAttributeVisibleName);

                titleAttribute.Value = webPartTitle;
                webPartElement.Attributes.Append(titleAttribute);

                visibleAttribute.Value = ZoneTabConstants.XmlAttributeVisibleValueFalse;
                webPartElement.Attributes.Append(visibleAttribute);

                tabElement.AppendChild(webPartElement);
            }
        }

        #endregion

        #region SyncChanges - Logic to sync the ToolPart controls with the web part values

        // SyncChanges - Override to update the EditorPart controls
        // with the latest web part properties
        public override void SyncChanges()
        {
            this.EnsureChildControls();

            // First, get the web part as a ZonePart so we can access its properties
            ZoneTabs ztWebPart = this.WebPartToEdit as ZoneTabs;

            if (ztWebPart != null)
            {
                // Get the XML that represents the tab settings
                XmlDocument settingsXmlDoc = new XmlDocument();
                settingsXmlDoc.LoadXml(ztWebPart.ZoneTabXml);
                XmlNodeList tabNodes =
                    settingsXmlDoc.GetElementsByTagName(ZoneTabConstants.XmlElementNameTab);

                // Each web part in the zone will have a pair of rows in _webPartTable.
                // For each one of these, set the check boxes per the XML.
                for (int i = 0; i < _webPartTable.Rows.Count; i += 2)
                {
                    string webPartTitle = _webPartTable.Rows[i].Cells[0].Text;
                    TableCell webPartCbCell = _webPartTable.Rows[i + 1].Cells[0];

                    FixCheckBoxes(webPartTitle, webPartCbCell, tabNodes);
                }

                foreach (ListItem li in _zoneLayoutDropDownList.Items)
                {
                    li.Selected = (li.Value == ztWebPart.ZoneLayout.ToString());
                }

                _reverseColorsCB.Checked = ztWebPart.ReverseColors;
                _fontSizeTB.Text = ztWebPart.FontSize.ToString();
                _boldCB.Checked = ztWebPart.Bold;
            }
        }

        // Updates the check boxes for a given web part based on the <tab /> XML elements
        // (all zones are shown unless they are disabled by a child <webPart> element)
        private void FixCheckBoxes(string webPartTitle, TableCell webPartCbCell,
                XmlNodeList tabNodes)
        {
            // Iterate through the tabs, each of which will have a <tab /> element
            // As we do this we are going horizontally across the table cell of check
            // boxes which correspond to the specified web part
            for (int i = 0; i < tabNodes.Count; i++)
            {
                // Get the checkbox for this tab
                CheckBox cb = webPartCbCell.Controls[i] as CheckBox;
                XmlElement tabElement = tabNodes[i] as XmlElement;
                if (cb != null && tabElement != null)
                {
                    // If here, both the checkbox and the tab exist, so enable
                    // the checkbox and set it if the web part is enabled
                    cb.Enabled = true;
                    cb.Checked = WebPartEnabledOn(tabElement, webPartTitle);
                }
            }

            // Now handle the case where one or more tabs were removed (by setting
            // the name to blank). We need to disable the corresponding check boxes.
            if (tabNodes.Count < ZoneTabConstants.MaxTabs)
            {
                // If here, there are fewer <tab> elements than there are controls
                // (we always have MAX_TABS checkboxes per web part)
                // Iterate through the blank ones and disable them
                for (int i = tabNodes.Count; i < ZoneTabConstants.MaxTabs; i++)
                {
                    CheckBox cb = webPartCbCell.Controls[i] as CheckBox;
                    if (cb != null)
                    {
                        cb.Checked = true;
                        cb.Enabled = false;
                    }
                }
            }
        }

        // Returns true if the specified web part is enabled for the <tab> element
        // passed.
        private bool WebPartEnabledOn(XmlElement tabElement, string webPartTitle)
        {
            // Construct an XPath query to find the desired web part
            string xpathQuery = "./" +
                ZoneTabConstants.XmlElementNameWebPart +
                "[@" +
                ZoneTabConstants.XmlAttributeTitleName +
                "=\"" + webPartTitle + "\"]";
            XmlElement wpElement = tabElement.SelectSingleNode(xpathQuery) as XmlElement;

            // If in doubt, show the web part
            bool result = true;

            if (wpElement != null)
            {
                // If here, we found the web part - check its state
                XmlAttribute visibleAttribute =
                    wpElement.Attributes[ZoneTabConstants.XmlAttributeVisibleName];
                if (visibleAttribute != null)
                {
                    if (visibleAttribute.Value ==
                        ZoneTabConstants.XmlAttributeVisibleValueFalse)
                    {
                        result = false;
                    }
                }
            }
            return (result);
        }

        #endregion

        #region Tool Part Rendering

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            this.Title = "Configure Zone Tabs";

            // Get a reference to the ZoneTabs web part we are editing
            ZoneTabs ztWebPart = this.WebPartToEdit as ZoneTabs;

            if (ztWebPart != null)
            {
                // Get current settings info, stored in an XML document
                XmlDocument settingsXmlDoc = new XmlDocument();
                settingsXmlDoc.LoadXml(ztWebPart.ZoneTabXml);

                // Get a node for each <tab> element
                XmlNodeList nodes =
                    settingsXmlDoc.GetElementsByTagName(ZoneTabConstants.XmlElementNameTab);

                // 1. Label for tab name text boxes

                this.Controls.Add(CreateLiteral("Tab Names:"));

                // 2. Now build tab name table of textbox controls
                _tabNameTable = new Table();
                _tabNameTB = new TextBox[ZoneTabConstants.MaxTabs];

                // We always have MaxTabs text boxes so user can fill in new ones
                for (int i = 0; i < ZoneTabConstants.MaxTabs; i++)
                {
                    // Create a table row, cell and text box
                    TableRow tr = new TableRow();
                    TableCell tc = new TableCell();
                    tc.Controls.Add (CreateLiteral((i + 1).ToString() + ".&nbsp;"));

                    _tabNameTB[i] = new TextBox();

                    // Get the tab name if it's defined in the XML, otherwise leave it empty
                    XmlElement tabElement = nodes[i] as XmlElement;
                    if (tabElement != null)
                    {
                        string tabName =
                            tabElement.Attributes[ZoneTabConstants.XmlAttributeNameName].Value;
                        _tabNameTB[i].Text = tabName;
                    }
                    else
                    {
                        _tabNameTB[i].Text = "";
                    }
                    // Now add the controls to the table
                    tc.Controls.Add(_tabNameTB[i]);
                    tr.Cells.Add(tc);
                    _tabNameTable.Rows.Add(tr);
                }
                this.Controls.Add(_tabNameTable);

                // 3. Add a literal to explain how to use the editor part

                this.Controls.Add(CreateLiteral(
                        "After changing tabs, please click \"Apply\" to update checkboxes. " +
                        "Then use the checkboxes to select which tabs reveal each of the web parts."
                    ));

                // 4. Add table with check boxes for mapping web parts to tabs
                _webPartTable = new Table();

                // Create a row for each web part in the zone
                //				WebPartZone wpZone = ztWebPart.GetZone();
                //                for (int i=0; i<wpZone.Controls.Count; i++)
                System.Web.UI.WebControls.WebParts.WebPartZoneBase wpZone
                    = ztWebPart.Zone;
                for (int i = 0; i < wpZone.WebParts.Count; i++)
                {
                    System.Web.UI.WebControls.WebParts.WebPart wp2 =
                        wpZone.WebParts[i] as System.Web.UI.WebControls.WebParts.WebPart;
                    if (wp2 != null)
                    {
                        AddWpTableRow(_webPartTable, nodes, ztWebPart, wp2);
                    }
                }
                this.Controls.Add(_webPartTable);

                // 5. Now a label for the theme
                this.Controls.Add(CreateLiteral("Tab Theme:&nbsp;&nbsp;"));

                // 6. Now a dropdown list for the theme selection
                _themeDropDownList = new DropDownList();
                foreach (EasyTabParts.TabList.TabThemeOption option in
                            Enum.GetValues(typeof(EasyTabParts.TabList.TabThemeOption)))
                {
                    ListItem li = new ListItem(option.ToString(), option.ToString());
                    if (option == ztWebPart.TabTheme)
                    {
                        li.Selected = true;
                    }
                    _themeDropDownList.Items.Add(li);
                }
                this.Controls.Add(_themeDropDownList);

                // 7. Reverse colors
                this.Controls.Add(CreateLiteral("<br />"));

                this.Controls.Add(
                    _reverseColorsCB = CreateCheckBox("Reverse Colors<br />")
                    );

                // 8. Bold
                this.Controls.Add(
                    _boldCB = CreateCheckBox("Bold"));

                // 9. Font size
                this.Controls.Add(CreateLiteral("<br />Font Size:&nbsp;&nbsp;"));

                _fontSizeTB = new TextBox();
                _fontSizeTB.Width = Unit.Pixel(25);
                this.Controls.Add(_fontSizeTB);

                // 10. Now a checkbox and label for the zone layout
                this.Controls.Add(CreateLiteral("<hr />"));

                this.Controls.Add(CreateLiteral("Zone layout:&nbsp;&nbsp;"));

                // 11. Now a dropdown list for the zone layout orientation
                _zoneLayoutDropDownList = new DropDownList();

                ListItem zli = new ListItem("Horizontal",
                    System.Web.UI.WebControls.Orientation.Horizontal.ToString());
                zli.Selected = (ztWebPart.ZoneLayout ==
                    System.Web.UI.WebControls.Orientation.Horizontal);
                _zoneLayoutDropDownList.Items.Add(zli);

                zli = new ListItem("Vertical",
                    System.Web.UI.WebControls.Orientation.Vertical.ToString());
                zli.Selected = (ztWebPart.ZoneLayout ==
                    System.Web.UI.WebControls.Orientation.Vertical);
                _zoneLayoutDropDownList.Items.Add(zli);

                this.Controls.Add(_zoneLayoutDropDownList);
            }
        }

        // Adds a pair of rows to the table of web parts for the provided web part
        private void AddWpTableRow(Table _webPartTable, XmlNodeList tabNodes,
            WebPart zoneTabsWebPart,
            System.Web.UI.WebControls.WebParts.WebPart webPartToAdd)
        {
            // First off - we never add ourselves, so if the web part to add is
            // our own web part, then skip the whole thing
            if (webPartToAdd != zoneTabsWebPart)
            {
                // OK, we have a valid web part. Grab its title.
                string webPartTitle = webPartToAdd.Title;

                // Now create a table row and cell for the web part name
                TableRow tr = new TableRow();
                TableCell tc = new TableCell();
                tc.Text = webPartTitle;
                tr.Cells.Add(tc);
                _webPartTable.Rows.Add(tr);

                // Next, we need another row for the checkboxes.
                // We use one check box per tab, to control which tabs the web part
                // will appear on
                tr = new TableRow();
                tc = new TableCell();

                // First, create checkboxes for the tabs we have
                for (int i = 0; i < tabNodes.Count; i++)
                {
                    XmlElement tabElement = tabNodes[i] as XmlElement;
                    if (tabElement != null)
                    {
                        CheckBox cb = new CheckBox();
                        cb.Text = (i + 1).ToString();
                        cb.TextAlign = TextAlign.Left;
                        if (WebPartEnabledOn(tabElement, webPartTitle))
                        {
                            cb.Checked = true;
                        }
                        else
                        {
                            cb.Checked = false;
                        }
                        tc.Controls.Add(cb);
                    }
                }

                // Now create the remaining checkboxes, which will be disabled
                if (tabNodes.Count < ZoneTabConstants.MaxTabs)
                {
                    for (int i = tabNodes.Count; i < ZoneTabConstants.MaxTabs; i++)
                    {
                        CheckBox cb = new CheckBox();
                        cb.Text = (i + 1).ToString();
                        cb.TextAlign = TextAlign.Left;
                        cb.Enabled = false;
                        cb.Checked = true;
                        tc.Controls.Add(cb);
                    }
                }
                tr.Cells.Add(tc);

                _webPartTable.Rows.Add(tr);
            }
        }

        private Literal CreateLiteral(string text)
        {
            Literal lit = new Literal();
            lit.Text = text;
            return lit;
        }

        private CheckBox CreateCheckBox(string text)
        {
            CheckBox cb = new CheckBox();
            cb.Text = text;
            return cb;
        }

        #endregion

    }
}
