using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
using System.Text.RegularExpressions;
using System.Data;

namespace IronIntel.Contractor
{
    internal class ChartFormatedData : IOpenXmlExcelStyleAndData
    {
        public ChartFormatedData()
        {
            FormatType = CellFormatType.CSharp;
        }

        #region IOpenXmlExcelStyleAndData Members

        public CellAlignment CAlignment { get; set; }

        public CellBorder CBorder { get; set; }

        public CellFill CFill { get; set; }

        public CellFont CFont { get; set; }

        public CellDataType DataType { get; set; }

        public string FormatCode { get; set; }

        public CellFormatType FormatType { get; set; }

        public object Value { get; set; }

        public String MergedCellsPosition { get; set; }

        #endregion
    }

    public class ExportToExcel
    {
        /// <summary>
        /// 将DataTable的数据导出到Excel
        /// </summary>
        /// <returns></returns>
        public byte[] CreateExcel(DataTable data, string caption, double[] columnWidths, string[] MergeTitles)
        {
            COpenXmlExcelSheet osheet = ConvertToOpenXmlObject(data, caption, columnWidths, MergeTitles);

            MemoryStream ms = null;
            try
            {
                ms = new MemoryStream();
                ExcelXlsx xls = new ExcelXlsx();
                xls.CreatePackage(ms, osheet);
                ms.Position = 0;

                byte[] bts = new byte[ms.Length];
                int offset = 0;
                while (offset < bts.Length)
                {
                    offset += ms.Read(bts, offset, bts.Length - offset);
                }

                return bts;
            }
            catch { }
            finally
            {
                if (ms != null)
                    ms.Close();
            }

            return null;
        }

        private object ConvertIvalidChars(object s)
        {
            if (s == null) return null;
            if (s.GetType() != typeof(string)) return s;
            const string invalidCharsMatch =
                "(?ims)[\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf" +
                "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f]";

            //取代其中無效字元, 通通換成空字串
            s = Regex.Replace(
                 s.ToString(),
                 invalidCharsMatch, "");

            return s;
        }
        
