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

namespace OBJ_FileBus
{
    public class FileElement : INotifyPropertyChanged
    {
        public override string ToString()
        {
            string s="";
            if (FileAttr == FileAttributes.Directory)
                s += "+" + Name + "+";
            else
                s += "[" + Name + "]";
            s += ",";
            s += LastWriteTime.ToShortTimeString();
            s += ",";
            s += DownloadSize.ToString();
            return s;
        }
        private string name = "";
        /// <summary>
        /// 文件名
        /// </summary>
        public string Name
        {
            get { return name; }
            set {
                if (name != value)
                {
                    name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }


        private FileAttributes fileAttributes = FileAttributes.Normal;
        /// <summary>
        /// 文件属性，文件Normal or 文件夹Directory
        /// </summary>
        public FileAttributes FileAttr 
        {
            get {
                return fileAttributes;
            }
            set {
                if (fileAttributes != value)
                {
                    fileAttributes = value;
                    NotifyPropertyChanged("FileAttr");
                }
            }
        }

        private DateTime lastWriteTime = DateTime.Now;
        /// <summary>
        /// 最后修改时间
        /// </summary>
        public DateTime LastWriteTime 
        {
            get { return lastWriteTime; }
            set
            {
                if (lastWriteTime != value) {
                    lastWriteTime = value;

                    NotifyPropertyChanged("LastWriteTime");
                    BeChanged = true;
                }
            }
        }
        private DateTime createTime = DateTime.MinValue;
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime 
        {
            get { return createTime; }
            set {
                if (createTime != value)
                {
                    createTime = value;
                    NotifyPropertyChanged("CreateTime");
                    BeChanged = true;
                }
            }
        }
        private int size;
        public int Size 
        {
            get { return size; }
            set {
                if (size != value) 
                {
                    size = value;
                    NotifyPropertyChanged("Size");
                    BeChanged = true;
                }
            }
        }

        private bool update = false;
        /// <summary>
        /// 需要从服务器下载文件
        /// </summary>
        public bool Update 
        {
            get { return update; }
            set
            {
                if (update != value)
                {
                    update = value;

                    NotifyPropertyChanged("Update");
                    BeChanged = true;
                }
            }
        }

        private int download_size = 0;
        /// <summary>
        /// 已经下载了的大小
        /// </summary>
        public int DownloadSize 
        {
            get { return download_size; }
            set
            {
                if (download_size != value)
                {
                    download_size = value;
                    NotifyPropertyChanged("DownloadSize");
                }
            }
        }

        //public List<FileElement> child;
        public ObservableCollection<FileElement> child;
        public FileElement parent;

        private bool beChanged = false;
        public bool BeChanged
        {
            get { return beChanged; }
            set {
                if (beChanged != value)
                {
                    beChanged = value;
                    if (beChanged)
                    {
                        if (parent != null)
                            parent.BeChanged = true;
                    }
                    else 
                    {
                        if (child != null) 
                        {
                            foreach (FileElement f in child)
                                f.BeChanged = false;
                        }
                    }
                    NotifyPropertyChanged("BeChanged");
                }
            }
        }
        public event EventHandler OnChanged;//TODO, 当数据发生变化时，调用
        public class FileElementEventArgs : EventArgs 
        {
            public string Path;//更新路径
        }
        /// <summary>
        /// 总路径，不断向parent 查询
        /// </summary>
        public string FullName
        {
            get {
                if (parent != null)
                {
                    string name = parent.FullName;
                    if (name == @"\")
                    {
                        return @"\" + Name;
                    }
                    else
                    {
                        return parent.FullName + @"\" + Name;
                    }
                }
                else 
                {
                    return Name;
                }
            }
        }

        #region Load Save
        /// <summary>
        /// 从xmlnode 格式中提取 单层 FileElement 结构 加载到当前的 FileElement
        /// </summary>
        /// <param name="xmlNode"></param>
        /// <returns></returns>
        private bool Load(XmlNode xmlNode)
        {
            FileAttr = (FileAttributes)Enum.Parse(typeof(FileAttributes), xmlNode.Name);
            Name = xmlNode.Attributes["name"].Value;
            if (xmlNode.Attributes.GetNamedItem("ctime") != null)
            CreateTime = DateTime.Parse(xmlNode.Attributes["ctime"].Value);

            LastWriteTime = DateTime.Parse(xmlNode.Attributes["mtime"].Value);

            if (xmlNode.Attributes.GetNamedItem("size") != null)
                Size = int.Parse(xmlNode.Attributes["size"].Value);

            if (xmlNode.Attributes.GetNamedItem("update") != null)
            {
                Update = bool.Parse(xmlNode.Attributes["update"].Value);

                if (Update == false)
                    DownloadSize = Size;
            }

            if (FileAttr == FileAttributes.Directory)
            {
                child = new ObservableCollection<FileElement>();

                foreach (XmlNode xn in xmlNode.ChildNodes)
                {
                    FileElement element = new FileElement();
                    element.Load(xn);
                    AddChild(element);
                }
            }
            return true;
        }
        /// <summary>
        /// 保存 到 xml, level &lt;= 1, 只保存本层， &gt; 1, 连子层都保存
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <returns></returns>
        private XmlNode Save(XmlDocument xmlDoc,int level)
        {
            XmlElement xmlElement = xmlDoc.CreateElement(FileAttr.ToString());
            xmlElement.SetAttribute("name", Name);
            xmlElement.SetAttribute("ctime", Converter.DateTimeToString(CreateTime));
            xmlElement.SetAttribute("mtime", Converter.DateTimeToString(LastWriteTime));
            xmlElement.SetAttribute("size", Size.ToString());
            xmlElement.SetAttribute("update", Update.ToString());
            
            if (level > 1)
            {
                if (child != null)
                {
                    foreach (FileElement fe in child)
                    {
                        xmlElement.AppendChild(fe.Save(xmlDoc,level));
                    }
                }
            }
            return xmlElement;
        }


        /// <summary>
        /// 被 FileBus.Client 调用, 用于加载 Gage1 的 xxx.fileinfo.xml
        /// 加载 xml 文件，会自动填充本项 及 子项
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool Load(string path)
        {
            XmlDocument xmlDoc = new XmlDocument();
            try
            {
                xmlDoc.Load(path);
            }
            catch
            {
                return false;
            }
            return Load(xmlDoc.DocumentElement);
        }
        /// <summary>
        /// 被 FileBus.Client 调用
        /// 以当前项 与 子项 保存为 xml 文件
        /// </summary>
        /// <param name="path"></param>
        public void Save(string path)
        {
            XmlDocument xmlDoc = new XmlDocument();

            XmlNode node = Save(xmlDoc, 2);

            xmlDoc.AppendChild(node);

            xmlDoc.Save(path);
            
        }
        #endregion



        /// <summary>
        /// 添加单个子项
        /// </summary>
        /// <param name="fElement"></param>
        private void AddChild(FileElement fElement)
        {
            child.Add(fElement);
            fElement.parent = this;
            BeChanged = true;
        }
        /// <summary>
        /// 在指定路径创建 子 FileElement
        /// </summary>
        /// <param name="XPath"></param>
        /// <param name="fa"></param>
        /// <returns></returns>
        private FileElement CreateNode(string XPath, FileAttributes fa)
        {
            string[] parent_path = XPath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);

            if (parent_path.Length == 0)//根目录为 @"\"
            {
                return this;
            }

            FileElement fe = this;
            foreach(string name in parent_path)
            {
                FileElement parent = fe;

                var fel = from _fe in parent.child where _fe.Name == name select _fe;
                if (fel.Count() > 0)
                {
                    fe = fel.First();
                }
                else 
                {
                    fe = new FileElement(FileAttributes.Directory, name);
                    parent.AddChild(fe);
                    
                }
            }
            if(fa == FileAttributes.Normal)
                fe.child = null;
            
            fe.FileAttr = fa;
            fe.BeChanged = true;
            return fe;
        }

        /// <summary>
        /// 被 FileBus.Client 调用
        /// 根据 XPath，获取 FileElement 树
        /// </summary>
        /// <param name="XPath"></param>
        /// <returns></returns>
        public FileElement SelectSingleNode(string XPath)
        {
            string[] parent = XPath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);

            if (parent.Length == 0)//根目录为 @"\"
            {
                return this;
            }

            FileElement fe = this;
            foreach (string name in parent)
            {
                var fel = from _fe in fe.child where _fe.Name == name select _fe;
                if (fel.Count() > 0)
                {
                    fe = fel.First();
                }
                else 
                {
                    return null;
                }
            }
            return fe;
        }
        #region 设定Update 标志位
        /// <summary>
        /// 被 FileBus.Client 调用;
        /// 设置 路径 的 update = b
        /// </summary>
        /// <param name="XPath"></param>
        public bool SetUpdateFlag(string XPath, bool b)
        {
            //TODO
            FileElement fe = SelectSingleNode(XPath);
            if (fe != null)
            {
                fe.Update = b;
            }
            return b;
        }
        /// <summary>
        /// 当时间不一样时，update=true
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public bool SetUpdateFlag(DateTime dt)
        {
            if (LastWriteTime != dt)
            {
                this.LastWriteTime = dt;
                this.Update = true;
            }
            return this.Update;
        }
        /// <summary>
        /// 从FileElement比较 时间
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        private bool SetUpdateFlag(FileElement element) 
        {
            CreateTime = element.CreateTime;

            if (LastWriteTime.ToString() != element.LastWriteTime.ToString())
            {
                Update = true;
            }
            LastWriteTime = element.LastWriteTime;
           
            if (Size != element.Size)
            {
                DownloadSize = element.DownloadSize;
            }
            Size = element.Size;

            if (DownloadSize < element.DownloadSize)
                DownloadSize = element.DownloadSize;



            return this.Update;
        }
        #endregion
        #region 更新

        /// <summary>
        /// 被 FileBus.Client 调用;
        /// 把element 更新到 指定的 路径;
        /// 当 hasChild=true 时，更新 element的子 element;
        /// 当 修改时间不一致时， 设置 update = true
        /// </summary>
        /// <param name="XPath">路径</param>
        /// <param name="fElement">源FileElement</param>
        /// <param name="needUpdateChild">需要更新child</param>
        /// <returns></returns>
        public FileElement ToUpdate(string XPath, FileElement element, bool needUpdateChild) 
        {
            FileElement fe = SelectSingleNode(XPath);
            if(fe==null)
            {
                //没有该路径，需要创造
                fe = CreateNode( XPath, element.FileAttr );
            }
            fe.ToUpdate(element, needUpdateChild);
            return fe;
        }

        /// <summary>
        /// 判断子文件 当前修改时间是否与对象fElements 一致，
        /// 1.不一样则复制fElement的修改时间，且 标志为“需要更新”
        /// 2.当fElements有，但child 没的元素，child 添加且 标志为“需要更新”
        /// 3.当fElements没，但child 有的元素，child 删除且把被删除的输出del_Elements
        /// </summary>
        /// <param name="fElements">匹配的</param>
        /// <param name="del_Elements">被删除的</param>
        /// <returns></returns>
        //public bool ToUpdate(IEnumerable<FileElement> fElements, out List<FileElement> del_Elements)
        //{
        //    del_Elements = null;
        //    if (FileAttr != FileAttributes.Directory)
        //        return false;

        //    //添加
        //    foreach (FileElement fe in fElements)
        //    {
        //        var fel = from _fe in child where _fe.Name == fe.Name select _fe;
        //        if (fel.Count() > 0)
        //        {
        //            FileElement f = fel.First();
        //            f.SetUpdateFlag(fe);
        //        }
        //        else
        //        {
        //            //添加到本地
        //            FileElement f = new FileElement(fe.FileAttr, fe.Name)
        //            {
        //                LastWriteTime = fe.LastWriteTime,
        //                CreateTime = fe.CreateTime,
        //                Size = fe.Size
        //            };
        //            AddChild(f);
        //            f.SetUpdateFlag(fe);
        //        }
        //    }

        //    //删除
        //    List<FileElement> del = new List<FileElement>();
        //    {
        //        var fel = from _fe in child
        //                  where
        //                      (from _fe1 in fElements where _fe1.Name == _fe.Name select _fe1).Count() == 0
        //                  select _fe;

        //        del.AddRange(fel);
        //        for (int i = 0; i < fel.Count(); i++)
        //        {
        //            child.Remove(fel.ElementAt(i));
        //        }
        //    }
        //    return true;

        //}

        /// <summary>
        /// 判断当前修改时间是否与源FileElement 一致，不一样则
        /// 复制fElement的修改时间，且 标志为需要更新
        /// </summary>
        /// <param name="fElement">源FileElement</param>
        /// <param name="needUpdateChild">需要更新child</param>
        /// <returns></returns>
        public bool ToUpdate(FileElement fElement, bool needUpdateChild)
        {
            bool ret = SetUpdateFlag(fElement);
            if (needUpdateChild)
            {
                if (fElement.child == null)
                {
                    if (child != null)
                    {
                        child.Clear();
                    }
                }
                else
                {
                    //添加
                    foreach (FileElement fe in fElement.child)
                    {
                        var fel = from _fe in child where _fe.Name == fe.Name select _fe;
                        if (fel.Count() > 0)
                        {
                            FileElement f = fel.First();
                            f.SetUpdateFlag(fe);
                        }
                        else
                        {
                            //添加到本地
                            FileElement f = new FileElement(fe.FileAttr, fe.Name)
                            {
                                LastWriteTime = fe.LastWriteTime,
                                CreateTime = fe.CreateTime,
                                Size = fe.Size
                            };
                            AddChild(f);
                        }
                    }
                    
                    //删除多余！！！！
                    for (int i = 0; i < child.Count(); i++)
                    {
                        FileElement fe = child[i];
                        var fel = from _fe in fElement.child where _fe.Name == fe.Name select _fe;
                        if (fel.Count() == 0)
                        {
                            //不存在，删除
                            child.Remove(fe);
                            i--;
                        }
                    }
                }
            }
            return ret;
        }
        #endregion
        #region 创建FileElement
        public FileElement()
        {

        }

        //被 FileBus.Client 调用 new FileElement(FileAttributes.Directory, @"\");
        public FileElement(FileAttributes fa, string name)
        {
            this.Name = name;
            if (fa != FileAttributes.Directory && fa != FileAttributes.Normal)
                fa = FileAttributes.Directory;

            FileAttr = fa;
            LastWriteTime = DateTime.Now;
            Update = true;
            if (fa == FileAttributes.Directory)
            {
                //child = new List<FileElement>();
                child = new ObservableCollection<FileElement>();
            }
        }


        /// <summary>
        /// 被 FileBus.Client 调用
        /// 把格式为 “sysparam.ini F 2015/4/20 10:55:20” 字符串 转为单个FileElement 
        /// </summary>
        /// <param name="fileinfo"></param>
        /// <returns></returns>
        //public static FileElement Parse(string fileinfo) 
        //{
        //    //格式 sysparam.ini F 2015/4/20 10:55:20
        //    string[] blocks = fileinfo.Split(' ');
        //    string path=blocks[0];
        //    FileAttributes fa;
        //    if(blocks[1]=="F")
        //        fa=FileAttributes.Normal;
        //    else// if(blocks[1]=="D")
        //        fa=FileAttributes.Directory;
        //    DateTime mtime = DateTime.Parse(blocks[2] + " " + blocks[3]);

        //    FileElement fe = new FileElement(fa, path);
        //    fe.LastWriteTime = mtime;
        //    return fe;
        //}

        #endregion

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

        #endregion
    }
}
