using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
using System.IO;

namespace NewPosInstaller
{
    /// <summary>
    /// 차세대 POS 신규 설치 및 설정정보 이행 
    /// </summary>
    public partial class frmInstaller : Form
    {
        private const string EXE_UPDATE = "NewPosInstallerStart.exe";

        private Point LastLocation = new Point(0, 0);
        
        private string RestartArg = string.Empty;

        delegate void SetTextCallback(string text);
        
        public frmInstaller()
        {
            InitializeComponent();
            this.ShowInTaskbar = false;
        }

        private void Form_Load(object sender, EventArgs e)
        {
            try
            {
                RestartArg = string.Empty;

                // 어셈블리 버전정보 
                //BasicInfo.InstallPgmVer = Assembly.GetEntryAssembly().GetName().Version.ToString();

                // 파일 버전정보 
                BasicInfo.InstallPgmVer = Application.ProductVersion;

                this.Text += " [ver:" + BasicInfo.InstallPgmVer + "]";

                ComLog.WriteLog(ComLog.Level.trace
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , string.Empty.PadRight(80, '='));
                ComLog.WriteLog(ComLog.Level.trace
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , "SPC NewPos Installer Start [version=" + BasicInfo.InstallPgmVer + "]");

                // 화면 초기 위치 (윈도우 오른쪽 하단)
                int x = Screen.PrimaryScreen.WorkingArea.Size.Width - 400;
                int y = Screen.PrimaryScreen.WorkingArea.Size.Height - 150;
                this.Location = this.LastLocation = new Point(x, y);

                this.Visible = false;
            }
            catch (Exception ex)
            {
                ComLog.WriteLog(ComLog.Level.Exception
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , ex.Message);
            }
        }

        private void Form_Shown(object sender, EventArgs e)
        {
            try
            {
                // TrayIcon 
                TrayOn();

                // 화면초기화 
                lblTitle.Text = "New POS program is being installed.\nPlease wait a moment.";
                lblWork.Text = "";

                //this.Refresh();

                // 작업시작
                Task worker = Task.Factory.StartNew(DoWork);
            }
            catch (Exception ex)
            {
                ComLog.WriteLog(ComLog.Level.Exception
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , ex.Message);
            }
        }

        private void Form_Closing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (e.CloseReason == CloseReason.UserClosing)
                {
                    TrayOn();
                    e.Cancel = true;
                    return;
                }

                this.notifyIcon.Visible = false;

                string closeReason = string.Empty;
                switch (e.CloseReason)
                {
                    case CloseReason.ApplicationExitCall: closeReason = "ApplicationExitCall"; break;
                    case CloseReason.TaskManagerClosing: closeReason = "TaskManagerClosing"; break;
                    case CloseReason.FormOwnerClosing: closeReason = "FormOwnerClosing"; break;
                    case CloseReason.MdiFormClosing: closeReason = "MdiFormClosing"; break;
                    case CloseReason.UserClosing: closeReason = "UserClosing"; break;
                    case CloseReason.None:
                    default:
                        closeReason = "Unknown";
                        break;
                }

                ComLog.SendStatus(ComLog.InstallStatus.ApplicationExit, ComLog.InstallResult.Success, "Application Exit = " + closeReason);

                ComLog.WriteLog(ComLog.Level.trace
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , string.Format("SPC NewPos Installer Stop [{0}]", closeReason));

                // Restart
                if (string.IsNullOrWhiteSpace(RestartArg) == false)
                {
                    // Excute Start program 
                    Process.Start(Path.Combine(DirInfo.WorkDir, EXE_UPDATE), RestartArg);
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLog(ComLog.Level.Exception
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , ex.Message);
            }
        }

        private void TrayOn()
        {
            this.LastLocation = this.Location;
            this.Visible = false;
            this.notifyIcon.Visible = true;
        }

