using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.IO;
using System.Text.RegularExpressions;

namespace FLY.Thick.RemoteHistory
{
    public interface IFlyData
    {
        DateTime Time { get; set; }
        string GetHeader();
        string ToString();
        bool TryParse(string header_str, string str);
    }


    /// <summary>
    /// 历史数据, 保存格式为 csv
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IHistory<T>
        : IRemoteHistory
        where T : IFlyData
    {
        /// <summary>
        /// 设置自定义的部分文件名，不能出现特殊符号 “.” 都不可以
        /// </summary>
        string FileNameCustom{get;set;}

        /// <summary>
        /// 数据添加
        /// </summary>
        /// <param name="t"></param>
        void AddRange(IEnumerable<T> t_array);
        void Add( T t);
    
        /// <summary>
        /// 根路径
        /// </summary>
        string RootPath{get;set;}
    }

    /// <summary>
    /// 历史数据, 保存格式为 csv
    /// </summary>
    public class History<T> : IHistory<T>, Misc.ISaveToXml
        where T : IFlyData
    {
        private DateTime last_add = DateTime.MinValue;
        private List<T> datas = new List<T>();
        /// <summary>
        /// 已经保存了数据的日期，
        /// </summary>
        private List<DateTime> dateInRootPath = new List<DateTime>();
        public History(string rootpath)
        {
            KeeyDay = 180;
            SaveRows = 200;
            MinSaveInterval = 5;
            RootPath = rootpath;
            FileNameCustom = null;
            CurrPath = null;

            CurrRows = 0;
            
            InitKeepDay();

            FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, () =>
            {
                if(last_add != DateTime.MinValue){
                    TimeSpan ts = DateTime.Now - last_add;
                    if (ts.TotalMinutes >= MinSaveInterval) 
                    {
                        //长时间没新数据，保存！！！！
                        Flush();
                        last_add = DateTime.MinValue;
                    }
                }
            }, TimeSpan.FromMinutes(1));
        }


        #region IHistory<T> 成员
        private string filenamecustom;
        /// <summary>
        /// 设置自定义的部分文件名，不能出现特殊符号 “.” 都不可以
        /// </summary>
        public string FileNameCustom
        {
            get
            {
                return filenamecustom;
            }
            set
            {
                if (filenamecustom != value) 
                {
                    
                    Flush();
                    filenamecustom = value;
                
                    NotifyPropertyChanged("FileNameCustom");
                }
            }
        }

        private int currdays = 0;
        /// <summary>
        /// 当前保存的数据天数
        /// </summary>
        public int CurrDays { get { return currdays; }
            protected set {
                if (currdays != value)
                {
                    currdays = value;
                    NotifyPropertyChanged("CurrDays");
                }
            }
        }


        private int keeyday;
        /// <summary>
        /// 数据能保存的天数
        /// </summary>
        public int KeeyDay
        {
            get
            {
                return keeyday;
            }
            set
            {
                if (value <= 0) 
                    value = 1;

                if (keeyday != value)
                {
                    keeyday = value;
                    NotifyPropertyChanged("KeeyDay");
                }
            }
        }

        private string rootpath;
        /// <summary>
        /// 根路径
        /// </summary>
        public string RootPath
        {
            get
            {
                return rootpath;
            }
            set
            {
                if (rootpath != value)
                {
                    rootpath = value;
                    NotifyPropertyChanged("RootPath");
                }
            }
        }
        private int minSaveInterval = 5;
        
        /// <summary>
        /// 最小保存时间，当5分钟内都没新的数据产生，自动 Flush() 单位min
        /// </summary>
        public int MinSaveInterval 
        {
            get { return minSaveInterval; }
            set {
                if (minSaveInterval != value) 
                {
                    minSaveInterval = value;
                    NotifyPropertyChanged("MinSaveInterval");
                }
            }
        }


        private int saverows;
        /// <summary>
        /// 多少行保存一次
        /// </summary>
        public int SaveRows
        {
            get
            {
                return saverows;
            }
            set
            {
                if (saverows != value) 
                {
                    saverows = value;
                    NotifyPropertyChanged("SaveRows");
                }
            }
        }

