using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace JWH.NETWORK
{

    public class FtpClient
    {

        #region [ Properties ] ------------------------------------------------

        public string URI { get; set; }

        public string UID { get; set; }

        public string PWD { get; set; }

        #endregion

        #region [ FtpClient ] -------------------------------------------------

        public FtpClient()
        {
        }

        public FtpClient(string uri, string uid, string pwd)
        {
            this.URI = uri;
            this.UID = uid;
            this.PWD = pwd;
        }

        #endregion

        #region [ Method ] ----------------------------------------------------

        private FtpWebRequest GetFtpWebRequest(string path)
        {
            try
            {
                FtpWebRequest ftpRequest = WebRequest.Create(path) as FtpWebRequest;
                ftpRequest.Timeout = 3000;
                if (string.IsNullOrEmpty(this.UID)) ftpRequest.Credentials = new NetworkCredential();
                else ftpRequest.Credentials = new NetworkCredential(this.UID, this.PWD);

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

        public string[] GetList(string path = "")
        {
            StreamReader reader = null;
            try
            {
                List<string> lstFile = new List<string>();

                string fullPath = $"{this.URI}{path}";
                FtpWebRequest ftpRequest = this.GetFtpWebRequest(fullPath);
                //ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
                ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                
                WebResponse response = ftpRequest.GetResponse();
                reader = new StreamReader(response.GetResponseStream());
                string fileInfo = reader.ReadLine();
                while (string.IsNullOrEmpty(fileInfo) == false)
                {
                    object obj = this.CreateFtpObject(fileInfo);
                    lstFile.Add(fileInfo);
                    fileInfo = reader.ReadLine();
                }

                return lstFile.ToArray();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (reader != null) reader.Close();
            }
        }

        public FtpObject[] GetFtpObjects(string path = "")
        {
            StreamReader reader = null;
            string fullPath = $"{this.URI}{path}";
            try
            {
                List<FtpObject> lstObject = new List<FtpObject>();

                FtpWebRequest ftpRequest = this.GetFtpWebRequest(fullPath);
                //ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
                ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

                WebResponse response = ftpRequest.GetResponse();
                reader = new StreamReader(response.GetResponseStream());
                string fileInfo = reader.ReadLine();
                while (string.IsNullOrEmpty(fileInfo) == false)
                {
                    FtpObject obj = this.CreateFtpObject(fileInfo);
                    lstObject.Add(obj);
                    fileInfo = reader.ReadLine();
                }

                return lstObject.ToArray();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (reader != null) reader.Close();
            }
        }

        public bool Download(string path, string localpath)
        {
            FileStream writer = null;
            Stream reader = null;
            try
            {
                string fullPath = $"{this.URI}{path}";
                FtpWebRequest ftpRequest = this.GetFtpWebRequest(fullPath);
                ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;

                WebResponse response = ftpRequest.GetResponse();
                reader = response.GetResponseStream();

                if (System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(localpath)) == false)
                    System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(localpath));
                writer = new FileStream(localpath, FileMode.Create, FileAccess.Write);
                byte[] byteBuffer = new byte[1024];

                int byteSize = reader.Read(byteBuffer, 0, byteBuffer.Length);
                while (byteSize > 0)
                {
                    writer.Write(byteBuffer, 0, byteSize);
                    byteSize = reader.Read(byteBuffer, 0, byteBuffer.Length);
                }

                return true;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Error($"{this.URI}{path} --> {localpath}");
                throw ex;
            }
            finally
            {
                if (writer != null) writer.Close();
                if (reader != null) reader.Close();
            }
        }

        #endregion

        public FtpObject CreateFtpObject(string value)
        {
            try
            {
                string strDateTime = value.Substring(0, 17).Trim();
                string strType = value.Substring(18, 11).Trim();
                string strLength = value.Substring(30, 8).Trim();
                string strName = value.Substring(39).Trim();

                DateTime dtCreate = DateTime.MinValue;
                DateTime.TryParse(strDateTime, out dtCreate);
                long nLength = 0;
                long.TryParse(strLength, out nLength);

                FtpObject result = null;
                if (string.Compare(strType, "<DIR>", true) == 0)
                {
                    FtpDirectory ftpDirectory = new FtpDirectory();
                    ftpDirectory.Name = strName;
                    ftpDirectory.CreateDateTime = dtCreate;
                    result = ftpDirectory;
                }
                else
                {
                    FtpFile ftpFile = new FtpFile();
                    ftpFile.Name = strName;
                    ftpFile.Length = nLength;
                    ftpFile.CreateDateTime = dtCreate;
                    result = ftpFile;
                }

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

    }

    public class FtpObject
    {

        public string Name { get; set; } = string.Empty;

        public string Path { get; set; } = string.Empty;

        public string FullName { get { return $"{this.Path}/{this.Name}"; } }

        public DateTime CreateDateTime { get; set; } = DateTime.MinValue;

        public TreeNode GetTreeNode()
        {
            TreeNode node = null;
            node = new TreeNode(this.Name, 0, 0);
            node.Tag = this;

            if (this.GetType() == typeof(FtpDirectory))
            {
                node.ImageIndex = 1;
                FtpDirectory ftpDirectory = this as FtpDirectory;
                foreach(FtpObject obj in ftpDirectory.Childs )
                {
                    node.Nodes.Add(obj.GetTreeNode());
                }
            }

            return node;
        }

        public void Refresh(FtpClient ftpClient, int depth = 0)
        {
            try
            {
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }


    public class FtpDirectory : FtpObject
    {

        public List<FtpObject> Childs { get; set; } = new List<FtpObject>();

        public FtpObject[] GetChilds(FtpClient ftpClient, int depth = 0)
        {
            try
            {
                List<FtpObject> lstChild = new List<FtpObject>();
                if (depth < 0) return lstChild.ToArray();

                foreach (FtpObject obj in ftpClient.GetFtpObjects(this.FullName))
                {
                    obj.Path = this.FullName;
                    lstChild.Add(obj);
                    FtpDirectory ftpDirectory = obj as FtpDirectory;
                    if (ftpDirectory != null && depth > 0)
                        ftpDirectory.Childs.AddRange(this.GetChilds(ftpClient, depth - 1));
                }

                this.Childs.Clear();
                this.Childs.AddRange(lstChild.ToArray());

                return this.Childs.ToArray();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }

    public class FtpFile : FtpObject
    {

        public long Length { get; set; } = 0;

        public FtpFile()
        {
        }

        public FtpFile(string name, long length)
        {
            this.Name = name;
            this.Length = length;
        }

    }

}
