using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace SystemX.Net.Comm.IIS_FTP
{
    public class CtrlFTP : ManagerInfoFTP
    {
        public CtrlFTP(bool bUseService, IPAddress SetIpAddress, int nSetPort, string strSetUserName, string strSetUserPassword) 
            : base (bUseService, SetIpAddress, nSetPort, strSetUserName, strSetUserPassword)
        {
            /*
            "14.33.116.123", "2121"
            "ALISFTP", "Kefico!@34"
            */

            if (Connect())
                FTPConnState = true;
            else
                FTPConnState = false;

            string strGetLastCommandState = LastestCommandDebugInformation();
        }

        public override string GetLastestCommandStatusDescriptionText()
        {
            return LastestCommandStatusDescription;
        }

        public override FtpStatusCode GetLastestCommandStatusCodeText()
        {
            return LastestCommandStatusCode;
        }

        public override string GetLastestExceptionText()
        {
            return LastestCommandException.Message;
        }

        public override Exception GetLastestException()
        {
            return LastestCommandException;
        }

        public bool Connect()
        {
            LastestCommandException = null;

            bool bCommandResult = true;

            string url = string.Format(@"FTP://{0}:{1}/", UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            FtpWebRequest fwr = (FtpWebRequest)WebRequest.Create(url);
            fwr.KeepAlive = false;
            //fwr.UseBinary = true;
            fwr.UsePassive = true;

            fwr.Method = WebRequestMethods.Ftp.ListDirectory;
            fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

            StringBuilder sb = new StringBuilder();

            FtpWebResponse wr = null;
            try
            {
                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;
                }
            }
            catch(Exception FtpException)
            {
                bCommandResult = false;

                LastestCommandException = FtpException;
            }
            finally
            {
                if(wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            return bCommandResult;
        }

        private List<string> CheckList(StringBuilder sb, bool bFolderCheck = false, string strFolderPath = "")
        {
            List<string> lstInfo = new List<string>();
            string[] directorys = sb.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string strList in directorys)
            {
                string[] strGetItems = new string[4];

                int[] nSplitPos = new int[3];
                string strCopyText = strList;
                for (int n = 0; n < 3; n++)
                {
                    int nPos = strCopyText.IndexOf(' ');
                    int nOriginPosition = nPos;
                    int nCnt = 0;
                    while (strCopyText[nPos] == ' ')
                    {
                        nPos++;
                        nCnt++;
                    }

                    string strTempText = strCopyText.Substring(0, nOriginPosition);
                    strCopyText = strCopyText.Remove(0, nOriginPosition + nCnt);

                    strGetItems[n] = strTempText;
                }

                strGetItems[3] = strCopyText;

                //string[] strGetItems = strList.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                if (strGetItems.Length != 4)
                    continue;
                if (bFolderCheck)
                {
                    if (strGetItems[2] != "<DIR>")
                        continue;

                    if (strFolderPath.Length == 0)
                        lstInfo.Add(strGetItems[3]);
                    else
                    {
                        if (strFolderPath.CompareTo(strGetItems[3]) == 0)
                        {
                            lstInfo.Add(strGetItems[3]);
                            break;
                        }
                    }
                }
                else
                {
                    if (strGetItems[2] == "<DIR>")
                        continue;

                    DateTime dt = new DateTime();

                    if (DateTime.TryParse(strGetItems[0] + " " + strGetItems[1], CultureInfo.GetCultureInfo("en-US"), DateTimeStyles.None, out dt))
                    {
                        TimeSpan ts = DateTime.Now - dt;

                        if (ts.TotalSeconds >= ManagerInfoFTP.dDiffScanCheckFileTime)
                            lstInfo.Add(strGetItems[3]);

                        // TODO : FTP Download file list chekc limit 2048
                        if (lstInfo.Count >= 2048)
                            break;
                    }
                    else //Parsing failed!
                        continue;
                }
            }

            return lstInfo;
        }

        public List<string> PositionRootCheckList(bool bFolderCheck = false, string strFolderPath = "")
        {
            LastestCommandException = null;

            bool bCommandResult = true;

            List<string> lstInfo = new List<string>();
            string url = string.Format(@"FTP://{0}:{1}/", UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            FtpWebRequest fwr = (FtpWebRequest)WebRequest.Create(url);
            fwr.KeepAlive = false;
            //fwr.UseBinary = true;
            fwr.UsePassive = true;

            fwr.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

            StringBuilder sb = new StringBuilder();

            FtpWebResponse wr = null;
            try
            {
                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    StreamReader streamReader = new StreamReader(wr.GetResponseStream(), Encoding.Default);
                    sb.Append(streamReader.ReadToEnd());
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            if (bCommandResult)
                lstInfo = CheckList(sb, bFolderCheck, strFolderPath);

            return lstInfo;
        }

        public List<string> PositionSubCheckList(string strSubPath, bool bFolderCheck = false, string strFolderPath = "")
        {
            LastestCommandException = null;

            bool bCommandResult = true;

            List<string> lstInfo = new List<string>();
            string url = string.Format(@"FTP://{0}:{1}/" + strSubPath + @"/", UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            FtpWebRequest fwr = (FtpWebRequest)WebRequest.Create(url);
            fwr.KeepAlive = false;
            //fwr.UseBinary = true;
            fwr.UsePassive = true;

            fwr.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

            StringBuilder sb = new StringBuilder();

            FtpWebResponse wr = null;
            try
            {
                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    StreamReader streamReader = new StreamReader(wr.GetResponseStream(), Encoding.Default);
                    sb.Append(streamReader.ReadToEnd());
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            if (bCommandResult)
                lstInfo = CheckList(sb, bFolderCheck, strFolderPath);

            return lstInfo;
        }

        public bool MakeDirectory(string strDirectoryPath)
        {
            LastestCommandException = null;

            bool bCommandResult = true;

            string url = string.Format(@"FTP://{0}:{1}/" + strDirectoryPath + @"/", UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            FtpWebRequest fwr = null;
            FtpWebResponse wr = null;

            try
            {
                fwr = (FtpWebRequest)WebRequest.Create(url);
                fwr.KeepAlive = false;
                //fwr.UseBinary = true;
                fwr.UsePassive = true;
                
                fwr.Method = WebRequestMethods.Ftp.MakeDirectory;
                fwr.Credentials = new NetworkCredential("ALISFTP", "Kefico!@34");

                StringBuilder sb = new StringBuilder();

                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    // FTP 결과 스트림
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    if (LastestCommandStatusCode != System.Net.FtpStatusCode.PathnameCreated)
                        bCommandResult = false;
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            return bCommandResult;
        }

        public bool FileExist(string strFileName)
        {
            LastestCommandException = null;

            bool bCommandResult = true;

            string url = string.Format(@"FTP://{0}:{1}/" + strFileName, UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            FtpWebRequest fwr = null;
            FtpWebResponse wr = null;

            try
            {
                fwr = (FtpWebRequest)WebRequest.Create(url);
                fwr.KeepAlive = false;
                //fwr.UseBinary = true;
                fwr.UsePassive = true;
                fwr.Method = WebRequestMethods.Ftp.GetFileSize;
                fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

                StringBuilder sb = new StringBuilder();

                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    if (LastestCommandStatusCode == System.Net.FtpStatusCode.ActionNotTakenFileUnavailable)
                        bCommandResult = false;
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            return bCommandResult;
        }

        public bool DeleteFile(string strFileName)
        {
            LastestCommandException = null;

            bool bCommandResult = true;

            string url = string.Format(@"FTP://{0}:{1}/" + strFileName, UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            FtpWebRequest fwr = null;
            FtpWebResponse wr = null;

            try
            {
                fwr = (FtpWebRequest)WebRequest.Create(url);
                fwr.KeepAlive = false;
                //fwr.UseBinary = true;
                fwr.UsePassive = true;
                fwr.Method = WebRequestMethods.Ftp.DeleteFile;
                fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

                StringBuilder sb = new StringBuilder();

                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    if (LastestCommandStatusCode != System.Net.FtpStatusCode.FileActionOK)
                        bCommandResult = false;
                    else
                    {
                        if (LastestCommandStatusDescription.IndexOf("DELE command successful") < 0)
                            bCommandResult = false;
                    }
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            return bCommandResult;
        }

        public bool FileUpload(string strFilePos)
        {
            string strFileName = Path.GetFileName(strFilePos);

            string url = string.Format(@"FTP://{0}:{1}/" + strFileName, UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            LastestCommandException = null;

            bool bCommandResult = true;

            FtpWebRequest fwr = null;
            FtpWebResponse wr = null;

            try
            {
                fwr = (FtpWebRequest)WebRequest.Create(url);
                fwr.KeepAlive = false;
                fwr.UseBinary = true;
                fwr.UsePassive = true;
                fwr.Method = WebRequestMethods.Ftp.UploadFile;
                fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

                // 입력파일을 바이트 배열로 읽음
                byte[] ucReadData;
                using (FileStream reader = new FileStream(strFilePos, FileMode.Open, FileAccess.Read))
                {
                    ucReadData = new byte[reader.Length];
                    reader.Read(ucReadData, 0, (int)reader.Length);
                }

                // RequestStream에 데이타를 쓴다
                fwr.ContentLength = ucReadData.Length;
                using (Stream reqStream = fwr.GetRequestStream())
                {
                    reqStream.Write(ucReadData, 0, ucReadData.Length);
                }

                // FTP Upload 실행
                using (wr = (FtpWebResponse)fwr.GetResponse())
                {
                    // FTP 결과 상태 출력
                    //Console.WriteLine("Upload: {0}", wr.StatusDescription);
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    if (LastestCommandStatusDescription.IndexOf("Transfer complete") >= 0)
                        bCommandResult = true;
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            return bCommandResult;
        }

        public async Task<string> FileDownload(string strDownloadFileName, string strCreateFilePath)
        {
            string url = string.Format(@"FTP://{0}:{1}/" + strDownloadFileName, UseInfo.InfoIPAddress.ToString(), UseInfo.InfoPort.ToString());

            LastestCommandException = null;

            bool bCommandResult = true;

            FtpWebRequest fwr = null;
            FtpWebResponse wr = null;

            try
            {
                fwr = (FtpWebRequest)WebRequest.Create(url);
                fwr.KeepAlive = false;
                fwr.UseBinary = true;
                fwr.UsePassive = true;
                fwr.Method = WebRequestMethods.Ftp.DownloadFile;
                fwr.Credentials = new NetworkCredential(UseInfo.InfoUseUserAccount, UseInfo.InfoUseUserPassword);

                // FTP Request 결과를 가져온다.
                using (wr = (FtpWebResponse)fwr.GetResponseAsync().ConfigureAwait(false).GetAwaiter().GetResult())
                {
                    // FTP 결과 스트림
                    LastestCommandStatusDescription = wr.StatusDescription;
                    LastestCommandStatusCode = wr.StatusCode;

                    Stopwatch stWaitTime = new Stopwatch();
                    stWaitTime.Start();
                    while (true)
                    {
                        LastestCommandStatusDescription = wr.StatusDescription;
                        LastestCommandStatusCode = wr.StatusCode;

                        if (stWaitTime.ElapsedMilliseconds >= 10000)
                            throw new Exception("Can't FtpStatusCode.DataAlreadyOpen|FtpStatusCode.ClosingData");

                        if (LastestCommandStatusCode == System.Net.FtpStatusCode.DataAlreadyOpen ||
                            LastestCommandStatusCode == System.Net.FtpStatusCode.ClosingData)
                            break;

                        await Task.Delay(0);
                    }

                    Stream stream = wr.GetResponseStream();
                    FileStream fileStream = new FileStream(strCreateFilePath + strDownloadFileName, FileMode.Create);

                    byte[] ucBuffer = new byte[4096];
                    int nBytesRead;
                    while (true)
                    {
                        nBytesRead = stream.Read(ucBuffer, 0, ucBuffer.Length);

                        if (nBytesRead == 0)
                            break;

                        fileStream.Write(ucBuffer, 0, nBytesRead);
                    }
                    fileStream.Close();
                }
            }
            catch (Exception FtpException)
            {
                LastestCommandException = FtpException;

                bCommandResult = false;
            }
            finally
            {
                if (wr != null)
                {
                    wr.Close();
                    wr = null;
                }
            }

            if (bCommandResult)
                return strCreateFilePath + strDownloadFileName;
            else
                return string.Empty;
        }
    }
}