        private int currrows;
        /// <summary>
        /// 当前总行数
        /// </summary>
        public int CurrRows
        {
            get
            {
                return currrows;
            }
            set
            {
                if (currrows != value) 
                {
                    currrows = value;
                    NotifyPropertyChanged("CurrRows");
                }
            }
        }

        private string currpath=null;
        public string CurrPath
        {
            get
            {
                return currpath;
            }
            set
            {
                if (currpath != value)
                {
                    currpath = value;
                    NotifyPropertyChanged("CurrPath");
                }
            }
        }

        #endregion

        #region IHistory
        void CreateCurrPath() 
        {
            if (CurrPath == null)
            {
                //创建路径
                DateTime dt = datas.First().Time;
                string dirpath = RootPath + @"\" + dt.ToString(@"yyyy\\MM\\dd");
                Directory.CreateDirectory(dirpath);
                Keep(dt);

                if (string.IsNullOrEmpty(FileNameCustom))
                {
                    CurrPath = dirpath + @"\" + dt.ToString("HH_mm_ss") + ".csv";
                }
                else
                {
                    CurrPath = dirpath + @"\" + dt.ToString("HH_mm_ss") + "_" + FileNameCustom + ".csv";
                }
            }
        }
        public void Flush()
        {
            if (datas.Count() == 0)
                return;
            if (string.IsNullOrEmpty(CurrPath))
            {
                CreateCurrPath();
            }

            //以附加的方式打开只写文件。
            //若文件不存在，则会建立该文件，
            //如果文件存在，写入的数据会被加到文件尾，即文件原先的内容会被保留。
            StreamWriter sw = new StreamWriter(CurrPath, true, Encoding.GetEncoding("GB2312"));
            sw.WriteLine(datas[0].GetHeader());
            for (int i = 0; i < datas.Count; i++)
                sw.WriteLine(datas[i].ToString());
            datas.Clear();
            sw.Flush();
            sw.Close();

            CurrRows = 0;
            CurrPath = null;
        }
        public void AddRange(IEnumerable<T> t_array)
        {
            datas.AddRange(t_array);
            UpdateAdd();
        }
        public void Add(T t)
        {
            datas.Add(t);
            UpdateAdd();
        }
        void UpdateAdd() 
        {
            CurrRows = datas.Count;
            if ((CurrRows >= 1) && (CurrPath == null))
            {
                CreateCurrPath();
            }
            else if (datas.Count >= SaveRows)
            {
                //保存数据
                Flush();
            }

            last_add = DateTime.Now;
        }

        #endregion

