using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace NewPosInstaller
{
    /// <summary>
    /// FTP를 위한 유틸 클래스입니다.
    /// </summary>
    public class FTP
    {
        #region 멤버 변수 & 프로퍼티
        /// <summary>
        /// 
        /// </summary>
        private string host;

        /// <summary>
        /// FTP 서버 호스트명(IP)를 가져옵니다.
        /// </summary>
        public string Host
        {
            get { return host; }
            private set { host = value; }
        }

        private string userName;

        /// <summary>
        /// 사용자 명을 가져옵니다.
        /// </summary>
        public string UserName
        {
            get { return userName; }
            private set { userName = value; }
        }

        private string password;
        /// <summary>
        /// 비밀번호를 가져옵니다.
        /// </summary>
        public string Password
        {
            get { return password; }
            private set { password = value; }
        }
        #endregion

        #region 생성자
        /// <summary>
        /// FXFtpUtil의 새 인스턴스를 생성합니다.
        /// </summary>
        /// <param name="host">FTP 서버 주소 입니다.</param>
        /// <param name="userName">FTP 사용자 아이디 입니다.</param>
        /// <param name="password">FTP 비밀번호 입니다.</param>
        #region public FTP(string host, string userName, string password)
        public FTP(string host, string userName, string password)
        {
            this.Host = host;
            this.UserName = userName;
            this.Password = password;
        }
        #endregion

        #endregion

        #region 메서드

        /// <summary>
        /// FTP 서버의 파일을 Delete 합니다.
        /// </summary>
        /// <param name="ftpFilePath">파일을 Delete 할 FTP 전체 경로 입니다.</param>
        /// <returns></returns>
        #region public bool DeleteFile(string ftpFilePath)
        public bool DeleteFile(string ftpFilePath)
        {
            bool bRet = false;
            string ftpFileFullPath = string.Format("ftp://{0}/{1}", this.Host, ftpFilePath);
            FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFileFullPath)) as FtpWebRequest;
            FtpWebResponse ftpResponse = null;
            try
            {
                ftpWebRequest.Credentials = GetCredentials();
                ftpWebRequest.UseBinary = true;
                ftpWebRequest.UsePassive = true;
                ftpWebRequest.KeepAlive = true;
                ftpWebRequest.Method = WebRequestMethods.Ftp.DeleteFile;
                ftpResponse = (FtpWebResponse)ftpWebRequest.GetResponse();

                ftpResponse.Close();
                bRet = true;
            }
            catch (WebException ex)
            {
                ftpResponse = ex.Response as FtpWebResponse;
                if (ftpResponse.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable) bRet = false;
                else bRet = true;
                /* Resource Cleanup */
                //localFileStream.Close();
                //ftpResponse.Close();
                //ftpWebRequest = null;
                return bRet;
            }
            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                    "NetworkFtp.Download()", "FTP.Download EXCEPTION=[" + ex.Message);

                ///* Resource Cleanup */
                //localFileStream.Close();
                //if( ftpStream != null) ftpStream.Close();
                //if (ftpResponse != null) ftpResponse.Close();
                //ftpWebRequest = null;

                return bRet = false;
            }

            return bRet;
        }
        #endregion

        /// <summary>
        /// 파일을 FTP 서버에 Download 합니다.
        /// </summary>
        /// <param name="localFileFullPath">로컬 파일의 전체 경로 입니다.</param>
        /// <param name="ftpFilePath"><para>파일을 Download 할 FTP 전체 경로 입니다.</para><para>하위 디렉터리에 넣는 경우 /디렉터리명/파일명.확장자 형태 입니다.</para></param>
        /// <exception cref="FileNotFoundException">지정한 로컬 파일(localFileFullPath)이 없을 때 발생합니다.</exception>
        /// <returns>Download 성공 여부입니다.</returns>
        #region public bool Download(string ftpFilePath, string localFileFullPath)
        public bool Download(string ftpFilePath, string localFileFullPath)
        {
            string sMsg = "";
            return Download(ftpFilePath, localFileFullPath, ref sMsg);
        }
        public bool Download(string ftpFilePath, string localFileFullPath, ref string sMsg)
        {
            bool bRet = false;
            string ftpFileFullPath = string.Format("ftp://{0}/{1}", this.Host, ftpFilePath);
            //string ftpFileFullPath = ftpFilePath;
            FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFileFullPath)) as FtpWebRequest;
            FtpWebResponse ftpResponse = null;
            Stream ftpStream = null;
            sMsg = "";
            FileStream localFileStream = null;

            try
            {
                int bufferSize = 8192;

                ftpWebRequest.Credentials = GetCredentials();
                ftpWebRequest.UseBinary = true;
                ftpWebRequest.UsePassive = true;
                ftpWebRequest.KeepAlive = true;
                ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
                ftpResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
                long svrFileSize = ftpResponse.ContentLength;
                ftpStream = ftpResponse.GetResponseStream();

                /* Buffer for the Downloaded Data */
                byte[] byteBuffer = new byte[bufferSize];
                int bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
                /* Download the File by Writing the Buffered Data Until the Transfer is Complete */

                /* Open a File Stream to Write the Downloaded File */
                localFileStream = new FileStream(localFileFullPath, FileMode.Create);

                while (bytesRead > 0)
                {
                    localFileStream.Write(byteBuffer, 0, bytesRead);
                    bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
                }
                
                if (localFileStream.Length == svrFileSize) bRet = true;
                else
                {
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                        "NetworkFtp.Download()", "File sise error SvrFileSize=[" + svrFileSize.ToString() + "], DownFileSize=[" + localFileStream.Length.ToString() + "]");

                    sMsg = "File sise error SvrFileSize=[" + svrFileSize.ToString() + "], DownFileSize=[" + localFileStream.Length.ToString() + "]";

                    bRet = false;
                }
            }
            catch (WebException ex)
            {
                ftpResponse = ex.Response as FtpWebResponse;
                if (ftpResponse.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable) bRet = false;
                else bRet = true;
                /* Resource Cleanup */
                //localFileStream.Close();
                //ftpResponse.Close();
                //ftpWebRequest = null;
                sMsg = ex.Message;
                return bRet;
            }
            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                    "NetworkFtp.Download()", "FTP.Download EXCEPTION=[" + ex.Message);

                ///* Resource Cleanup */
                //localFileStream.Close();
                //if( ftpStream != null) ftpStream.Close();
                //if (ftpResponse != null) ftpResponse.Close();
                //ftpWebRequest = null;

                sMsg = ex.Message;
                return bRet = false;
            }
            finally
            {
                /* Resource Cleanup */
                localFileStream.Close();
                if (ftpStream != null) ftpStream.Close();
                if (ftpResponse != null) ftpResponse.Close();
                ftpWebRequest = null;

                if (!bRet && File.Exists(localFileFullPath)) File.Delete(localFileFullPath); //정상수신 실패시, 다운로드중이던 파일 삭제
            }

            ///* Resource Cleanup */
            //localFileStream.Close();
            //ftpStream.Close();
            //ftpResponse.Close();
            //ftpWebRequest = null;
            //if (!bRet && File.Exists(localFileFullPath)) File.Delete(localFileFullPath); //정상수신 실패시, 다운로드중이던 파일 삭제
            return bRet;
        }
        #endregion


        /// <summary>
        /// 파일을 FTP 서버에 업로드 합니다.
        /// </summary>
        /// <param name="localFileFullPath">로컬 파일의 전체 경로 입니다.</param>
        /// <param name="ftpFilePath"><para>파일을 업로드 할 FTP 전체 경로 입니다.</para><para>하위 디렉터리에 넣는 경우 /디렉터리명/파일명.확장자 형태 입니다.</para></param>
        /// <exception cref="FileNotFoundException">지정한 로컬 파일(localFileFullPath)이 없을 때 발생합니다.</exception>
        /// <returns>업로드 성공 여부입니다.</returns>
        #region public bool Upload(string localFileFullPath, string ftpFilePath)
        public bool Upload(string localFileFullPath, string ftpFilePath)
        {
            LocalFileValidationCheck(localFileFullPath);

            //FTPDirectioryCheck(GetDirectoryPath(ftpFilePath));
            //if (IsFTPFileExsit(ftpFilePath))
            //{
            //    throw new ApplicationException(string.Format("{0}은 이미 존재하는 파일 입니다.", ftpFilePath));
            //}

            string ftpFileFullPath = string.Format("ftp://{0}/{1}", this.Host, ftpFilePath);
            FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFileFullPath)) as FtpWebRequest;
            ftpWebRequest.Credentials = GetCredentials();
            ftpWebRequest.UseBinary = true;
            ftpWebRequest.UsePassive = true;
            ftpWebRequest.Timeout = 10000;
            ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile;

            FileInfo fileInfo = new FileInfo(localFileFullPath);
            FileStream fileStream = fileInfo.OpenRead();
            Stream stream = null;
            byte[] buf = new byte[2048];
            int currentOffset = 0;
            try
            {
                stream = ftpWebRequest.GetRequestStream();
                currentOffset = fileStream.Read(buf, 0, 2048);
                while (currentOffset != 0)
                {
                    stream.Write(buf, 0, currentOffset);
                    currentOffset = fileStream.Read(buf, 0, 2048);
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                    "NetworkFtp.Upload()", "FTP.Upload EXCEPTION=[" + ex.Message);
                return false;
            }
            finally
            {
                fileStream.Dispose();
                if (stream != null)
                    stream.Dispose();
            }

            return true;
        }
        #endregion

        /// <summary>
        /// 해당 경로에 파일이 존재하는지 여부를 가져옵니다.
        /// </summary>
        /// <param name="ftpFilePath">파일 경로</param>
        /// <returns>존재시 참 </returns>
        #region private bool FTPFileExsit(string ftpFilePath)
        public bool FTPFileExsit(string ftpFilePath)
        {
            bool bExisit = false;
            string fileName = GetFileName(ftpFilePath);
            string ftpFileFullPath = string.Format("ftp://{0}{1}", this.Host, GetDirectoryPath(ftpFilePath));
            FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFileFullPath)) as FtpWebRequest;
            ftpWebRequest.Credentials = new NetworkCredential(this.UserName, this.Password);
            ftpWebRequest.UseBinary = true;
            ftpWebRequest.UsePassive = true;
            ftpWebRequest.Timeout = 10000;
            ftpWebRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

            FtpWebResponse response = null;
            string data = string.Empty;

            try
            {
                response = ftpWebRequest.GetResponse() as FtpWebResponse;
                if (response != null)
                {
                    StreamReader streamReader = new StreamReader(response.GetResponseStream(), Encoding.Default);
                    data = streamReader.ReadToEnd();
                }
            }
            catch (WebException ex)
            {
                response = ex.Response as FtpWebResponse;
                if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailableOrBusy) bExisit = false;
                else bExisit = true;
            }
            finally
            {
                if (response != null)
                    response.Close();
            }

            bExisit = false;
            string CRLF = string.Format("{0}", (char)0x0a);
            string[] directorys = data.Split(new string[] { CRLF }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string directory in directorys)
            {
                string[] str = directory.Replace(((char)0x0d).ToString(), "").Split(new char[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);
                if ((str.Length > 8) && (str[0].Substring(0, 1) != "d") && (str[8] == fileName))
                {
                    bExisit = true;
                    break;
                }
            }

            return bExisit;
        }
        #endregion


        /// <summary>
        /// FTP 풀 경로에서 Directory 경로만 가져옵니다.
        /// </summary>
        /// <param name="ftpFilePath">FTP 풀 경로</param>
        /// <returns>디렉터리 경로입니다.</returns>
        #region private string GetDirectoryPath(string ftpFilePath)
        private string GetDirectoryPath(string ftpFilePath)
        {
            string[] datas = ftpFilePath.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            string directoryPath = string.Empty;

            for (int i = 0; i < datas.Length - 1; i++)
            {
                directoryPath += string.Format("/{0}", datas[i]);
            }
            return directoryPath;
        }
        #endregion


        /// <summary>
        /// FTP 풀 경로에서 파일이름만 가져옵니다.
        /// </summary>
        /// <param name="ftpFilePath">FTP 풀 경로</param>
        /// <returns>파일명입니다.</returns>
        #region private string GetFileName(string ftpFilePath)
        private string GetFileName(string ftpFilePath)
        {
            string[] datas = ftpFilePath.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            return datas[datas.Length - 1];
        }
        #endregion


        /// <summary>
        /// FTP 경로의 디렉토리를 점검하고 없으면 생성
        /// </summary>
        /// <param name="directoryPath">디렉터리 경로 입니다.</param>
        #region public void FTPDirectioryCheck(string directoryPath)
        public void FTPDirectioryCheck(string directoryPath)
        {

            string[] directoryPaths = directoryPath.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            string currentDirectory = string.Empty;
            foreach (string directory in directoryPaths)
            {
                currentDirectory += string.Format("/{0}", directory);
                if (!IsExtistDirectory(currentDirectory))
                {
                    MakeDirectory(currentDirectory);
                }
            }
        }
        #endregion



        /// <summary>
        /// FTP에 해당 디렉터리가 있는지 알아온다.
        /// </summary>
        /// <param name="currentDirectory">디렉터리 명</param>
        /// <returns>있으면 참</returns>
        #region private bool IsExtistDirectory(string currentDirectory)
        private bool IsExtistDirectory(string currentDirectory)
        {

            string ftpFileFullPath = string.Format("ftp://{0}{1}", this.Host, GetParentDirectory(currentDirectory));
            FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFileFullPath)) as FtpWebRequest;
            ftpWebRequest.Credentials = new NetworkCredential(this.UserName, this.Password);
            ftpWebRequest.UseBinary = true;
            ftpWebRequest.UsePassive = true;
            ftpWebRequest.Timeout = 10000;
            ftpWebRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            FtpWebResponse response = null;
            string data = string.Empty;

            try
            {
                response = ftpWebRequest.GetResponse() as FtpWebResponse;
                if (response != null)
                {
                    StreamReader streamReader = new StreamReader(response.GetResponseStream(), Encoding.Default);
                    data = streamReader.ReadToEnd();
                }
            }

            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }

            string[] directorys = data.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            return (from directory in directorys
                    select directory.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries)
                        into directoryInfos
                        where directoryInfos[0][0] == 'd'
                        select directoryInfos[8]).Any(
                        name => name == (currentDirectory.Split('/')[currentDirectory.Split('/').Length - 1]).ToString());
        }
        #endregion

        /// <summary>
        /// 상위 디렉터리를 알아옵니다.
        /// </summary>
        /// <param name="currentDirectory"></param>
        /// <returns></returns>
        #region private string GetParentDirectory(string currentDirectory)
        private string GetParentDirectory(string currentDirectory)
        {
            string[] directorys = currentDirectory.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            string parentDirectory = string.Empty;
            for (int i = 0; i < directorys.Length - 1; i++)
            {
                parentDirectory += "/" + directorys[i];
            }

            return parentDirectory;
        }
        #endregion


        /// <summary>
        /// 인증을 가져옵니다.
        /// </summary>
        /// <returns>인증</returns>
        private ICredentials GetCredentials()
        {
            return new NetworkCredential(this.UserName, this.Password);
        }

        private string GetStringResponse(FtpWebRequest ftp)
        {
            string result = "";
            using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
            {

                long size = response.ContentLength;
                using (Stream datastream = response.GetResponseStream())
                {
                    if (datastream != null)
                    {
                        using (StreamReader sr = new StreamReader(datastream))
                        {
                            result = sr.ReadToEnd();
                            sr.Close();
                        }
                        datastream.Close();
                    }
                }
                response.Close();
            }
            return result;
        }

        private FtpWebRequest GetRequest(string URI)
        {
            FtpWebRequest result = (FtpWebRequest)WebRequest.Create(URI);
            result.Credentials = GetCredentials();
            result.KeepAlive = false;
            return result;
        }

        /// <summary>
        /// FTP에 해당 디렉터리를 만든다.
        /// </summary>
        /// <param name="dirpath"></param>
        #region public bool MakeDirectory(string dirpath)
        public bool MakeDirectory(string dirpath)
        {

            string URI = string.Format("ftp://{0}/{1}", this.Host, dirpath);
            System.Net.FtpWebRequest ftp = GetRequest(URI);
            ftp.Method = System.Net.WebRequestMethods.Ftp.MakeDirectory;

            try
            {
                string str = GetStringResponse(ftp);
            }
            catch
            {
                return false;
            }
            return true;
        }
        #endregion


        /// <summary>
        /// 지정한 로컬 파일이 실제 존재하는지 확인합니다.
        /// </summary>
        /// <param name="localFileFullPath">로컬 파일의 전체 경로입니다.</param>
        #region private void LocalFileValidationCheck(string localFileFullPath)
        private void LocalFileValidationCheck(string localFileFullPath)
        {
            if (!File.Exists(localFileFullPath))
            {
                throw new FileNotFoundException(string.Format("지정한 로컬 파일이 없습니다.\n경로 : {0}", localFileFullPath));
            }
        }
        #endregion


        /// <summary>
        /// FTP 서버에 지정한 파일의 생성일자를 가져 온다.
        /// </summary>
        /// <param name="ftpFileCreateDateCheck">로컬 파일의 전체 경로입니다.</param>
        #region private void ftpFileCreateDateCheck(string ftpFilePath, string ftpFileName)
        public string ftpFileCreateDateCheck(string ftpFilePath, string ftpFileName)
        {
            string sCreateDate = string.Empty;
            //string sRet = UserCom.RST_ERR;
            //bool bExisit = false;

            string fileName = GetFileName(ftpFilePath);
            string ftpFileFullPath = string.Format("ftp://{0}{1}", this.Host, GetDirectoryPath(ftpFilePath));
            FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFileFullPath)) as FtpWebRequest;
            //FtpWebRequest ftpWebRequest = WebRequest.Create(new Uri(ftpFilePath)) as FtpWebRequest;

            ftpWebRequest.Credentials = new NetworkCredential(this.UserName, this.Password);
            ftpWebRequest.UseBinary = true;
            ftpWebRequest.UsePassive = true;
            ftpWebRequest.Timeout = 10000;
            ftpWebRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

            //ftpWebRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp;

            FtpWebResponse response = null;
            string data = string.Empty;

            try
            {
                response = ftpWebRequest.GetResponse() as FtpWebResponse;
                if (response != null)
                {
                    StreamReader streamReader = new StreamReader(response.GetResponseStream(), Encoding.Default);
                    data = streamReader.ReadToEnd();
                }
            }
            catch (WebException ex)
            {
                response = ex.Response as FtpWebResponse;
                //if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailableOrBusy) bExisit = false;
                //else bExisit = true;
            }
            finally
            {
                if (response != null)
                    response.Close();
            }

            //bExisit = false;
            string CRLF = string.Format("{0}", (char)0x0a);
            string[] directorys = data.Split(new string[] { CRLF }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string directory in directorys)
            {
                string[] str = directory.Split(new char[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);
                if ((str.Length > 8) && (str[0].Substring(0, 1) != "d") && (str[8].Replace("\r", "") == fileName))
                {
                    //bExisit = true;
                    sCreateDate = str[5] + "|" + str[6] + "|" + str[7];
                    break;
                }
            }

            //return bExisit;
            return sCreateDate;

        }
        #endregion

        #endregion
    }
}
