using System;
using System.Windows.Forms;
using FastReport.Table;
using FastReport.Data;
using FastReport.Controls;
using FastReport.Utils;

namespace FastReport.AdvMatrix
{
    internal enum MatrixElement
    {
        None,
        Corner,
        Column,
        Row,
        Cell
    }

    // cell extension methods
    internal static partial class CellExt
    {
        internal static readonly string[] PercentNames = { "None", "PercentOfColumnTotal", "PercentOfRowTotal", "PercentOfGrandTotal", "PercentOfPreviousColumn", "PercentOfPreviousRow" };

        private static AdvMatrixObject Matrix(this TableCell cell)
        {
            return cell.Table as AdvMatrixObject;
        }

        internal static MatrixElement GetMatrixElement(this TableCell cell)
        {
            AdvMatrixObject matrix = cell.Matrix();
            int cornerWidth = matrix.Data.Rows.Size;
            int cornerHeight = matrix.Data.Columns.Size;
            if (cornerWidth == 0 && cornerHeight == 0)
                return MatrixElement.None;

            if (cell.Address.X < cornerWidth && cell.Address.Y < cornerHeight)
                return MatrixElement.Corner;
            if (cell.Address.X >= cornerWidth && cell.Address.Y < cornerHeight)
                return MatrixElement.Column;
            if (cell.Address.X < cornerWidth && cell.Address.Y >= cornerHeight)
                return MatrixElement.Row;
            return MatrixElement.Cell;
        }

        internal static HeaderDescriptor GetHeaderDescriptor(this TableCell cell)
        {
            AdvMatrixObject matrix = cell.Table as AdvMatrixObject;

            foreach (HeaderDescriptor d in matrix.Data.Columns.Descriptor.AllItems)
            {
                if (d.TemplateCell == cell)
                    return d;
            }

            foreach (HeaderDescriptor d in matrix.Data.Rows.Descriptor.AllItems)
            {
                if (d.TemplateCell == cell)
                    return d;
            }

            return null;
        }

        private static Column GetColumn(Report report, string text)
        {
            if (text.StartsWith("[") && text.EndsWith("]"))
                text = text.Substring(1, text.Length - 2);

            return DataHelper.GetColumn(report.Dictionary, text);
        }

        private static string GetCaption(Report report, string text)
        {
            Column c = GetColumn(report, text);
            if (c != null)
                return c.Alias;
            if (text.StartsWith("[") && text.EndsWith("]"))
                return text.Substring(1, text.Length - 2);
            return text;
        }

        internal static string GetDisplayText(this TableCell cell, string defaultText)
        {
            if (!cell.IsDesigning)
                return defaultText;
            
            MatrixElement el = cell.GetMatrixElement();
            if (el == MatrixElement.Cell)
            {
                CellDescriptor descr = cell.GetDescriptor();
                if (descr != null && descr.Aggregates.Count > 0)
                {
                    string text = cell.Text;
                    foreach (AggregateExpressionPair ag in descr.Aggregates)
                    {
                        Column c = GetColumn(cell.Report, ag.Expression);
                        if (c != null)
                        {
                            while (text.Contains(ag.Expression))
                            {
                                text = text.Replace(ag.Expression, c.Alias);
                            }
                        }
                    }
                    return text;
                }
            }
            else if (el == MatrixElement.Column || el == MatrixElement.Row)
            {
                HeaderDescriptor descr = cell.GetHeaderDescriptor();
                if (descr != null)
                {
                    Column c = GetColumn(cell.Report, descr.Expression);
                    if (c != null)
                        return "[" + c.Alias + "]";
                    if (descr.IsGroup && !descr.Expression.StartsWith("["))
                        return "[" + descr.Expression + "]";
                }
            }

            return defaultText;
        }

        private static TreeNode AddNode(string text, int imageIndex)
        {
            TreeNode node = new TreeNode(text);
            node.ImageIndex = imageIndex;
            node.SelectedImageIndex = imageIndex;
            return node;
        }