        private COpenXmlExcelSheet ConvertToOpenXmlObject(DataTable data, string caption, double[] columnWidths, string[] MergeTitles)
        {
            try
            {
                COpenXmlExcelSheet osheet = new COpenXmlExcelSheet();

                // 设置数据和格式。

                //所有数据和格式存放在此结构中。
                List<List<IOpenXmlExcelStyleAndData>> dataMatrix = new List<List<IOpenXmlExcelStyleAndData>>();
                osheet.DataMatrix = dataMatrix;

                //行数据临时对象。
                List<IOpenXmlExcelStyleAndData> rowData = null;
                //单元格数据临时对象。
                ChartFormatedData cellData = null;

                DataRow rdr = null;
                DataColumn rdc = null;

                #region 设置宽度。

                foreach (var w in columnWidths)
                {
                    osheet.WidthList.Add(w);
                }
                //for (int k = 0; k < dataFromClient.Columns.Count; k++)
                //{
                //    try
                //    {
                //        rdc = dataFromClient.Columns[k];
                //        if (rdc.Attributes == null || !rdc.Attributes.ContainsKey("Width"))
                //        {
                //            osheet.WidthList.Add(100);
                //        }
                //        else
                //        {
                //            osheet.WidthList.Add(Convert.ToDouble(rdc.Attributes["Width"]));
                //        }
                //    }
                //    catch
                //    {
                //    }
                //}

                #endregion

                int rowIndex = 0;

                #region caption

                if (!string.IsNullOrEmpty(caption))
                {
                    rowData = new List<IOpenXmlExcelStyleAndData>();
                    cellData = new ChartFormatedData();
                    cellData.CAlignment = new CellAlignment();
                    cellData.CAlignment.Align = GetAlignment("center");
                    cellData.DataType = CellDataType.String;
                    cellData.FormatCode = "";

                    cellData.CFont = new CellFont();
                    cellData.CFont.FontSize = 14;

                    cellData.Value = caption;
                    cellData.MergedCellsPosition = "A1:" + ConvertColNumber(data.Columns.Count) + "1";

                    rowData.Add(cellData);
                    rowIndex += 1;
                    dataMatrix.Add(rowData);

                    //设置第一行的高度。
                    osheet.RowHeightList.Add(rowIndex, 23);

                    //添加一个空行。
                    rowData = new List<IOpenXmlExcelStyleAndData>();
                    cellData = new ChartFormatedData();
                    cellData.MergedCellsPosition = "A2:" + ConvertColNumber(data.Columns.Count) + "2";
                    rowData.Add(cellData);
                    rowIndex += 1;
                    dataMatrix.Add(rowData);
                }

                #endregion

                #region MergeTitles

                if (MergeTitles != null && MergeTitles.Length % 2 == 0)
                {
                    rowData = new List<IOpenXmlExcelStyleAndData>();

                    for (int q = 0; q < data.Columns.Count; q++)
                    {
                        cellData = new ChartFormatedData();
                        cellData.CAlignment = new CellAlignment();
                        cellData.CAlignment.Align = GetAlignment("center");
                        cellData.DataType = CellDataType.String;
                        cellData.FormatCode = "";

                        cellData.CFont = new CellFont();
                        cellData.CFont.FontSize = 14;

                        rowData.Add(cellData);
                    }

                    for (int i = 0; i < MergeTitles.Length; i += 2)
                    {
                        string[] tmp = MergeTitles[i + 1].Split('-');
                        if (tmp.Length == 1) continue;

                        cellData = (ChartFormatedData)rowData[(Convert.ToInt32(tmp[0]) - 1)];
                        cellData.Value = MergeTitles[i];
                        cellData.MergedCellsPosition = ConvertColNumber(Convert.ToInt32(tmp[0])) + "3:" + ConvertColNumber(Convert.ToInt32(tmp[1])) + "3";
                    }

                    rowIndex += 1;
                    dataMatrix.Add(rowData);
                    //设置第一行的高度。
                    osheet.RowHeightList.Add(rowIndex, 15);
                }

                #endregion

                #region header。

                rowData = new List<IOpenXmlExcelStyleAndData>();

                for (int q = 0; q < data.Columns.Count; q++)
                {
                    rdc = data.Columns[q];
                    cellData = new ChartFormatedData();

                    string alignment = "";
                    if (rdc != null)
                    {
                        //if (rdc.Attributes != null && rdc.Attributes.ContainsKey("Alignment"))
                        //{
                        //    alignment = rdc.Attributes["Alignment"];
                        //}

                        cellData.CAlignment = new CellAlignment();
                        cellData.CAlignment.Align = GetAlignment(alignment);

                        cellData.CFont = new CellFont();
                        cellData.CFont.IsBold = true;

                        cellData.CFill = new CellFill();
                        cellData.CFill.FillColor = System.Drawing.Color.Gray;

                        cellData.DataType = CellDataType.String;
                        cellData.FormatCode = "";
                    }

                    cellData.Value = rdc.ColumnName;

                    rowData.Add(cellData);
                }
                rowIndex += 1;
                osheet.RowHeightList.Add(rowIndex, 18);
                dataMatrix.Add(rowData);

                #endregion

                #region real data 。

                for (int k = 0; k <= data.Rows.Count - 1; k++)
                {
                    rdr = data.Rows[k];
                    rowData = new List<IOpenXmlExcelStyleAndData>();

                    for (int q = 0; q < data.Columns.Count; q++)
                    {
                        rdc = data.Columns[q];

                        string format = "";
                        //if (rdc != null)
                        //{
                        //if (rdc.Attributes != null && rdc.Attributes.ContainsKey("DataFormat"))
                        //{
                        //    format = (rdc.Attributes["DataFormat"] == null
                        //        ? ""
                        //        : rdc.Attributes["DataFormat"].ToString());
                        //}
                        //}

                        cellData = new ChartFormatedData();

                        //将特殊格式值处理成字符串显示。
                        if (rdr != null)
                        {
                            bool isProcessed = false;
                            cellData.Value =
                                ConvertIvalidChars(
                                    ProcessSpecialFormat(rdr[q], format, ref isProcessed));
                            if (isProcessed) format = "";
                        }
                        cellData.FormatCode = ProcessFormat(format);
                        cellData.DataType =
                            GetDataType((cellData.Value == null ? typeof(string) : cellData.Value.GetType()));

                        string alignment = "";
                        if (rdc != null)
                        {
                            //if (rdc.Attributes != null && rdc.Attributes.ContainsKey("Alignment"))
                            //{
                            //    alignment = rdc.Attributes["Alignment"];
                            //}
                            cellData.CAlignment = new CellAlignment();
                            cellData.CAlignment.Align = GetAlignment(alignment);
                        }

                        //如果是合计行则以浅灰色显示。
                        //if (hasTotalRow && k == dataFromClient.Rows.Count - 1)
                        //{
                        //    cellData.CFill = new CellFill();
                        //    cellData.CFill.FillColor = System.Drawing.Color.LightGray;
                        //}

                        //如果是合计列则以浅灰色显示。
                        //if (hasTotalColumn && q == dataFromClient.Columns.Count - 1)
                        //{
                        //    cellData.CFill = new CellFill();
                        //    cellData.CFill.FillColor = System.Drawing.Color.LightGray;
                        //}

                        rowData.Add(cellData);
                    }

                    //rowIndex += 1;
                    //if (hasTotalRow && k == dataFromClient.Rows.Count - 1)
                    //{
                    //    osheet.RowHeightList.Add(rowIndex, totalRowHeight);
                    //}
                    //else
                    //{
                    //    osheet.RowHeightList.Add(rowIndex, 18);
                    //}

                    dataMatrix.Add(rowData);
                }

                #endregion

                return osheet;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private string ConvertColNumber(int colnum)
        {
            string zzz = "Z+";
            Regex reg = new Regex(zzz);
            string result = "";
            MatchCollection mc = null;
            for (int k = 0; k < colnum; k++)
            {
                mc = reg.Matches(result);
                if (mc.Count > 0)
                {
                    //是zzz格式。
                    string first = result.Substring(0, mc[0].Index);
                    if (string.IsNullOrEmpty(first))
                    {
                        result = result.Replace("Z", "A") + "A";
                    }
                    else
                    {
                        char c = first[first.Length - 1];
                        c = Convert.ToChar(c + 1);
                        result = c.ToString() + result.Substring(mc[0].Index).Replace("Z", "A");
                    }
                }
                else
                {
                    if (string.IsNullOrEmpty(result))
                    {
                        result = "A";
                    }
                    else
                    {
                        char c = result[result.Length - 1];
                        c = Convert.ToChar(c + 1);
                        result = result.Substring(0, result.Length - 1) + c.ToString();
                    }
                }
            }
            return result;
        }

        private Alignment GetAlignment(string align)
        {
            Alignment horizon = new Alignment() { Horizontal = HorizontalAlignmentValues.Left, WrapText = true };

            if (string.Equals(align, "center", StringComparison.OrdinalIgnoreCase))
            {
                horizon = new Alignment() { Horizontal = HorizontalAlignmentValues.Center, WrapText = true };
            }
            else if (string.Equals(align, "right", StringComparison.OrdinalIgnoreCase))
            {
                horizon = new Alignment() { Horizontal = HorizontalAlignmentValues.Right, WrapText = true };
            }

            return horizon;
        }

        private CellDataType GetDataType(Type typ)
        {
            CellDataType result = CellDataType.String;

            switch (Type.GetTypeCode(typ))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    result = CellDataType.Integer;
                    break;
                case TypeCode.Decimal:
                case TypeCode.Double:
                case TypeCode.Single:
                    result = CellDataType.Float;
                    break;
                case TypeCode.DateTime:
                    result = CellDataType.Date;
                    break;
                case TypeCode.Boolean:
                    result = CellDataType.Bool;
                    break;
                default:
                    break;
            }

            return result;
        }

        private object ProcessSpecialFormat(object val, string format, ref bool isProcessed)
        {
            object result = val;
            isProcessed = false;
            if (val == null || string.IsNullOrEmpty(format.Trim())) return result;

            CellDataType typ = GetDataType(result.GetType());

            //第一个特殊格式：如果值是6位字符串，格式是"MMM-yyyy"，则按日期处理。
            if (typ == CellDataType.String && string.Equals(format.Replace(" ", ""), "{0:MMM-yyyy}"))
            {
                string str = result.ToString();
                if (str.Length == 6)
                {
                    try
                    {
                        result = new DateTime(Convert.ToInt32(str.Substring(0, 4)), Convert.ToInt32(str.Substring(4)), 1);
                        result = string.Format(format, result);
                        isProcessed = true;
                    }
                    catch { }
                }
            }

            return result;
        }
        /// <summary>
        /// 处理Format格式，使一些特殊符号（如：*、@）可以直接在Excel中看到，而不是解释成Excel中对这些符号的定义
        /// </summary>
        /// <param name="format">格式字符串</param>
        /// <returns></returns>
        private string ProcessFormat(string format)
        {
            string resultFormat = format;
            if (string.IsNullOrEmpty(resultFormat)) return resultFormat;
            if (format.IndexOf("*") >= 0)
            {
                resultFormat = resultFormat.Replace("*", "\"*\"");
            }
            if (format.IndexOf("@") >= 0)
            {
                resultFormat = resultFormat.Replace("@", "\"@\"");
            }
            return resultFormat;
        }
    }
}
