using System.Collections.Generic;
using System;
using System.Drawing;
using System.Diagnostics;
using System.Data;
using System.Collections;
using System.Windows.Forms;
using AR;
using vmsnet;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Project;
using System.IO;


namespace vmsnet
{
    public class CFDBA
    {
        string _BaseDir;

        public class ReadProgessArgs : EventArgs
        {
            public double value { get; set; }
            public ReadProgessArgs(double value)
            {
                this.value = value;
            }
        }
        public class MessageArgs : EventArgs
        {
            public string Message { get; set; }
            public MessageArgs(string value)
            {
                Message = value;
            }
        }

        //public event EventHandler<ReadProgessArgs> ReadingProgressValueChanged;
        public event EventHandler<MessageArgs> Message;

        public CFDBA(params string[] dirarrays)
        {
            var dir = System.IO.Path.Combine(dirarrays);
            _BaseDir = dir;
            if (System.IO.Directory.Exists(_BaseDir) == false)
            {
                System.IO.Directory.CreateDirectory(_BaseDir);
            }
        }


        /// <summary>
        /// 데이터를 기록합니다.
        /// </summary>
        /// <param name="time">기록시간(파일명이 결정됨)</param>
        /// <param name="table">기록할테이블명(파일명)</param>
        /// <param name="buffer">기록할데이터(열데이터가 틀려서 고정할 수 없다)</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public bool InsertData(DateTime time, int ch, COMM.EALAMRAISETYPE raiseType, float volt, COMM.EALAMTYPE almType, float maxvolt, float minvolt, string am, string am2)
        {
            string DayStr = time.ToString("yyyyMMdd");
            System.IO.FileInfo fi = new System.IO.FileInfo(_BaseDir + "\\" + DayStr.Substring(0, 4) + "\\" + DayStr.Substring(4, 2) + "\\" + DayStr.Substring(6) + "\\" + (time.Hour + 1).ToString("00") + ".txt");
            if (!fi.Directory.Exists)
            {
                try
                {
                    fi.Directory.Create();
                }
                catch
                {
                    PUB.log.AddE($"fail:make directory value={fi.Directory.FullName}");
                    return false;
                }
            }

            System.Text.StringBuilder StrBuf = new System.Text.StringBuilder();

            ////파일이 없다면 제목줄을 생성해준다.
            if (!fi.Exists)
            {

                ////헤더를 추가해야한다.
                System.Text.StringBuilder Header = new System.Text.StringBuilder();
                Header.Append("\t" + "ATIME    CH  RTYPE   VOLT    ATYPE   MAXVOLT MINVOLT AM  AM2");
                Header.AppendLine(); ////제목줄을 한칸띄운다
                StrBuf.Append(Header.ToString());
            }

            ////실제데이터를 추가
            int timeNumber = (int)(PUB.get_TimeNumber(time)); // time.Hour * 3600 + time.Minute * 60 + time.Second
            StrBuf.AppendLine($"\t{timeNumber}\t{ch}\t{(int)raiseType}\t{volt}\t{(int)almType}\t{maxvolt}\t{minvolt}\t{am}\t{am2}");

            try
            {
                //fi.WriteText(StrBuf.ToString(), true);    // 이전은 단독모드
                /* 작성자: 이재웅, 작성일: 2024-09-23, 작성내용: 접근모드를 공유모드로 전환 */
                using (FileStream fs = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                using (StreamWriter writer = new StreamWriter(fs))
                { writer.Write(StrBuf.ToString()); }
                
                StrBuf.Clear();
            }
            catch (Exception)
            {
                PUB.log.AddE($"fail:write aldata file={fi.FullName}");
                return false;
            }

            return true;
        }

        ////기간내 존재하는 파일을 반환합니다(테이블단위)
        public List<string> GetfileS(DateTime sd, DateTime ed)
        {

            List<string> retval = new List<string>();
            int SttYear = sd.Year;
            int EndYear = ed.Year;
            if (EndYear < SttYear)
            {
                return retval;
            }

            int SttMonth = sd.Month;
            int EndMonth = ed.Month;

            int SttDay = sd.Day;
            int EndDay = ed.Day;

            int si = System.Convert.ToInt32(sd.ToString("yyyyMMdd"));
            int ei = System.Convert.ToInt32(ed.ToString("yyyyMMdd"));
            bool SameDay = si == ei ? true : false;

            ////20140101
            for (int dayinfo = si; dayinfo <= ei; dayinfo++)
            {
                string DayStr = dayinfo.ToString();
                System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(_BaseDir + "\\" + DayStr.Substring(0, 4) + "\\" + DayStr.Substring(4, 2) + "\\" + DayStr.Substring(6));
                if (!dir.Exists)
                {
                    continue;
                }
                ////폴더가 존재하므로 해당 테이블파일이 존재하는지 확인한다

                if (SameDay)
                {
                    ////동일날짜라면 해당 시간대만 조회하면됨
                    for (int hourinfo = int.Parse(sd.ToString("HH")); hourinfo <= int.Parse(ed.ToString("HH")); hourinfo++)
                    {
                        string fn = dir.FullName + "\\" + (hourinfo + 1).ToString("00") + ".txt";
                        if (System.IO.File.Exists(fn))
                        {
                            retval.Add(fn);
                        }
                    }
                }
                else
                {
                    if (dayinfo == si) ////시작일이라면 시작시간부터 24시까지 이다.
                    {
                        for (int hourinfo = int.Parse(sd.ToString("HH")); hourinfo <= 23; hourinfo++)
                        {
                            string fn = dir.FullName + "\\" + (hourinfo + 1).ToString("00") + ".txt";
                            if (System.IO.File.Exists(fn))
                            {
                                retval.Add(fn);
                            }
                        }
                    }
                    else if (dayinfo == ei) ////종료일이라면 1~ 종료시까지이다.
                    {
                        for (int hourinfo = 0; hourinfo <= int.Parse(ed.ToString("HH")); hourinfo++)
                        {
                            string fn = dir.FullName + "\\" + (hourinfo + 1).ToString("00") + ".txt";
                            if (System.IO.File.Exists(fn))
                            {
                                retval.Add(fn);
                            }
                        }
                    }
                    else
                    {
                        ////중간에 끼어있는 날짜라면 1~24모두가 속한다.
                        for (int hourinfo = 0; hourinfo <= 23; hourinfo++)
                        {
                            string fn = dir.FullName + "\\" + (hourinfo + 1).ToString("00") + ".txt";
                            if (System.IO.File.Exists(fn))
                            {
                                retval.Add(fn);
                            }
                        }
                    }
                }
            }
            retval.Sort();
            return retval;
        }


        /// <summary>
        /// 이번달이후의 데이터를 조회합니다.
        /// </summary>
        /// <param name="ch"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        public (TimeSpan, DocumentElement.ALARMDataTable) GetAlarmData(int ch)
        {
            ////이번달이후의 데이터를 가져온다.
            DateTime sd = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01 00:00:00"));
            DateTime ed = DateTime.Now;
            return GetAlarmData(ch, sd, ed);
        }

        public (TimeSpan, DocumentElement.ALARMDataTable) GetAlarmData(DateTime sd, DateTime ed)
        {
            return GetAlarmData(-1, sd, ed);
        }

        public (TimeSpan, DocumentElement.ALARMDataTable) GetAlarmData(DateTime sd, DateTime ed, int rtypes, int rtypee)
        {
            return GetAlarmData(-1, sd, ed, rtypes, rtypee);
        }

        public volatile bool cancel;
        private readonly object lockObject = new object();

        /// <summary>
        /// 지정된시간사이의 데이터를 조회합니다.
        /// </summary>
        /// <param name="stime">시작시간(20120319161800)</param>
        /// <param name="etime">종료시간(년월일시분초)</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public (TimeSpan, DocumentElement.ALARMDataTable) GetAlarmData(int ch, DateTime sd, DateTime ed, int rtypes = -1, int rtypee = -1)
        {
            DateTime dtStart = DateTime.Now;
            List<string> files = GetfileS(sd, ed);

            //전체파일의 내용을 버퍼에 담는다
            Dictionary<string, string[]> lines = new Dictionary<string, string[]>();

            foreach (var fn in files)
            {
                lines.Add(fn, System.IO.File.ReadAllLines(fn));
            }
            var totallines = lines.Sum(t => t.Value.Length);
            Message?.Invoke(this, new MessageArgs($"total : {totallines} lines"));

            var progressMax = (double)totallines;
            var progressVal = 0.0;
            var currentline = 0;
            var alertprogress = 0.0;


            //ReadingProgressValueChanged?.Invoke(this, new ReadProgessArgs(0));

            var alaramDT = new DocumentElement.ALARMDataTable();
            cancel = false;
            try
            {
                foreach (var line in lines)
                {
                    foreach (string linedata in line.Value)
                    {

                        lock (lockObject)
                        {
                            if (cancel)
                            {
                                //Console.WriteLine("작업이 취소되었습니다.");
                                //token.ThrowIfCancellationRequested();  // OperationCanceledException을 발생시켜 작업을 종료합니다.
                                //return (new TimeSpan(0), null);
                                //cancel = true;
                                Message?.Invoke(this, new MessageArgs($"job cancel"));
                                break;

                            }
                        }



                        currentline += 1;
                        progressVal = currentline / progressMax;

                        if (progressVal - alertprogress >= 5)
                        {
                            alertprogress = progressVal;
                            Message?.Invoke(this, new MessageArgs($"Progress : {progressVal} lines"));
                            //ReadingProgressValueChanged?.Invoke(this, new ReadProgessArgs(progressVal));
                        }


                        if (linedata.Trim() == "") continue;

                        string[] buf = linedata.Split(System.Convert.ToChar("\t"));
                        if (buf.GetUpperBound(0) < 8) continue; ////데이터수량이 안맞다.

                        string chStr = buf[2];
                        if (chStr.IsNumeric() == false) continue; ////채널정보가 일치하지 않는경우

                        if (ch > -1 && chStr != ch.ToString()) continue;

                        if (rtypes != -1 && rtypee != -1) ////rtype도 검색한다.
                        {
                            if (buf[3] != rtypes.ToString() && buf[3] != rtypee.ToString()) continue;
                        }

                        var v_atime = buf[1];
                        var v_ch = short.Parse(buf[2]);
                        var v_rtype = short.Parse(buf[3]);

                        //존재하는 데이터는 처리하지 않느낟.
                        if (alaramDT.Where(t => t.ATIME == v_atime && t.CH == v_ch && t.RTYPE == v_rtype).Any() == false)
                        {
                            var newdr = alaramDT.NewALARMRow();
                            newdr.ATIME = v_atime;// buf[1];
                            newdr.TIME = PUB.get_TimeString(decimal.Parse(newdr.ATIME), line.Key);
                            newdr.CH = v_ch;// short.Parse(buf[2]);
                            newdr.RTYPE = v_rtype;// short.Parse(buf[3]);
                            newdr.VOLT = float.Parse(buf[4]);
                            newdr.ATYPE = short.Parse(buf[5]);
                            newdr.MAXVOLT = float.Parse(buf[6]);
                            newdr.MINVOLT = float.Parse(buf[7]);
                            newdr.AM = buf[8];
                            newdr.AM2 = buf[9];
                            alaramDT.AddALARMRow(newdr);
                        }
                        else
                        {
                            //중복데이터
                            PUB.log.AddAT($"알람중복데이터 atime={v_atime},ch={v_ch},rtype={v_rtype}");
                        }


                    }

                    if (cancel)
                    {
                        //Console.WriteLine("작업이 취소되었습니다.");
                        //token.ThrowIfCancellationRequested();  // OperationCanceledException을 발생시켜 작업을 종료합니다.
                        //return (new TimeSpan(0), null);
                        break;
                    }
                }
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("작업이 정상적으로 취소되었습니다.");
                throw;  // 예외를 다시 던져 작업이 중지되도록 합니다.
            }

            alaramDT.AcceptChanges();
            var ts = DateTime.Now - dtStart;
            return (ts, alaramDT);
        }


    }

}