        internal static void ProvideMatrixItems(this TableCell cell, DataTreeView tree)
        {
            string name = cell.Table.Name;
            TreeNode matrixNode = AddNode(name, 142);
            tree.Nodes.Add(matrixNode);

            MatrixElement el = cell.GetMatrixElement();
            if (el == MatrixElement.Column || el == MatrixElement.Row)
            {
                TreeNode propertiesNode = AddNode(Res.Get("ComponentMenu,AdvMatrixCell,Properties"), 233);
                matrixNode.Nodes.Add(propertiesNode);
                propertiesNode.Nodes.Add(AddNode(name + ".RowNo", 224));
                propertiesNode.Nodes.Add(AddNode(name + ".ItemCount", 224));
            }
            else if (el == MatrixElement.Cell)
            {
                MyRes res = new MyRes("ComponentMenu,AdvMatrixCell");
                TreeNode aggregatesNode = AddNode(res.Get("AggregateFunctions"), 132);
                TreeNode specialFunctionsNode = AddNode(res.Get("SpecialFunctions"), 52);
                TreeNode propertiesNode = AddNode(res.Get("Properties"), 233);

                matrixNode.Nodes.Add(aggregatesNode);
                matrixNode.Nodes.Add(specialFunctionsNode);
                matrixNode.Nodes.Add(propertiesNode);

                foreach (string s in Aggregates.Names)
                {
                    aggregatesNode.Nodes.Add(AddNode(s + "()", 132));
                }
                foreach (string s in ExpressionParser.SpecialFunctionNames)
                {
                    specialFunctionsNode.Nodes.Add(AddNode(s + "()", 52));
                }

                propertiesNode.Nodes.Add(AddNode(name + ".ColumnIndex", 224));
                propertiesNode.Nodes.Add(AddNode(name + ".RowIndex", 224));
                propertiesNode.Nodes.Add(AddNode(name + ".ColumnValues[]", 233));
                propertiesNode.Nodes.Add(AddNode(name + ".RowValues[]", 233));
            }
        }

        public static void AddCollapseButton(this TableCell cell)
        {
            MatrixCollapseButton btn = new MatrixCollapseButton();
            btn.Parent = cell;
            btn.Width = 16;
            btn.SymbolSize = 5;
            btn.Dock = DockStyle.Left;
            btn.CreateUniqueName();
            cell.Padding = new Padding(16, 1, cell.GetSortButton() != null ? 16 : 2, 1);

            HeaderDescriptor descr = cell.GetHeaderDescriptor();
            if (descr != null)
            {
                if (descr.Items.Count == 0)
                    descr = descr.Parent;
                foreach (HeaderDescriptor child in descr.Items)
                {
                    if (child.IsGroup)
                        child.VisibleToggledBy = btn.Name;
                }
            }
        }

        public static void AddSortButton(this TableCell cell)
        {
            MatrixSortButton btn = new MatrixSortButton();
            btn.Parent = cell;
            btn.Width = 16;
            btn.SymbolSize = 7;
            btn.Dock = DockStyle.Right;
            btn.CreateUniqueName();
            cell.Padding = new Padding(cell.GetCollapseButton() != null ? 16 : 2, 1, 16, 1);
        }

        public static void ChangeExpression(this TableCell cell, string expression)
        {
            CellDescriptor cellDescr = cell.GetDescriptor();
            if (expression == "")
                cell.Text = "";
            else if (cellDescr.Aggregates.Count == 1)
            {
                AggregateExpressionPair ag = cellDescr.Aggregates[0];
                if (ag.Expression != expression)
                {
                    while (cell.Text.Contains(ag.Expression))
                    {
                        cell.Text = cell.Text.Replace(ag.Expression, expression);
                    }
                }
            }
            else
            {
                cell.Text = "[Sum(" + expression + ")]";
            }
        }

        public static void ChangeAggregateFunction(this TableCell cell, string func)
        {
            CellDescriptor descr = cell.GetDescriptor();
            if (descr.Aggregates.Count == 1)
            {
                AggregateExpressionPair ag = descr.Aggregates[0];
                string oldFunc = ag.AggregateName + "(" + ag.Expression + ")";
                string newFunc = func + "(" + ag.Expression + ")";
                if (oldFunc != newFunc)
                {
                    while (cell.Text.Contains(oldFunc))
                    {
                        cell.Text = cell.Text.Replace(oldFunc, newFunc);
                    }
                }
            }
        }

        public static void ChangePercentFunction(this TableCell cell, string func)
        {
            CellDescriptor descr = cell.GetDescriptor();
            if (descr.ContentType == CellContentType.Aggregate)
            {
                AggregateExpressionPair ag = descr.Aggregates[0];
                if (func != "None")
                    cell.Text = "[" + func + "(" + ag.AggregateName + "(" + ag.Expression + "))]";
            }
            else if (descr.ContentType == CellContentType.SingleExpression)
            {
                string text = cell.Text.Substring(1, cell.Text.Length - 2);
                foreach (string name in PercentNames)
                {
                    if (text.StartsWith(name))
                    {
                        if (func == "None")
                            cell.Text = "[" + text.Substring(name.Length + 1, text.Length - name.Length - 2) + "]";
                        else
                            cell.Text = "[" + func + text.Substring(name.Length) + "]";
                        break;
                    }
                }
            }
        }

