using System.Diagnostics;

namespace MigraDoc.DocumentObjectModel.Tables
{
    /// <summary>
    /// Contains methods to simplify table formatting.
    /// </summary>
    public static class TableFormatter
    {
        /// <summary>
        /// Adds one variable count of rows and columns to the top, right, bottom and left of the table.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="newSurroundingCells"></param>
        public static void ExpandTable(Table table, int newSurroundingCells)
        {
            ExpandTable(table, newSurroundingCells, newSurroundingCells, newSurroundingCells, newSurroundingCells);
        }

        /// <summary>
        /// Adds one variable count of rows and one of columns to the right and the bottom of the table.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="newColsRight"></param>
        /// <param name="newRowsBottom"></param>
        public static void ExpandTable(Table table, int newColsRight, int newRowsBottom)
        {
            ExpandTable(table, 0, newColsRight, newRowsBottom, 0);
        }

        /// <summary>
        /// Adds variable counts of rows and columns to the top, right, bottom and of left of the table.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="newRowsTop"></param>
        /// <param name="newColsRight"></param>
        /// <param name="newRowsBottom"></param>
        /// <param name="newColsLeft"></param>
        public static void ExpandTable(Table table, int newRowsTop, int newColsRight, int newRowsBottom, int newColsLeft)
        {
            for (int i = 0; i < newRowsTop; i++)
                table.Rows.InsertObject(0, new Row());

            for (int i = 0; i < newColsLeft; i++)
            {
                table.Columns.InsertObject(0, new Column());
                foreach (Row row in table.Rows)
                    row.Cells.InsertObject(0, new Cell());
            }

            for (int i = 0; i < newRowsBottom; i++)
                table.Rows.Add(new Row());
            
            for (int i = 0; i < newColsRight; i++)
                table.Columns.Add(new Column());
        }

        /// <summary>
        /// Sets the corner cells of the table to rounded corners.
        /// </summary>
        /// <param name="table"></param>
        public static void SetRoundedCorners(Table table)
        {
            int rowCount = table.Rows.Count;
            int colCount = table.Columns.Count;

            if (rowCount < 2 || colCount < 2)
                return;

            table.Rows[0].Cells[0].RoundedCorner = RoundedCorner.TopLeft;
            table.Rows[0].Cells[colCount - 1].RoundedCorner = RoundedCorner.TopRight;
            table.Rows[rowCount - 1].Cells[colCount - 1].RoundedCorner = RoundedCorner.BottomRight;
            table.Rows[rowCount - 1].Cells[0].RoundedCorner = RoundedCorner.BottomLeft;
        }

        /// <summary>
        /// Sets the width/height of the outer columns/rows to one value.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="width"></param>
        public static void SetOuterCellsWidth(Table table, Unit width)
        {
            SetOuterCellsWidth(table, width, width);
        }

        /// <summary>
        /// Sets each the width of the outer columns and the height of the outer rows to a value.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public static void SetOuterCellsWidth(Table table, Unit width, Unit height)
        {
            int rowCount = table.Rows.Count;
            int colCount = table.Columns.Count;

            if (rowCount < 2 || colCount < 2)
                return;

            table.Columns[0].Width = width;
            table.Columns[colCount - 1].Width = width;

            table.Rows[0].Height = height;
            table.Rows[0].HeightRule = RowHeightRule.Exactly;
            table.Rows[rowCount - 1].Height = height;
            table.Rows[rowCount - 1].HeightRule = RowHeightRule.Exactly;
        }

        /// <summary>
        /// Get the Shading of the first "targetRowCount" Rows from the Row below.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="targetRowCount"></param>
        public static void CopyNeighbouringRowShadingToTop(Table table, int targetRowCount)
        {
            if (table.Rows.Count <= targetRowCount)
                return;

            Row source = table.Rows[targetRowCount];
            for (int i = 0; i < targetRowCount; i++)
                table.Rows[i].Shading = source.Shading.Clone();
        }

