//------------------------------------------------------------------------------
// <copyright file=”*.cs” company=”EhLib Team”>
//     Copyright (c) 2018 Dmitry V. Bolshakov   
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

//DataGridPrintService
//PrintDataGrid

//TBasePrintServiceComponentEh

//    property ColorSchema: TPrintColotSchemaEh read FColorSchema write FColorSchema default pcsAdaptedColorEh;
//       TPrintColotSchemaEh = (pcsFullColorEh, pcsAdaptedColorEh, pcsBlackAndWhiteEh);

// end;


namespace EhLib
{
  class PropertyGridPageColumnData
  {
    public int FromCol;
    public int ToCol;
  }

  class PropertyGridPageRowData
  {
    public int FromRow;
    public int ToRow;
  }

  public class DataPropertyGridPrintService : BasePrintServiceComponent
  {
    private readonly List<GridPageColumnData> gridPageColumnDataList = new List<GridPageColumnData>();
    private readonly List<GridPageRowData> gridPageRowDataList = new List<GridPageRowData>();
    private DataPropertyGridEh grid;
    private int pageDataWidth;
    private int pageDataHeight;
    private int pageColDataListIndex;
    private int pageRowDataListIndex;

    private readonly List<int> gridColWidths = new List<int>();
    private readonly List<int> gridRowHeights = new List<int>();

    public void PrintPreview(DataPropertyGridEh grid)
    {
      this.grid = grid;
      try
      {
        base.PrintPreview();
      }
      finally
      {
        this.grid = null;
      }
    }

    public void Print(DataPropertyGridEh grid)
    {
      this.grid = grid;
      try
      {
        base.Print();
      }
      finally
      {
        this.grid = null;
      }
    }

    protected override void OnBeginPrint(PrintEventArgs e)
    {
      gridColWidths.Clear();

      if (grid.TitleBar.Visible)
        gridColWidths.Add(grid.ColWidths[grid.TitleColumnBaseColIndex]);

      if (grid.VisibleColumns.Count > 0)
        gridColWidths.Add(grid.ColWidths[grid.StartDataColumnBaseColIndex]);
      else
        gridColWidths.Add(grid.ColWidths[grid.EmptyDataColumnBaseColIndex]);


      gridRowHeights.Clear();
      foreach (DataVertGridRow r in grid.VisibleRows)
      {
        gridRowHeights.Add(r.Height + grid.LineOptions.HorzLineSpace);
      }

      base.OnBeginPrint(e);

      CalcPagesData();
      InitMacroValues();

      pageColDataListIndex = 0;
      pageRowDataListIndex = 0;
    }

    protected virtual void CalcPagesData()
    {
      int i;
      int incWidth;
      int incFromCol;
      int incHeight;
      int incFromRow;
      GridPageColumnData pcd;
      GridPageRowData prd;
      int pageDataRowsHeight;

      gridPageColumnDataList.Clear();
      gridPageRowDataList.Clear();

      pageDataWidth = PageContentBounds.Width;
      incWidth = 0;
      incFromCol = 0;

      for (i = 0; i < gridColWidths.Count; i++)
      {
        incWidth = incWidth + gridColWidths[i];
        if (incWidth > pageDataWidth && i > incFromCol)
        {
          pcd = new GridPageColumnData
          {
            FromColIndex = incFromCol,
            ToColIndex = i - 1
          };
          gridPageColumnDataList.Add(pcd);

          incFromCol = i;
          incWidth = gridColWidths[i];
        }
      }

      pcd = new GridPageColumnData
      {
        FromColIndex = incFromCol,
        ToColIndex = gridColWidths.Count - 1
      };
      gridPageColumnDataList.Add(pcd);

      pageDataHeight = PageContentBounds.Height;
      pageDataRowsHeight = pageDataHeight;
      incHeight = 0;
      incFromRow = 0;
      for (i = 0; i < gridRowHeights.Count; i++)
      {
        if (incHeight > pageDataRowsHeight)
        {
          prd = new GridPageRowData
          {
            FromRow = incFromRow,
            ToRow = i - 1
          };
          incFromRow = i;
          incHeight = 0;
          gridPageRowDataList.Add(prd);
        }
        incHeight = incHeight + gridRowHeights[i];
      }

      prd = new GridPageRowData
      {
        FromRow = incFromRow,
        ToRow = gridRowHeights.Count - 1
      };
      gridPageRowDataList.Add(prd);

      PageCount = gridPageColumnDataList.Count * gridPageRowDataList.Count;
    }