        private static HeaderDescriptor CreateSimpleHeader(TableCell cell, HeaderDescriptor d)
        {
            HeaderDescriptor descr = d.InsertChild(new HeaderDescriptor());
            CellDescriptor cellDescr = cell.GetDescriptor();
            if (cellDescr.Aggregates.Count > 0)
                descr.DisplayText = GetCaption(cell.Report, cellDescr.Aggregates[0].Expression);
            return descr;
        }

        private static HeaderDescriptor GetColumnDescriptor(TableCell cell)
        {
            AdvMatrixObject matrix = cell.Table as AdvMatrixObject;
            TableColumn column = matrix.Columns[cell.Address.X];

            foreach (HeaderDescriptor d in matrix.Data.Columns.Descriptor.TerminalItems)
            {
                if (d.TemplateColumn == column)
                {
                    if (d.IsGroup)
                        return CreateSimpleHeader(cell, d);
                    return d;
                }
            }

            return null;
        }

        private static HeaderDescriptor GetRowDescriptor(TableCell cell)
        {
            AdvMatrixObject matrix = cell.Table as AdvMatrixObject;
            TableRow row = matrix.Rows[cell.Address.Y];

            foreach (HeaderDescriptor d in matrix.Data.Rows.Descriptor.TerminalItems)
            {
                if (d.TemplateRow == row)
                {
                    if (d.IsGroup)
                        return CreateSimpleHeader(cell, d);
                    return d;
                }
            }

            return null;
        }

        private static void UpdateColumnCells(AdvMatrixObject matrix, HeaderDescriptor descr, TextObject text)
        {
            descr.DisplayText = GetCaption(matrix.Report, text.Text);
            matrix.BuildTemplate();

            foreach (HeaderDescriptor d in matrix.Data.Rows.Descriptor.TerminalItems)
            {
                matrix[descr.TemplateColumn.Index, d.TemplateRow.Index].Text = "[Sum(" + text.Text + ")]";
                matrix[descr.TemplateColumn.Index, d.TemplateRow.Index].Format = text.Format;
            }
            matrix.UpdateDescriptors(true);
        }

        private static void UpdateRowCells(AdvMatrixObject matrix, HeaderDescriptor descr, TextObject text)
        {
            descr.DisplayText = GetCaption(matrix.Report, text.Text);
            matrix.BuildTemplate();

            foreach (HeaderDescriptor d in matrix.Data.Columns.Descriptor.TerminalItems)
            {
                matrix[d.TemplateColumn.Index, descr.TemplateRow.Index].Text = "[Sum(" + text.Text + ")]";
                matrix[d.TemplateColumn.Index, descr.TemplateRow.Index].Format = text.Format;
            }
            matrix.UpdateDescriptors(true);
        }

        public static void InsertAfter(this TableCell cell, TextObject text)
        {
            HeaderDescriptor colDescr = GetColumnDescriptor(cell);
            if (colDescr != null)
            {
                HeaderDescriptor descr = colDescr.InsertAfter(new HeaderDescriptor());
                UpdateColumnCells(cell.Matrix(), descr, text);
            }
        }

        public static void InsertBefore(this TableCell cell, TextObject text)
        {
            HeaderDescriptor colDescr = GetColumnDescriptor(cell);
            if (colDescr != null)
            {
                HeaderDescriptor descr = colDescr.InsertBefore(new HeaderDescriptor());
                UpdateColumnCells(cell.Matrix(), descr, text);
            }
        }

        public static void InsertAbove(this TableCell cell, TextObject text)
        {
            HeaderDescriptor rowDescr = GetRowDescriptor(cell);
            if (rowDescr != null)
            {
                HeaderDescriptor descr = rowDescr.InsertBefore(new HeaderDescriptor());
                UpdateRowCells(cell.Matrix(), descr, text);
            }
        }

        public static void InsertBelow(this TableCell cell, TextObject text)
        {
            HeaderDescriptor rowDescr = GetRowDescriptor(cell);
            if (rowDescr != null)
            {
                HeaderDescriptor descr = rowDescr.InsertAfter(new HeaderDescriptor());
                UpdateRowCells(cell.Matrix(), descr, text);
            }
        }

        public static void InsertInside(this TableCell cell, TextObject text)
        {

            AdvMatrixObject matrix = cell.Matrix();
            if (matrix.Data.Columns.Descriptor.Items.Count > 0)
            {
                HeaderDescriptor descr = matrix.Data.Columns.Descriptor.Items[0];
                cell.Text = "[Sum(" + text.Text + ")]";
                cell.Format = text.Format;
                if (descr.IsEmpty())
                {
                    descr.DisplayText = GetCaption(matrix.Report, text.Text);
                }
                matrix.BuildTemplate();
            }
        }
    }
}