        private void TrayOff()
        {
            this.notifyIcon.Visible = false;
            this.Visible = true;
            this.Location = this.LastLocation;
            this.Size = new Size(400, 150);
            this.Activate();
        }

        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            TrayOff();
        }

        private void OnEchoMessage(string args)
        {
            try
            {
                if (this.lblWork.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(OnEchoMessage);
                    this.Invoke(d, new object[] { args });
                }
                else
                {
                    this.lblWork.Text = args.Trim();
                    this.lblWork.Update();
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLog(ComLog.Level.Exception
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , ex.Message);
            }
        }

        private void DoWork()
        {
            try
            {
                bool ret = false;
                string statusData = string.Empty;
                string checkData = string.Empty; 

                #region ### New POS Program 실행여부 확인 
                // New POS Program 실행여부 확인 
                List<string> processCheckList = new List<string>();
                processCheckList.Add("PosMain.exe");
                processCheckList.Add("PosStart.exe");
                processCheckList.Add("PosConfiguration.exe");
                processCheckList.Add("TableConfiguration.exe");
                foreach (string processName in processCheckList)
                {
                    System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(processName);
                    if (process.Length > 1)
                    {
                        ComLog.WriteLog(ComLog.Level.trace
                                      , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                      + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                      , string.Format("Application exit because new POS program is runing. [{0}]", processName));
                        return;
                    }
                }
                #endregion

                #region ### POS 기본정보 및 설치일정 확인

                // 설치상태정보 가져오기
                BasicInfo.ReadStatusInfo();

                // 체크항목정보 가져오기 
                CheckInfo.ReadCheckInfo();

                // POS 기본정보 및 설치일정 확인
                BasicInfoCheck basicinfo = new BasicInfoCheck();
                basicinfo.EchoHandler += OnEchoMessage;
                ret = basicinfo.Execute();
                basicinfo.EchoHandler -= OnEchoMessage;

                // 상태로그 전송 (01:Install Start)
                statusData = string.Format("Install Start => OrgCompCd={0} OrgBrandCd={1} BrandGb={2} OrgStroeNo={3} OrgStoreType={4} OrgOrderType={5} OrgCountry={6} LocalIP={7} InstallDate={8}/{9} InstallerPgmVer={10}"
                                         , BasicInfo.CompCd, BasicInfo.BrandCd, BasicInfo.BrandGb, BasicInfo.OldStoreNo
                                         , BasicInfo.StoreType, BasicInfo.OrderType, BasicInfo._country, ComLib.GetLocalIpAddress()
                                         , BasicInfo.InstallDate, BasicInfo.InstallTime, BasicInfo.InstallPgmVer);
                ComLog.SendStatus(ComLog.InstallStatus.InstallStart, ret ? ComLog.InstallResult.Success : ComLog.InstallResult.Fail, statusData);
                if (ret == false)
                {
                    ComLog.WriteLog(ComLog.Level.Error
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [Basic information failed]");
                    ComLog.SendLog(ComLog.Level.Error, statusData);
                    return;
                }

                #region >> 이전점포코드 확인정보 전송
                // 이전점포코드 확인정보 전송
                if (BasicInfo.OldStoreNo != CheckInfo.OldStoreNo)
                {
                    ret = ComLog.SendStatus(ComLog.CheckStatus.OLD_STORE_CODE, BasicInfo.OldStoreNo, string.Format( "OrgStoreNo={0} NewStoreNo={1} StoreName={2}", BasicInfo.OldStoreNo, BasicInfo.StoreNo, BasicInfo.StoreNm));
                    if (ret) CheckInfo.OldStoreNo = BasicInfo.OldStoreNo;
                } 
                #endregion

                #region >> 주모니터 해상도 확인정보 전송 
                // 주모니터 해상도 확인정보 전송 
                checkData = string.Format("{0}x{1}", Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                if (CheckInfo.ScreenSize != checkData)
                {
                    statusData = "PrimaryScreen Current resolution = " + checkData;

                    if (checkData != "1024x768")
                    {
                        // 주모니터 1024x768 모드 지원여부 확인 
                        DisplayInfo dp = new DisplayInfo();
                        var d = dp.GetDisplayDevice().Where(x => x.Primary == true).FirstOrDefault();
                        if (d != null)
                        {
                            List<DisplayInfo.DeviceMode> dmList = dp.GetDeviceMode(d.DeviceNum);
                            foreach (var dm in from x in dmList
                                               where x.PelsWidth == 1024 && x.PelsHeight == 768
                                               orderby x.BitsPerPel descending, x.DisplayFrequency descending
                                               select x)
                            {
                                statusData += " , Supported resolution = " + dm.ToString();
                                break;
                            }
                        }
                    }

                    ret = ComLog.SendStatus(ComLog.CheckStatus.SCREEN_SIZE, checkData, statusData);
                    if (ret) CheckInfo.ScreenSize = checkData;
                } 
                #endregion

                #region >> OS 버전 
                // OS 버전 
                checkData = ComLib.IsAdministrator() ? ComLib.GetOSProductName(true) : ComLib.GetOSVersionString(true);
                if (CheckInfo.OSVerInfo != checkData)
                {
                    string osName = ComLib.GetOSVersionString(false);
                    ret = ComLog.SendStatus(ComLog.CheckStatus.OSVersion, osName, "OS Version = " + checkData);
                    if (ret) CheckInfo.OSVerInfo = checkData;
                } 
                #endregion

                #region >> 현재사용자 계정 권한 (Role)
                // 현재사용자 계정 권한 (Role)
                checkData = ComLib.GetCurrentPermission();
                if (CheckInfo.Permission != checkData)
                {
                    ret = ComLog.SendStatus(ComLog.CheckStatus.Permissions, checkData, ComLib.IsAdministrator() ? "Have administrator privileges." : "Not have administrator privileges.");
                    if (ret) CheckInfo.Permission = checkData;
                } 
                #endregion

                #endregion

                #region ### 디스크 가용 크기 확인 (400 MB 미만 설치종료)
                // 디스크 가용 크기 
                DriveInfo drv = new DriveInfo("C");
                long f = (long)(drv.AvailableFreeSpace / Math.Pow(1024, 2));   // MByte
                if (f < 400)
                {
                    ComLog.WriteLog(ComLog.Level.Error
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , string.Format("Application Exit [Disk free size={0}MByte]", f));

                    statusData = string.Format("Install Start => Disk Available FreeSpace={0}MByte", f);
                    ComLog.SendStatus(ComLog.InstallStatus.InstallStart, ComLog.InstallResult.Fail, statusData);
                    return;
                } 
                #endregion

                #region ### 인스톨프로그램 업데이트
                // 인스톨프로그램 업데이트
                InstallerUpdate update = new InstallerUpdate();
                update.EchoHandler += OnEchoMessage;
                ret = update.Execute();
                update.EchoHandler -= OnEchoMessage;
                if (ret)
                {
                    // 원본경로, 실행경로, 실행파일
                    string arg = string.Format("{0} {1} {2}", DirInfo.WorkDir.Trim(), Environment.CurrentDirectory.Trim(), "NewPosInstaller.exe");
                    string exe = Path.Combine(DirInfo.WorkDir, EXE_UPDATE);

                    // program restart 
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , string.Format("Application Exit [Program Restart] => {0} {1}", exe, arg));
                    // Excute Start program 
                    //Process.Start(Path.Combine(DirInfo.WorkDir, EXE_UPDATE), arg);
                    RestartArg = arg; 
                    return;
                } 
                #endregion

                #region ### 스캐줄 일자 확인
                // 스캐줄 일자 확인 
                if (ComLib.IntParse(BasicInfo.InstallDate) > ComLib.IntParse(DateTime.Now.ToString("yyyyMMdd")))
                {
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [This is not a setup day, InstallDate=" + BasicInfo.InstallDate + "]", true);
                    return;
                }
                #endregion

                #region ### 스캐쥴 시간 확인 (대기)
                // 스캐쥴 시간 확인 (대기)
                statusData = string.Format("Install Schedule Date : {0}\nInstall Schedule Time : {1}"
                                         , ComLib.StrToDate(BasicInfo.InstallDate), ComLib.StrToTime(BasicInfo.InstallTime));
                OnEchoMessage(statusData);

                ComLog.WriteLog(ComLog.Level.trace
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , string.Format("Wait for installation schedule [{0}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));
                while (true)
                {
                    // 5초대기 
                    Thread.Sleep(5 * 1000);

                    // 스캐쥴 시간 확인 
                    if (ComLib.IntParse(BasicInfo.InstallTime) <= ComLib.IntParse(DateTime.Now.ToString("HHmm")))
                    {
                        break;
                    }
                }

                ComLog.WriteLog(ComLog.Level.trace
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , string.Format("End of waiting [{0}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));

                #endregion

                #region ### 신규 POS 설치프로그램 다운로드 (FTP)
                // 신규 POS 프로그램 다운로드 및 설치 
                PosProgramInstall pgmSetup = new PosProgramInstall();
                pgmSetup.EchoHandler += OnEchoMessage;
                ret = pgmSetup.Execute();
                pgmSetup.EchoHandler -= OnEchoMessage;
                if (ret == false)
                {
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [New POS program download failed]");
                    return;
                }
                #endregion

                #region ### 데이터베이스 생성
                // 데이터베이스 생성 
                PosDatabaseCreate dbCreate = new PosDatabaseCreate();
                dbCreate.EchoHandler += OnEchoMessage;
                ret = dbCreate.Execute();
                dbCreate.EchoHandler -= OnEchoMessage;
                if (ret == false)
                {
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [Create Database failed]");

                    return;
                } 
                #endregion

                #region ### 로컬정보 이행 (환경설정, 장치정보)
                // 로컬정보 이행 (환경설정, 장치정보)
                PosDataMigration trans = new PosDataMigration();
                trans.EchoHandler += OnEchoMessage;
                ret = trans.Execute();
                trans.EchoHandler -= OnEchoMessage;
                if (ret == false)
                {
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [Pos data migration failed]");

                    return;
                } 
                #endregion

                #region ### 이미지 다운로드 => 설치시 이미지 다운로드 하지 않음, 2017.03.23
                //PosImageDownload imgDown = new PosImageDownload();
                //imgDown.EchoHandler += OnEchoMessage;
                //ret = imgDown.Execute();
                //imgDown.EchoHandler -= OnEchoMessage;
                //if (ret == false)
                //{
                //    ComLog.WriteLog(ComLog.Level.trace
                //                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                //                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                //                  , "Application Exit [Pos image down failed]");
                //    return;
                //}  
                #endregion

                // 마무리 
                PosInstallComplete end = new PosInstallComplete();
                end.EchoHandler += OnEchoMessage;
                ret = end.Execute();
                end.EchoHandler -= OnEchoMessage;

                statusData = ret ? "Install complete success" : "Install complete failed";
                ComLog.SendStatus(ComLog.InstallStatus.InstallComplete, ret ? ComLog.InstallResult.Success : ComLog.InstallResult.Fail, statusData);
                if (ret == false)
                {
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [Pos install completed failed]");
                }
                else
                {
                    ComLog.WriteLog(ComLog.Level.trace
                                  , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                                  + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                                  , "Application Exit [Pos install completed success]");
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLog(ComLog.Level.Exception
                              , System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "."
                              + System.Reflection.MethodBase.GetCurrentMethod().Name + "()"
                              , ex.Message);
            }
            finally
            {
                //this.Close();
                Application.Exit();
            }
        }
    }
}