    protected override float CalcScaleForFitToPages()
    {

      float result;
      long fullPagesTall, fullGridTall;
      long fullPagesWide, fullGridWide;
      int onePageTall, oneScaledPageTall;
      int onePageWide, oneScaledPageWide;
      int i;
      int scaleForTall, scaleForWide;
      int curExtand, curPages;

      Rectangle pgContentBounds = GetPageContentBoundsForScale(1);

      //-ScaleForTall
      fullPagesTall = 0;
      onePageTall = pgContentBounds.Height;
      for (i = 0; i < Scaling.FitToPagesTall; i++)
        fullPagesTall = fullPagesTall + onePageTall;

      fullGridTall = 0;
      for (i = 0; i < gridRowHeights.Count; i++)
        fullGridTall = fullGridTall + gridRowHeights[i];

      //FullGridTall = FullGridTall + FBeforeGridFullHeight + FAfterGridFullHeight;

      scaleForTall = (int)Math.Truncate((float)fullPagesTall / fullGridTall * 100);
      if (scaleForTall > 100)
        scaleForTall = 100;

      oneScaledPageTall = (int)Math.Truncate((float)onePageTall * 100 / scaleForTall);

      while (true)
      {
        curExtand = 0;
        curPages = 1;

        for (i = 0; i < gridRowHeights.Count; i++)
        {
          curExtand = curExtand + gridRowHeights[i];
          if (curExtand > oneScaledPageTall)
          {
            curPages = curPages + 1;
            curExtand = gridRowHeights[i];
          }
        }

        if (curPages > Scaling.FitToPagesTall)
        {
          scaleForTall = scaleForTall - 1;
          if (scaleForTall < 0)
          {
            scaleForTall = 1;
            break;
          }
          oneScaledPageTall = (int)Math.Truncate((float)onePageTall * 100 / scaleForTall);
        }
        else
          break;
      }

      //-ScaleForWide
      fullPagesWide = 0;
      onePageWide = pgContentBounds.Width;
      for (i = 0; i < Scaling.FitToPagesWide; i++)
        fullPagesWide = fullPagesWide + onePageWide;

      fullGridWide = 0;
      for (i = 0; i < gridColWidths.Count; i++)
        fullGridWide = fullGridWide + gridColWidths[i];

      scaleForWide = (int)Math.Truncate((float)fullPagesWide / fullGridWide * 100);
      if (scaleForWide > 100)
        scaleForWide = 100;

      oneScaledPageWide = (int)Math.Truncate((float)onePageWide * 100 / scaleForWide);

      while (true)
      {
        curExtand = 0;
        curPages = 1;

        for (i = 0; i < gridColWidths.Count; i++)
        {
          curExtand = curExtand + gridColWidths[i];
          if (curExtand > oneScaledPageWide)
          {
            curPages = curPages + 1;
            curExtand = gridColWidths[i];
          }
        }
        if (curPages > Scaling.FitToPagesWide)
        {
          scaleForWide = scaleForWide - 1;
          if (scaleForWide < 0)
          {
            scaleForWide = 1;
            break;
          }
          oneScaledPageWide = (int)Math.Truncate((float)onePageWide * 100 / scaleForWide);
        } else
          break;
      }

      if (scaleForWide < scaleForTall)
        result = (float)scaleForWide / 100;
      else
        result = (float)scaleForTall / 100;

      return result;
    }

