HierarchicalTable.cs
//
// This code is part of Document Solutions for Imaging demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Numerics;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Layout;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;

namespace DsImagingWeb.Demos
{
    // This example shows how to draw a table with hierarchical data,
    // using the GrapeCity.Documents.Drawing.TableRenderer and related classes.
    public class HierarchicalTable
    {
        public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
        {
            var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
            using var g = bmp.CreateGraphics(Color.White);
            g.Transform = Matrix3x2.CreateScale(1.6f);
            DrawTable(g, pixelSize.Width, pixelSize.Height);
            g.Transform = Matrix3x2.Identity;
            return bmp;
        }

        class Range
        {
            public Range(double valueLow, double deltaLow, double valueHigh, double deltaHigh)
            {
                ValueLow = valueLow;
                DeltaLow = deltaLow;
                ValueHigh = valueHigh;
                DeltaHigh = deltaHigh;
            }
            public double ValueLow { get; }
            public double DeltaLow { get; } 
            public double ValueHigh { get; } 
            public double DeltaHigh { get; }
        }

        class Scope
        {
            public Scope(int ntrials, Range ad, Range @as, Range nr, Range ra, Range av)
            {
                Ntrials = ntrials;
                AD = ad;
                AS = @as;
                NR = nr;
                RA = ra;
                AV = av;
            }
            public int Ntrials { get; }
            public Range AD { get; }
            public Range AS { get; }
            public Range NR { get; }
            public Range RA { get; }
            public Range AV { get; }
        }

        static readonly Scope[] Data = new Scope[]
        {
            new Scope(20, new Range(50.5, 3.2, 74.8, 4.9), new Range(50.4, 3.6, 74.9, 6.8),
                new Range(50.3, 2.7, 75.2, 6.5), new Range(49.9, 2.6, 74.8, 6.6), new Range(50.3, 3.0, 74.9, 6.2)),
            new Scope(60, new Range(50.3, 4.5, 75.1, 4.5), new Range(50.8, 4.1, 75.6, 6.5),
                new Range(50.7, 3.4, 75.7, 5.7), new Range(49.5, 2.9, 74.7, 5.9), new Range(50.3, 3.7, 75.3, 5.6)),
            new Scope(100, new Range(52.6, 4.7, 75.9, 4.8), new Range(50.3, 4.6, 75.6, 5.9),
                new Range(51.1, 4.1, 75.3, 5.4), new Range(49.8, 3.3, 74.9, 5.3), new Range(51.9, 4.2, 75.4, 5.4)),
            new Scope(140, new Range(51.7, 5.3, 75.8, 7.0), new Range(49.8, 5.7, 75.4, 6.8),
                new Range(51.4, 4.4, 75.6, 5.6), new Range(49.5, 3.5, 74.6, 6.2), new Range(50.6, 4.7, 75.4, 6.4)),
            new Scope(180, new Range(51.5, 8.4, 76.2, 9.5), new Range(52.2, 6.6, 74.9, 7.0),
                new Range(52.0, 4.9, 75.7, 6.4), new Range(50.7, 4.2, 74.8, 6.3), new Range(51.6, 6.0, 75.4, 7.3))
        };

        static void DrawTable(GcGraphics g, float pageWidth, float pageHeight)
        {
            var host = new LayoutHost();
            var view = host.CreateView(pageWidth, pageHeight);

            var rt = view.CreateRect();
            rt.AnchorTopLeft(null, 36, 36);

            int scopeCount = Data.Length;

            var ta = new TableRenderer(g,
                rt, FixedTableSides.TopLeft,
                rowCount: 12,
                columnCount: scopeCount + 2,
                gridLineColor: Color.Black,
                gridLineWidth: 1,
                rowMinHeight: 20);

            // Thicker width for some grid lines:
            ta.SetVerticalGridLineWidth(2, 3f);
            for (int i = 0; i < 13; i += 2)
            {
                ta.SetHorizontalGridLineWidth(i, 3f);
            }

            var fmt = new TextFormat
            {
                Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "cambria.ttc")),
                FontSize = 16,
                FontSizeInGraphicUnits = true
            };

            ta.AddCell(0, 0, 2, 2);
            ta.AddCell(new CellStyle
            {
                TextAlignment = TextAlignment.Center,
                CreateTextLayout = (g, cs, data) =>
                {
                    var tl = g.CreateTextLayout();
                    tl.Append("|", fmt);
                    tl.Append("N", new TextFormat(fmt) { FontItalic = true });
                    tl.Append("trials", new TextFormat(fmt) { FontItalic = true, Subscript = true });
                    tl.Append("|", fmt);
                    return tl;
                }
            }, 0, 2, 1, scopeCount, null);

            var cs = new CellStyle
            {
                PaddingLeftRight = 10,
                TextAlignment = TextAlignment.Center,
                ParagraphAlignment = ParagraphAlignment.Center,
                FixedWidth = false,
                TextFormat = fmt
            };

            // Add a rotated cell:
            ta.AddCell(new CellStyle(cs)
            {
                PaddingTopBottom = 10,
                RotationAngle = 270
            }, 2, 0, 8, 1, "Subject");

            // Assign a style to DefaultCellStyle to avoid passing
            // it to each call of the AddCell method:
            ta.DefaultCellStyle = cs;

            ta.AddCell(2, 1, 2, 1, "AD");
            ta.AddCell(4, 1, 2, 1, "AS");
            ta.AddCell(6, 1, 2, 1, "NR");
            ta.AddCell(8, 1, 2, 1, "RA");
            ta.AddCell(10, 0, 2, 2, "Average:");

            for (int i = 0; i < scopeCount; i++)
            {
                var sc = Data[i];
                int ci = i + 2;
                ta.AddCell(1, ci, sc.Ntrials.ToString());
                ta.AddCell(2, ci, $"{sc.AD.ValueLow:n1} ± {sc.AD.DeltaLow:n1}");
                ta.AddCell(3, ci, $"{sc.AD.ValueHigh:n1} ± {sc.AD.DeltaHigh:n1}");
                ta.AddCell(4, ci, $"{sc.AS.ValueLow:n1} ± {sc.AS.DeltaLow:n1}");
                ta.AddCell(5, ci, $"{sc.AS.ValueHigh:n1} ± {sc.AS.DeltaHigh:n1}");
                ta.AddCell(6, ci, $"{sc.NR.ValueLow:n1} ± {sc.NR.DeltaLow:n1}");
                ta.AddCell(7, ci, $"{sc.NR.ValueHigh:n1} ± {sc.NR.DeltaHigh:n1}");
                ta.AddCell(8, ci, $"{sc.RA.ValueLow:n1} ± {sc.RA.DeltaLow:n1}");
                ta.AddCell(9, ci, $"{sc.RA.ValueHigh:n1} ± {sc.RA.DeltaHigh:n1}");
                ta.AddCell(10, ci, $"{sc.AV.ValueLow:n1} ± {sc.AV.DeltaLow:n1}");
                ta.AddCell(11, ci, $"{sc.AV.ValueHigh:n1} ± {sc.AV.DeltaHigh:n1}");
            }

            // Add a special background to every other row:
            ta.DefaultCellStyle = new CellStyle
            {
                FillColor = Color.FromArgb(233, 240, 222),
                Background = true
            };
            for (int i = 3; i < 12; i += 2)
            {
                ta.AddCell(i, 2, 1, scopeCount);
            }

            ta.Render();
        }
    }
}