From fce7482d1b11bd0a49326cd98353f99172eefd3c Mon Sep 17 00:00:00 2001
From: ngfrolov <ng.frolov@autodrilling.ru>
Date: Thu, 23 Jun 2022 18:04:01 +0500
Subject: [PATCH] =?UTF-8?q?CellAddress=20=D0=B4=D0=BB=D1=8F=20=D0=B7=D0=B0?=
 =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=8B=20tuple;=20XLExtentions=20=D0=BC=D0=B5?=
 =?UTF-8?q?=D1=82=D0=BE=D0=B4=D1=8B=20=D1=80=D0=B0=D1=81=D1=88=D0=B8=D1=80?=
 =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=B0?=
 =?UTF-8?q?=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=20?=
 =?UTF-8?q?=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D1=80=D0=BE=D0=B2?=
 =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?=
 =?UTF-8?q?=D0=B8=D0=B9;=20SheetBlockAbstract=20=D0=B4=D0=BB=D1=8F=20?=
 =?UTF-8?q?=D0=B1=D0=BB=D0=BE=D0=BA=D0=BE=D0=B2=20=D1=80=D0=B0=D0=BF=D0=BE?=
 =?UTF-8?q?=D1=80=D1=82=D0=B0;?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Services/DailyReport/CellAddress.cs       | 92 +++++++++++++++++++
 .../DailyReport/SheetBlockAbstract.cs         | 11 +++
 .../Services/DailyReport/XLExtentions.cs      | 78 ++++++++++++++++
 3 files changed, 181 insertions(+)
 create mode 100644 AsbCloudInfrastructure/Services/DailyReport/CellAddress.cs
 create mode 100644 AsbCloudInfrastructure/Services/DailyReport/SheetBlockAbstract.cs
 create mode 100644 AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs

diff --git a/AsbCloudInfrastructure/Services/DailyReport/CellAddress.cs b/AsbCloudInfrastructure/Services/DailyReport/CellAddress.cs
new file mode 100644
index 00000000..4b2aa03a
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/DailyReport/CellAddress.cs
@@ -0,0 +1,92 @@
+using System;
+
+namespace AsbCloudInfrastructure.Services.DailyReport
+{
+    internal class CellAddress
+    {
+        const int excelLettersCount = 'Z' - 'A';
+        public int Row { get; set; }
+        public int Colunm { get; set; }
+
+        public CellAddress(int row, int colunm)
+        {
+            Row = row;
+            Colunm = colunm;
+        }
+
+        public static CellAddress operator + (CellAddress a, CellAddress b)
+            => new CellAddress(a.Row + b.Row, a.Colunm + b.Colunm);
+        public static CellAddress operator - (CellAddress a, CellAddress b)
+            => new CellAddress(a.Row - b.Row, a.Colunm - b.Colunm);
+        public static bool operator == (CellAddress a, CellAddress b)
+            => a.Row == b.Row && a.Colunm == b.Colunm;
+        public static bool operator !=(CellAddress a, CellAddress b)
+            => !(a == b);
+
+        public static bool TryParse(string cellAddress, out CellAddress parsedAddress)
+        {
+            if (cellAddress.Length < 2)
+            {
+                parsedAddress = default;
+                return false;
+            }
+
+            int row = 0;
+            int col = 0;
+
+            for (int i = 0; i < cellAddress.Length; i++)            
+                switch (cellAddress[i])
+                {
+                    case >= '0' and <= '9':
+                        row = row * 10 + cellAddress[i] - '0';
+                        break;
+                    case >= 'A' and <= 'Z':
+                        col = col * excelLettersCount + cellAddress[i] - 'A';
+                        break;
+                    case >= 'a' and <= 'z':
+                        col = col * excelLettersCount + cellAddress[i] - 'a';
+                        break;
+                    default:                        
+                        parsedAddress = default;
+                        return false;
+                }
+            parsedAddress = new CellAddress(row, col);
+            return true;
+        }
+
+        public string ToStringA1()
+        {
+            string letter = "";
+
+            while (Colunm > 0)
+            {
+                int modulo = (Colunm - 1) % excelLettersCount;
+                letter = Convert.ToChar('A' + modulo) + letter;
+                Colunm = (Colunm - modulo) / excelLettersCount;
+            }
+
+            return letter + Row;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(this, obj))
+            {
+                return true;
+            }
+
+            if (obj is null)
+            {
+                return false;
+            }
+
+            if (obj is CellAddress address)
+                return this == address;
+
+            return false;
+        }
+
+        public override int GetHashCode()
+            => base.GetHashCode();        
+    }
+}
diff --git a/AsbCloudInfrastructure/Services/DailyReport/SheetBlockAbstract.cs b/AsbCloudInfrastructure/Services/DailyReport/SheetBlockAbstract.cs
new file mode 100644
index 00000000..e9d67cce
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/DailyReport/SheetBlockAbstract.cs
@@ -0,0 +1,11 @@
+using ClosedXML.Excel;
+
+namespace AsbCloudInfrastructure.Services.DailyReport
+{
+
+    internal abstract class SheetBlockAbstract
+    {
+        public abstract CellAddress Draw(IXLWorksheet sheet, CellAddress startPoint);
+
+    }
+}
diff --git a/AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs b/AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs
new file mode 100644
index 00000000..5434a958
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs
@@ -0,0 +1,78 @@
+using ClosedXML.Excel;
+using System;
+
+namespace AsbCloudInfrastructure.Services.DailyReport
+{
+    internal static class XLExtentions
+    {
+        public static IXLRange SetValue(this IXLRange range, object value, int maxCharsToWrap = 88)
+        {
+            range.Merge();
+            range.FirstCell().SetValue(value, maxCharsToWrap);
+            return range;
+        }
+
+        public static IXLCell SetValue(this IXLCell cell, object value)
+        {
+            cell.Value = value;
+            cell.Style
+                .SetAllBorders()
+                .Alignment.WrapText = true;
+
+            if (value is string valueString && valueString.Length > maxChartsToWrap)
+            {
+                var row = cell.WorksheetRow();
+                var baseHeight = row.Height;
+                row.Height = 0.82d * baseHeight * Math.Ceiling(1d + valueString.Length / maxChartsToWrap);
+            }
+
+            if (value is DateTime)
+            {
+                cell.DataType = XLDataType.DateTime;
+                cell.Style.DateFormat.Format = "DD.MM.YYYY HH:MM:SS";
+            }
+            else if (value is IFormattable)
+            {
+                cell.DataType = XLDataType.Number;
+                cell.Style.NumberFormat.Format = "0.00";
+            }
+
+            return cell;
+        }
+
+        public static IXLCell SetValue(this IXLCell cell, string value, bool adaptRowHeight = false)
+        {
+            cell.Value = value;
+            cell.Style
+                .SetAllBorders()
+                .Alignment.WrapText = true;
+
+            cell.Value = value;
+
+            if (adaptRowHeight)
+            {
+                var colWidth = cell.WorksheetColumn().Width;                
+                var maxCharsToWrap = colWidth / (0.1d * cell.Style.Font.FontSize); // TODO: Подобрать коэффициент
+                if(value.Length > maxCharsToWrap)
+                {
+                    var row = cell.WorksheetRow();
+                    var baseHeight = row.Height;
+                    row.Height = 0.82d * baseHeight * Math.Ceiling(1d + value.Length / maxCharsToWrap);
+                }
+            }
+
+            return cell;
+        }
+
+        public static IXLStyle SetAllBorders(this IXLStyle style, XLBorderStyleValues borderStyle = XLBorderStyleValues.Thin)
+        {
+            style.Border.RightBorder = borderStyle;
+            style.Border.LeftBorder = borderStyle;
+            style.Border.TopBorder = borderStyle;
+            style.Border.BottomBorder = borderStyle;
+            style.Border.InsideBorder = borderStyle;
+            style.Border.OutsideBorder = borderStyle;
+            return style;
+        }
+    }
+}