    protected override void PrintPageContent(PrintPageEventArgs e)
    {
      PrintServiceEventArgs pe = new PrintServiceEventArgs(this, e);

      PrintPageCells(pe);
                                           
      SetNextPageData(e);
    }

    protected bool SetNextPageData(PrintPageEventArgs e)
    {
      bool result;

      if (pageColDataListIndex < gridPageColumnDataList.Count - 1)
      {
        pageColDataListIndex = pageColDataListIndex + 1;
        result = true;
      }
      else if (pageRowDataListIndex < gridPageRowDataList.Count - 1)
      {
        pageRowDataListIndex = pageRowDataListIndex + 1;
        pageColDataListIndex = 0;
        result = true;
      }
      else
      {
        pageColDataListIndex = gridPageColumnDataList.Count;
        pageRowDataListIndex = gridPageRowDataList.Count;
        result = false;
      }

      if (result)
        SetToNextPage(e);

      return result;
    }

    private void PrintPageCells(PrintServiceEventArgs e)
    {
      int fromRow, toRow;
      Rectangle rowRect;

      fromRow = gridPageRowDataList[pageRowDataListIndex].FromRow;
      toRow = gridPageRowDataList[pageRowDataListIndex].ToRow;

      rowRect = PageContentBounds;

      PrintTopLine(rowRect, e);

      rowRect.Y = rowRect.Y;
      rowRect.Height = rowRect.Height;

      for (int r = fromRow; r <= toRow; r++)
      {
        DataVertGridRow dataRow = grid.VisibleRows[r];
        rowRect.Height = dataRow.Height;
        PrintRow(r + grid.StartDataRowBaseRowIndex, dataRow, rowRect, e);
        rowRect.Offset(0, rowRect.Height);
      }
    }

    private void PrintRow(int rowIndex, DataVertGridRow row, Rectangle rowRect, PrintServiceEventArgs e)
    {
      int fromColIdx, toColIdx;
      Rectangle cellAreaRect;

      fromColIdx = gridPageColumnDataList[pageColDataListIndex].FromColIndex;
      toColIdx = gridPageColumnDataList[pageColDataListIndex].ToColIndex;

      cellAreaRect = rowRect;
      cellAreaRect.Width = gridColWidths[fromColIdx];

      PrintLine(Color.Black, new Point(cellAreaRect.Left-1, cellAreaRect.Top), new Point(cellAreaRect.Left-1, cellAreaRect.Bottom-1), e.Graphics);

      for (int colIdx = fromColIdx; colIdx <= toColIdx; colIdx++)
      {
        cellAreaRect.Width = gridColWidths[colIdx];
        if (colIdx == grid.TitleColumnBaseColIndex)
        {
          PrintTitleCellArea(rowIndex, row, cellAreaRect, e);
        }
        else
        {
          int dataColIndex = colIdx - grid.StartDataColumnBaseColIndex;
          PrintDataCellArea(colIdx, grid.VisibleColumns[dataColIndex], rowIndex, row, cellAreaRect, e);
        }
        cellAreaRect.Offset(cellAreaRect.Width, 0);
      }
    }

    protected virtual void PrintDataCellArea(int colIndex, object col, int rowIndex, DataVertGridRow row, Rectangle cellAreaRect, PrintServiceEventArgs e)
    {
      PrintBordersForCellArea(colIndex, rowIndex, ref cellAreaRect, e);
      PrintDataCell(colIndex, col, rowIndex, row, cellAreaRect, e);
    }

    protected virtual void PrintTitleCellArea(int rowIndex, DataVertGridRow row, Rectangle cellAreaRect, PrintServiceEventArgs e)
    {
      PrintBordersForCellArea(rowIndex, grid.TitleColumnBaseColIndex, ref cellAreaRect, e);
      PrintTitleCell(rowIndex, row, cellAreaRect, e);
    }