        public void Apply() 
        {
            
        }
        //获取历史数据
        public void GetPaths(DateTime dt_begin, DateTime dt_end, string profilename,GetPathsReponseHandler return_func, object state) 
        {
            List<string> paths = GetPaths(dt_begin, dt_end, profilename);
            return_func(paths,state);
        }
        List<string> GetPaths(DateTime dt_begin, DateTime dt_end, string profilename) 
        {
            List<string> paths = new List<string>();
            Regex regex_file;
            if (profilename != null)
            {
                string unescape = Regex.Escape(profilename);
                regex_file = new Regex(@"\d{2}_\d{2}_\d{2}_.*" + unescape + @".*\.csv");
            }
            else
                regex_file = new Regex(@"\d{2}_\d{2}_\d{2}.*\.csv");

            for (int i = 0; i < dateInRootPath.Count(); i++)
            {
                if ((dateInRootPath[i].Date >= dt_begin.Date) && (dateInRootPath[i].Date <= dt_end.Date))
                {
                    DateTime dt = dateInRootPath[i].Date;
                    string dirpath = RootPath + @"\" + dt.ToString(@"yyyy\\MM\\dd");

                    DirectoryInfo mydir = new DirectoryInfo(dirpath);
                    FileSystemInfo[] fsil_file = mydir.GetFileSystemInfos();
                    for (int j = 0; j < fsil_file.Count(); j++) 
                    {
                        FileSystemInfo fsi = fsil_file[j];
                        if((fsi.Attributes != FileAttributes.Directory) && (regex_file.IsMatch(fsi.Name)))
                        {
                            paths.Add(dirpath + @"\" + fsi.Name);
                        }
                    }
                    //var fsil_file = from fsi in mydir.GetFileSystemInfos()
                    //                where (fsi.Attributes == FileAttributes.Normal) && (regex_file.IsMatch(fsi.Name))
                    //                select fsi;
                    //foreach (FileSystemInfo fsi in fsil_file)
                    //{
                    //    paths.Add(dirpath + @"\" + fsi.Name);
                    //}
                }
                else if (dateInRootPath[i].Date > dt_end.Date)
                {
                    break;
                }
            }
            return paths;
        }
        public void GetRootPath(GetRootPathReponseHandler return_func, object state) 
        {
            string path = System.Environment.CurrentDirectory;
            return_func(path, state);   
        }
        #region 自动删数据
        private void InitKeepDay() 
        {
            dateInRootPath.Clear();
            if (!Directory.Exists(RootPath))
            {
                Directory.CreateDirectory(RootPath);
                return;
            }

            DirectoryInfo mydir = new DirectoryInfo(RootPath);
            //年
            Regex regex_year = new Regex(@"\d{4}");
            Regex regex_month = new Regex(@"\d{2}");
            Regex regex_day = new Regex(@"\d{2}");

            var fsil_year = from fsi in mydir.GetFileSystemInfos() 
                            where (fsi.Attributes == FileAttributes.Directory) && (regex_year.IsMatch(fsi.Name)) 
                            select fsi;

            foreach (var fsi_year in fsil_year) 
            {
                var fsil_month = 
                    from fsi in ((DirectoryInfo)fsi_year).GetFileSystemInfos() 
                    where (fsi.Attributes == FileAttributes.Directory) && (regex_month.IsMatch(fsi.Name)) 
                    select fsi;

                foreach (var fsi_month in fsil_month)
                {
                    var fsil_day =
                        from fsi in ((DirectoryInfo)fsi_month).GetFileSystemInfos()
                        where (fsi.Attributes == FileAttributes.Directory) && (regex_day.IsMatch(fsi.Name))
                        select fsi;

                    foreach (var fsi_day in fsil_day) 
                    {

                        DateTime dt = new DateTime(
                            int.Parse(fsi_year.Name),
                            int.Parse(fsi_month.Name),
                            int.Parse(fsi_day.Name));

                        //if (DateTime.TryParse(fsi_year.Name.ToString() + "-" + fsi_month.Name.ToString() + "-" + fsi_day.Name.ToString(), out dt)) 
                        {
                            dateInRootPath.Add(dt);
                        }
                    }
                }
            }
            dateInRootPath.Sort(new Comparison<DateTime>(
                delegate(DateTime t1, DateTime t2)
                {
                    if (t1 > t2)
                        return 1;
                    else if (t1 == t2)
                        return 0;
                    else
                        return -1;
                }));
            CurrDays = dateInRootPath.Count;
            Keep();
        }
        private void Keep() 
        {
            while (dateInRootPath.Count > KeeyDay) 
            {
                string dirpath = RootPath+@"\"+dateInRootPath.First().ToString(@"yyyy\\MM\\dd");
                Directory.Delete(dirpath,true);
                dateInRootPath.RemoveAt(0);
            }
            CurrDays = dateInRootPath.Count;
        }
        private void Keep(DateTime dt) 
        {
            if (dateInRootPath.Count() > 0)
            {
                DateTime last = dateInRootPath.Last();
                if (last.Date == dt.Date)
                    return;
            }
            dateInRootPath.Add(dt.Date);
            Keep();
        }
        #endregion

       
        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyname) 
        {
            if(PropertyChanged!=null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
        }
        #endregion

        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "MinSaveInterval",
                "KeeyDay",
                "SaveRows"
            };
        }
    }
}