        /// <summary>
        /// Get the Shading of the last "targetRowCount" Rows from the Row above.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="targetRowCount"></param>
        public static void CopyNeighbouringRowShadingToBottom(Table table, int targetRowCount)
        {
            int lastIndex = table.Rows.Count - 1;
            if (lastIndex - targetRowCount < 0)
                return;

            Row source = table.Rows[lastIndex - targetRowCount];
            for (int i = 0; i < targetRowCount; i++)
                table.Rows[lastIndex - i].Shading = source.Shading.Clone();
        }

        /// <summary>
        /// Removes all inner horizontal Borders between "start" index and the next "count" Rows.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="start"></param>
        /// <param name="count"></param>
        public static void RemoveInnerHorizontalBorders(Table table, int start, int count)
        {
            int end = start + count;
            for (int i = start; i < end - 1; i++)
            {
                if (i + 1 >= table.Rows.Count)
                    break;
                table.Rows[i].Borders.Bottom.Visible = false;
                table.Rows[i + 1].Borders.Top.Visible = false;
            }
        }

        /// <summary>
        /// Removes all inner vertical Borders between "start" index and the next "count" Columns.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="start"></param>
        /// <param name="count"></param>
        public static void RemoveInnerVerticalBorders(Table table, int start, int count)
        {
            int end = start + count;
            for (int i = start; i < end - 1; i++)
            {
                if (i + 1 >= table.Columns.Count)
                    break;
                table.Columns[i].Borders.Right.Visible = false;
                table.Columns[i + 1].Borders.Left.Visible = false;
            }
        }

        /// <summary>
        /// Inserts a magic glue column to avoid PageBreaks at the first "rowCountTop" and the last "rowCountBottom" of a Table.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="insertAtIndex"></param>
        /// <param name="rowCountTop">For example 3 for rounded corners Row, Header Row and first content Row.</param>
        /// <param name="rowCountBottom">For example 2 for last content Row and rounded corners Row.</param>
        public static void InsertGlueColumn(Table table, int insertAtIndex, int rowCountTop, int rowCountBottom)
        {
            if (table.Columns.Count == 0)
                return;

            int glueColumnIndex = insertAtIndex + 1;
            Unit glueColumnWidth = Unit.FromPoint(0.1);

            Column glueColumn = new Column();
            glueColumn.Width = glueColumnWidth;

            if (table.Columns[insertAtIndex].Width > glueColumnWidth)
                table.Columns[insertAtIndex].Width -= glueColumnWidth;
            table.Columns.InsertObject(glueColumnIndex, glueColumn);

            foreach (Row row in table.Rows)
            {
                if (row.Cells.Count > glueColumnIndex)
                    row.Cells.InsertObject(glueColumnIndex, new Cell());
            }

            int mergeTop = rowCountTop - 1;
            if (mergeTop < table.Rows.Count)
                table.Rows[0].Cells[glueColumnIndex].MergeDown = mergeTop;

            int mergeBottom = rowCountBottom - 1;
            if (table.Rows.Count - 1 - mergeBottom >= 0)
                table.Rows[table.Rows.Count - 1 - mergeBottom].Cells[glueColumnIndex].MergeDown = mergeBottom;
        }

        /// <summary>
        /// Surrounds "count" Elements beginning with index "start" with a one-Column Table of the specified width, where each row contains one of the paragraphs.
        /// </summary>
        /// <param name="section"></param>
        /// <param name="start"></param>
        /// <param name="count"></param>
        /// <param name="columnWidth"></param>
        public static Table SurroundContentWithTable(Section section, int start, int count, Unit columnWidth)
        {
            Table table = new Table();
            table.Columns.AddColumn();

            table.Borders.Width = 0;
            table.Columns[0].Width = columnWidth;

            while (count-- > 0)
            {
                Row row = table.AddRow();
                row.Cells.Add(new Cell());

                Paragraph paragraph = section.Elements[start].Clone() as Paragraph;

                if (paragraph != null)
                {
                    row.Cells[0].Add(paragraph);
                    section.Elements.RemoveObjectAt(start);
                }
                else
                    Debug.Assert(false);
            }

            section.Elements.InsertObject(start, table);
            return table;
        }
    }
}