    public void PrintBordersForCellArea(int col, int row, ref Rectangle rect, PrintServiceEventArgs e)
    {
      bool rIsPaint;
      Color rColor;
      DashStyle rStyle;
      bool rIsExtent;
      grid.ProcessGetCellBorders(col, row, GridCellBorderType.Right, out rIsPaint, out rColor, out rStyle, out rIsExtent);

      bool bIsPaint;
      Color bColor;
      DashStyle bStyle;
      bool bIsExtent;
      grid.ProcessGetCellBorders(col, row, GridCellBorderType.Bottom, out bIsPaint, out bColor, out bStyle, out bIsExtent);

      rColor = Color.Black;
      bColor = Color.Black;

      PrintBordersForRect(ref rect, rIsPaint, bIsPaint, rColor, bColor, rIsExtent, bIsExtent, e);
    }

    protected virtual void PrintDataCell(int colIndex, object col, int rowIndex, DataVertGridRow row, Rectangle cellRect, PrintServiceEventArgs e)
    {
      int areaColIndex;
      int areaRowIndex;

      BaseGridCellManager cell = (BaseGridCellManager)grid.CellByColRow(colIndex, rowIndex, out areaColIndex, out areaRowIndex);

      cell.PrintCell(e, cellRect, areaColIndex, areaRowIndex, null);
    }

    protected virtual void PrintTitleCell(int rowIndex, DataVertGridRow  row, Rectangle cellRect, PrintServiceEventArgs e)
    {
      int gridColIndex = 0;
      int gridRowIndex = rowIndex;
      int areaColIndex;
      int areaRowIndex;

      BaseGridCellManager cell = (BaseGridCellManager)grid.CellByColRow(gridColIndex, gridRowIndex);

      cell.GridCellPosToAreaCellPos(gridColIndex, gridRowIndex, out areaColIndex, out areaRowIndex);

      cell.PrintCell(e, cellRect, areaColIndex, areaRowIndex, e);
    }

    public void PrintBordersForRect(ref Rectangle rect, bool rIsDraw, bool bIsDraw,
      Color rBorderColor, Color bBorderColor, bool rIsExtent, bool bIsExtent, PrintServiceEventArgs e)
    {
      Rectangle rBorderRect, bBorderRect;
      if (rIsDraw && rIsExtent && bIsDraw && bIsExtent)
      {
        if (rBorderColor != bBorderColor)
          if (BaseGridControl.GetColorLuminance(rBorderColor) >
              BaseGridControl.GetColorLuminance(bBorderColor)
         )
            rIsExtent = false;
          else
            bIsExtent = false;
      }

      if (rIsDraw)
      {
        rBorderRect = rect;
        if (rIsExtent)
          rBorderRect.Height++;
        PrintLine(rBorderColor, new Point(rBorderRect.Right - 1, rBorderRect.Top), new Point(rBorderRect.Right - 1, rBorderRect.Bottom - 2), e.Graphics);
      }

      if (bIsDraw)
      {
        bBorderRect = rect;
        if (bIsExtent)
          bBorderRect.Width++;
        PrintLine(bBorderColor, new Point(bBorderRect.Left, bBorderRect.Bottom - 1), new Point(bBorderRect.Right - 2, bBorderRect.Bottom - 1), e.Graphics);
      }

      if (rIsDraw)
        rect.Width--;
      if (bIsDraw)
        rect.Height--;
    }

    public void PrintTopLine(Rectangle rowRect, PrintServiceEventArgs e)
    {
      int fromCol, toCol;
      Rectangle cellAreaRect;

      fromCol = gridPageColumnDataList[pageColDataListIndex].FromColIndex;
      toCol = gridPageColumnDataList[pageColDataListIndex].ToColIndex;

      cellAreaRect = rowRect;
      cellAreaRect.Y = cellAreaRect.Y - 1;
      cellAreaRect.Width = gridColWidths[fromCol];

      for (int c = fromCol; c <= toCol; c++)
      {
        cellAreaRect.Width = gridColWidths[c];
        PrintLine(Color.Black, cellAreaRect.Location, new Point(cellAreaRect.Right-1, cellAreaRect.Top), e.Graphics);
        cellAreaRect.Offset(cellAreaRect.Width, 0);
      }
    }

  }
}
