using BellwetherBackend.Utility;
using SocketTransfer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Threading;
using ICSharpCode.SharpZipLib.Zip;
using System.Threading;
using System.Net;

namespace BellwetherBackend.SubPage
{
    public enum UpdateState
    {
        TransferCompleted, Transferring, Disconnected, UpdateCompleted, UpdateFail, Abort, Connected
    }

    /// <summary>
    /// Interaction logic for UpdatePage.xaml
    /// </summary>
    public partial class UpdatePage : UserControl
    {
        private static SolidColorBrush IsConnectBrush = new SolidColorBrush(Colors.Green);
        private static SolidColorBrush IsDisconnectBrush = new SolidColorBrush(Colors.Red);
        private static string rootPath = System.AppDomain.CurrentDomain.BaseDirectory;
        private static string tempFolder = @"Temp\";
        private static string defaultZipFileName = @"Temp\Data.zip";

        private string ipAddress;
        public string IpAddress
        {
            get
            {
                if (CheckIpFormat(uxIp1Text.Text) && CheckIpFormat(uxIp2Text.Text) && CheckIpFormat(uxIp3Text.Text) && CheckIpFormat(uxIp4Text.Text))
                {
                    ipAddress = String.Format("{0}.{1}.{2}.{3}", uxIp1Text.Text, uxIp2Text.Text, uxIp3Text.Text, uxIp4Text.Text);
                }
                else
                {
                    ipAddress = "error format";
                }
                return ipAddress;
            }
            set
            {
                ipAddress = value;
                string[] str = ipAddress.Split('.');
                if (str.Length > 3)
                {
                    for (int i = 0; i < ipTextBlock.Length; i++)
                    {
                        ipTextBlock[i].Text = str[i];
                    }
                }
                this.Dispatcher.BeginInvoke((Action)delegate ()
                {
                    CheckKioskConnect();
                });
            }
        }

        private int ipPort;
        public int IpPort
        {
            set { ipPort = value; }
            get { return ipPort; }
        }

        private bool isConnect;
        public bool IsConnect
        {
            get { return isConnect; }
            set
            {
                isConnect = value;
                if (isConnect)
                {
                    uxConnectStateRect.Fill = IsConnectBrush;
                    uxConnectBtn.IsEnabled = false;
                    uxDisConnectBtn.IsEnabled = true;
                    uxIpGrid.IsEnabled = false;
                    uxUpdateBtn.IsEnabled = true;
                    UpdatingState = UpdateState.Connected;
                }
                else
                {
                    uxConnectStateRect.Fill = IsDisconnectBrush;
                    uxConnectBtn.IsEnabled = true;
                    uxDisConnectBtn.IsEnabled = false;
                    uxIpGrid.IsEnabled = true;
                    uxUpdateBtn.IsEnabled = false;
                    UpdatingState = UpdateState.Disconnected;
                }
            }
        }

        private UpdateState updatingState;
        public UpdateState UpdatingState
        {
            get { return updatingState; }
            set
            {
                updatingState = value;
            }
        }

        private string localipAddress;
        private TextBox[] ipTextBlock;
        private BackgroundWorker connectWorker;
        private SocketClient KioskSocketClient;

        private bool canTransFileTag = false;
        private List<string> wholeDataFolder = new List<string>();
        private List<string> cutFilePaths = new List<string>();

        public UpdatePage()
        {
            InitializeComponent();

            CreateTempFolder();


            new GlobalFunction();
            uxConnectBtn.IconData = GlobalFunction.ConnectBtnData;
            uxDisConnectBtn.IconData = GlobalFunction.DisconnectBtnData;
            uxUpdateBtn.IconData = GlobalFunction.UpdateBtnData;

            localipAddress = GetLocalIpAddress();
            // 連線
            ipTextBlock = new TextBox[]
            {
                uxIp1Text, uxIp2Text, uxIp3Text, uxIp4Text
            };
            IpAddress = Properties.Settings.Default["ConnectIP"].ToString();
            IpPort = int.Parse(Properties.Settings.Default["ConnectPort"].ToString());

            connectWorker = new BackgroundWorker();
            connectWorker.WorkerSupportsCancellation = true;
            connectWorker.DoWork += connectWorker_DoWork;
            connectWorker.RunWorkerCompleted += connectWorker_RunWorkerCompleted;

            uxIp1Text.TextChanged += uxIp1Text_TextChanged;
            uxIp2Text.TextChanged += uxIp1Text_TextChanged;
            uxIp3Text.TextChanged += uxIp1Text_TextChanged;
            uxIp4Text.TextChanged += uxIp1Text_TextChanged;

            this.Loaded += UpdatePage_Loaded;
        }

        private void UpdatePage_Loaded(object sender, RoutedEventArgs e)
        {
            //Setting.PageNameList[x]
            List<CheckBox> checkBoxList = new List<CheckBox>();
            foreach (UIElement element in uxUpdateListPanel.Children)
            {
                if (element is CheckBox checkBox)
                    checkBoxList.Add(checkBox);
            }

            //var visualElementCnt = checkBoxList.Count;
            //var dataStringCnt = Setting.PageNameList.Count;
            //for (int pageIndex = 0; pageIndex + 1 < visualElementCnt && pageIndex < dataStringCnt; pageIndex++)
            //{
            //    checkBoxList[pageIndex + 1].Content = Setting.PageNameList[pageIndex];
            //}
        }

        public void CheckKioskConnect()
        {
            if (connectWorker.IsBusy)
            {
                connectWorker.CancelAsync();
            }
            connectWorker.RunWorkerAsync();
        }

        public void Disconnect()
        {
            DisconnectHandle();
        }

        private void StartLoading()
        {
            uxProgressBar.Visibility = Visibility.Visible;
            uxWorkingGrid.IsEnabled = false;
            uxWorkingGrid.Opacity = 0.3;
        }

        private void StopLoading()
        {
            uxProgressBar.Visibility = Visibility.Collapsed;
            uxWorkingGrid.IsEnabled = true;
            uxWorkingGrid.Opacity = 1.0;
        }

        private void CreateTempFolder()
        {
            string path = System.IO.Path.Combine(rootPath, tempFolder);
            if (Directory.Exists(path))
            {
                ClearTempFolder();
            }
            else
            {
                Directory.CreateDirectory(path);
            }
        }
        private void ClearTempFolder()
        {
            string path = System.IO.Path.Combine(rootPath, tempFolder);
            if (Directory.Exists(path))
            {
                string[] files = Directory.GetFiles(path);

                foreach (string file in files)
                {
                    try
                    {
                        if (File.Exists(file))
                            File.Delete(file);
                    }
                    catch { }
                }
            }
        }
        private string GetLocalIpAddress()
        {
            string result = "";
            string strHostName = Dns.GetHostName();
            IPHostEntry iphostentry = Dns.GetHostEntry(strHostName);

            foreach (IPAddress ipaddress in iphostentry.AddressList)
            {
                // 只取得IP V4的Address
                if (ipaddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    result = ipaddress.ToString();
                }
            }
            return result;
        }

        private bool CheckIpFormat(string str)
        {
            byte checkPart = 0;
            if (!byte.TryParse(str, out checkPart))
            {
                return false;
            }
            return true;
        }

        private void InitialUpdateStatus()
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                uxStatus.Text = "";
                uxUpdateProgressBar.Value = 0;
                if (KioskSocketClient != null)
                    uxUpdateBtn.IsEnabled = true;
                ClearTempFolder();
            }, DispatcherPriority.Background);
        }

        private void ChangeUpdateProgrssBarValue(double value)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                uxUpdateProgressBar.Value += value;
            }, DispatcherPriority.Background);
        }

        private void uxIp1Text_TextChanged(object sender, TextChangedEventArgs e)
        {
            Properties.Settings.Default["ConnectIP"] = uxIp1Text.Text + "." + uxIp2Text.Text + "." + uxIp3Text.Text + "." + uxIp4Text.Text;
        }

        private void connectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                StopLoading();
            }, DispatcherPriority.Background);
        }

        private void connectWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                // loading bar 
                StartLoading();
            }, DispatcherPriority.Background);

            if (KioskSocketClient != null)
            {
                KioskSocketClient.ServerReady -= KioskSocketClient_ServerReady;
                KioskSocketClient.ServerClose -= KioskSocketClient_ServerClose;
            }

            string serverIp = String.Empty;
            this.Dispatcher.Invoke((Action)delegate ()
            {
                serverIp = IpAddress;
                if (serverIp != "error format")
                {
                    KioskSocketClient = new SocketClient(IpAddress, IpPort, localipAddress);
                    KioskSocketClient.ServerReady += KioskSocketClient_ServerReady;
                    KioskSocketClient.ServerClose += KioskSocketClient_ServerClose;
                }
            }, DispatcherPriority.Background);

            if (serverIp != "error format")
            {
                try
                {
                    KioskSocketClient.Connect();
                }
                catch
                {
                    this.Dispatcher.BeginInvoke((Action)delegate ()
                    {
                        IsConnect = false;
                    }, DispatcherPriority.Background);
                }
            }
            else
            {
                this.Dispatcher.BeginInvoke((Action)delegate ()
                {
                    IsConnect = false;
                }, DispatcherPriority.Background);
            }
        }

        private void KioskSocketClient_ServerReady(string serverIP, string version)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                IsConnect = true;
            }, DispatcherPriority.Background);
        }
        private void KioskSocketClient_ServerClose(string serverIP)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                IsConnect = false;
            }, DispatcherPriority.Background);
        }

        private void uxConnect_Click(object sender, RoutedEventArgs e)
        {
            CheckKioskConnect();
        }

        private void uxDisConnectBtn_Click(object sender, RoutedEventArgs e)
        {
            DisconnectHandle();
        }

        private void DisconnectHandle()
        {
            if (KioskSocketClient != null)
            {
                KioskSocketClient.ServerReady -= KioskSocketClient_ServerReady;
                KioskSocketClient.ServerClose -= KioskSocketClient_ServerClose;
                KioskSocketClient.Close();
                KioskSocketClient = null;
            }
            IsConnect = false;
            UpdatingState = UpdateState.Abort;
        }

        #region 更新開始
        private void uxUpdateBtn_Click(object sender, RoutedEventArgs e)
        {
            List<string> packageFilePath = new List<string>();

            canTransFileTag = true;
            uxUpdateBtn.IsEnabled = false;
            // 收集欲打包的資料路徑
            StatusChange("收集打包資訊..");
            wholeDataFolder = GetPackageFolder();
            packageFilePath = GetUpdatePackageFilePath();

            if (wholeDataFolder.Count == 0)
            {
                StatusChange("沒有選擇任何傳送檔案..");
                // 所有狀態回復
                WpfDelayDoWork.DoBackgroundWork(InitialUpdateStatus, 3000);
                return;
            }

            // Step 1 : 打包
            uxUpdateProgressBar.Value = 0;
            StatusChange("開始打包..");
            BackgroundWorker zipWorker = new BackgroundWorker();
            zipWorker.WorkerSupportsCancellation = true;
            zipWorker.DoWork += zipWorker_DoWork;
            zipWorker.RunWorkerCompleted += zipWorker_RunWorkerCompleted;
            zipWorker.RunWorkerAsync(packageFilePath);
        }
        private void StatusChange(string status)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()
            {
                uxStatus.Text = status;
            }, DispatcherPriority.Background);
        }
        private void DivisionZipFile()
        {
            // Step 2 : 分割
            uxUpdateProgressBar.Value = 0;
            StatusChange("分割封裝檔..");
            BackgroundWorker divisionWorker = new BackgroundWorker();
            divisionWorker.WorkerSupportsCancellation = true;
            divisionWorker.DoWork += divisionWorker_DoWork;
            divisionWorker.RunWorkerCompleted += divisionWorker_RunWorkerCompleted;
            divisionWorker.RunWorkerAsync();
        }

        private void TransCutFile()
        {
            // Step 3 : 傳送
            uxUpdateProgressBar.Value = 0;
            StatusChange("檔案傳送中...");
            BackgroundWorker updateWorker = new BackgroundWorker();
            updateWorker.WorkerSupportsCancellation = true;
            updateWorker.DoWork += updateWorker_DoWork;
            updateWorker.RunWorkerCompleted += updateWorker_RunWorkerCompleted;
            updateWorker.RunWorkerAsync();
        }
        #endregion
        #region 收集欲打包的資料
        private List<string> GetPackageFolder()
        {
            List<string> resultPaceage = new List<string>();

            for (int i = 0; i < uxUpdateListPanel.Children.Count; i++)
            {
                CheckBox chkBtn = uxUpdateListPanel.Children[i] as CheckBox;

                if (chkBtn != null)
                {
                    if (chkBtn.IsChecked == true)
                    {
                        if (chkBtn.Tag.ToString() != "")
                            resultPaceage.Add(chkBtn.Tag.ToString());
                    }
                }
            }

            return resultPaceage;
        }

        private List<string> GetUpdatePackageFilePath()
        {
            List<string> resultPackage = new List<string>();

            // 整理要打包的東西 : 所有要打包的檔案的相對根目錄底下路徑
            for (int i = 0; i < wholeDataFolder.Count; i++)
            {
                switch (wholeDataFolder[i])
                {
                    case "Basic":
                        PackageBasicSetting(resultPackage);
                        break;
                    case "FrontPage":
                        PackageFrontPageSetting(resultPackage);
                        break;
                    case "History":
                        PackageHistoryWall(resultPackage, Setting.PageType.History);
                        break;
                    case "Product":
                        PackageHistoryWall(resultPackage, Setting.PageType.Product);
                        break;
                    case "CSR":
                        PackageHistoryWall(resultPackage, Setting.PageType.CSR);
                        break;
                }
            }
            return resultPackage;
        }

        private void PackageBasicSetting(List<string> resultPackage)
        {
            resultPackage.Add(Setting.settingJsonDataPath);
        }

        private void PackageFrontPageSetting(List<string> resultPackage)
        {
            string[] files = Directory.GetFiles(Setting.frontPageDirectory);
            resultPackage.AddRange(files);
        }

        private void PackageHistoryWall(List<string> resultPackage, Setting.PageType type)
        {
            string folderPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Data", Setting.PageFolderName[type]);
            string DataConfigFileName = System.IO.Path.Combine(folderPath, @"Setting\ConfigSetting.ini");
            string historyFileName = System.IO.Path.Combine(folderPath, @"HistoryWallData\HistoryFile.ini");
            string DataThumFileFolder = System.IO.Path.Combine(folderPath, @"Thumb\");
            string DataJsonImgFileFolder = System.IO.Path.Combine(folderPath, @"HistoryImage\");

            // History : 1. Setting\ConfigSetting.ini + 2. HistoryWallData\HistoryFile.ini + 3. Thumb\bgMap file and logoMap file + 4. HistoryImage 整包
            if (File.Exists(DataConfigFileName))
            {
                resultPackage.Add(DataConfigFileName);
            }

            if (File.Exists(historyFileName))
            {
                resultPackage.Add(historyFileName);
            }

            string[] files = Directory.GetFiles(DataThumFileFolder);

            foreach (string file in files)
            {
                // 只接受 bgMap 及 logoMap 兩個檔名檔案
                if (GlobalFunction.CheckFileIsImage(file) && (file.IndexOf("bgMap") != -1 || file.IndexOf("logoMap") != -1))
                {
                    resultPackage.Add(file);
                }
            }

            if (Directory.Exists(DataJsonImgFileFolder))
            {
                List<string> dirs = new List<string>(Directory.EnumerateDirectories(DataJsonImgFileFolder));

                foreach (var dir in dirs)
                {
                    string[] inforfiles = Directory.GetFiles(dir);

                    foreach (string file in inforfiles)
                    {
                        // 接受圖片或影片
                        if (GlobalFunction.CheckFileIsImage(file) || GlobalFunction.CheckFileIsVideo(file))
                        {
                            resultPackage.Add(file);
                        }
                    }
                }
            }
        }
        #endregion
        #region  打包
        private void zipWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> packagePathList = e.Argument as List<string>;
            FileStream FS = File.Create(defaultZipFileName);
            ZipOutputStream s = new ZipOutputStream(FS);
            string currentPath = Directory.GetCurrentDirectory();
            s.SetLevel(9); //0 ~ 9
            double stepValue = 100 / packagePathList.Count;

            for (int i = 0; i < packagePathList.Count; i++)
            {
                FileStream fs = File.OpenRead(packagePathList[i]);
                byte[] buffer = new byte[fs.Length];
                fs.Read(buffer, 0, buffer.Length);

                string zipPath = packagePathList[i];
                if (packagePathList[i].Contains(currentPath))
                    zipPath = zipPath.Remove(0, currentPath.Length + 1);
                ZipEntry entry = new ZipEntry(zipPath);
                entry.DateTime = DateTime.Now;
                entry.Size = fs.Length;

                fs.Close();
                s.PutNextEntry(entry);
                s.Write(buffer, 0, buffer.Length);
                ChangeUpdateProgrssBarValue(stepValue);
            }
            s.Dispose();
            s.Close();
        }

        private void zipWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            StatusChange("打包完成..");
            uxUpdateProgressBar.Value = 100;
            //切割打包資料夾
            WpfDelayDoWork.DoBackgroundWork(DivisionZipFile, 1000);
        }
        #endregion
        #region 切割
        private void divisionWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string zipFilePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), defaultZipFileName);

            if (File.Exists(zipFilePath))
            {
                try
                {
                    int segmentSize = 4194304; //4MB
                    long fileSize = new FileInfo(zipFilePath).Length;
                    int datas = 0;
                    int segmentCount = (int)(fileSize / (long)segmentSize);
                    int lastSegmentSize = (int)(fileSize % (long)segmentSize);
                    segmentCount = lastSegmentSize > 0 ? segmentCount + 1 : segmentCount;
                    double stepValue = 100 / segmentCount;
                    FileStream fs = File.OpenRead(zipFilePath);
                    cutFilePaths = new List<string>();

                    for (int i = 0; i < segmentCount; i++)
                    {
                        byte[] binaryData;
                        string segmentFileName = zipFilePath.Replace(".zip", String.Empty) + "[" + (i + 1) + "].cut";
                        FileStream fOut = File.OpenWrite(segmentFileName);
                        if (lastSegmentSize > 0 && i == segmentCount - 1)
                        {
                            binaryData = new byte[lastSegmentSize];
                            datas = fs.Read(binaryData, 0, lastSegmentSize);
                            fOut.Write(binaryData, 0, lastSegmentSize);
                        }
                        else
                        {
                            binaryData = new byte[segmentSize];
                            datas = fs.Read(binaryData, 0, segmentSize);
                            fOut.Write(binaryData, 0, segmentSize);
                        }
                        fOut.Flush();
                        fOut.Close();
                        ChangeUpdateProgrssBarValue(stepValue);
                        cutFilePaths.Add(segmentFileName);
                    }
                    fs.Close();
                    File.Delete(zipFilePath);
                    canTransFileTag = true;
                }
                catch
                {
                    canTransFileTag = false;
                }
            }
            else
            {
                canTransFileTag = false;
            }
        }

        private void divisionWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            StatusChange("分割完成..");
            uxUpdateProgressBar.Value = 100;
            if (canTransFileTag)
            {
                //傳輸更新檔案開始
                WpfDelayDoWork.DoBackgroundWork(TransCutFile, 1000);
                return;
            }

            StatusChange("分割失敗..");
            // 所有狀態回復
            WpfDelayDoWork.DoBackgroundWork(InitialUpdateStatus, 3000);
        }
        #endregion
        #region 傳送
        bool isSendComplete = false;
        private void updateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (cutFilePaths.Count > 0)
            {
                try
                {
                    isSendComplete = false;
                    // 通知分割檔案總數
                    KioskSocketClient.NotifyFileTransferStart(cutFilePaths.Count);
                    // 掛上前台更新狀況事件
                    KioskSocketClient.SendSegmentFileFinish += KioskSocketClient_SendSegmentFileFinish;
                    KioskSocketClient.FailSendFile += KioskSocketClient_FailSendFile;
                    KioskSocketClient.ReceiveServerNotify += KioskSocketClient_ReceiveServerNotify;
                    //傳送 Cut File
                    for (int i = 0; i < cutFilePaths.Count; i++)
                    {
                        while (isSendComplete)
                        {
                            Thread.Sleep(1000);
                        }
                        KioskSocketClient.SendFile(cutFilePaths[i], "0");
                        isSendComplete = true;
                    }
                }
                catch
                {
                    KioskSocketClient = null;
                    canTransFileTag = false;
                }
            }
        }

        private void updateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (canTransFileTag)
            {
                //傳輸更新檔案開始
                StatusChange("傳送完成..");
                uxUpdateProgressBar.Value = 100;
                return;
            }
            StatusChange("傳送失敗..");
            // 所有狀態回復
            WpfDelayDoWork.DoBackgroundWork(InitialUpdateStatus, 3000);
        }

        private void KioskSocketClient_SendSegmentFileFinish(string serverIP, string index)
        {
            double stepValue = 100 / cutFilePaths.Count;

            ChangeUpdateProgrssBarValue(stepValue);
            isSendComplete = false;
        }

        #endregion
        #region 前台更新狀況回報
        private void KioskSocketClient_FailSendFile(object sender, EventArgs e)
        {
            StatusChange("傳輸異常...");
            // 所有狀態回復
            WpfDelayDoWork.DoBackgroundWorkAsync(Dispatcher.CurrentDispatcher, InitialUpdateStatus, 3000);
        }

        private void KioskSocketClient_ReceiveServerNotify(string serverIP, string message)
        {
            if (message.Equals("Success"))
            {
                StatusChange("前台更新完成重新啟動中...");
                // 所有狀態回復
                WpfDelayDoWork.DoBackgroundWorkAsync(Dispatcher.CurrentDispatcher, InitialUpdateStatus, 3000);
            }
            else if (message.Equals("Fail"))
            {
                StatusChange("傳輸異常...");
                // 所有狀態回復
                WpfDelayDoWork.DoBackgroundWorkAsync(Dispatcher.CurrentDispatcher, InitialUpdateStatus, 3000);
            }
        }
        #endregion
    }

    public class WpfDelayDoWork
    {
        public static void DoWork(Action action, int millisecond = 300)
        {
            new Action<Dispatcher, Action, int>(DoWorkAsync).BeginInvoke(Dispatcher.CurrentDispatcher, action, millisecond, null, null);
        }

        public static void DoBackgroundWork(Action action, int millisecond = 300)
        {
            new Action<Dispatcher, Action, int>(DoBackgroundWorkAsync).BeginInvoke(Dispatcher.CurrentDispatcher, action, millisecond, null, null);
        }

        public static void DoWorkAsync(Dispatcher dispatcher, Action action, int millisecond)
        {
            System.Threading.Thread.Sleep(millisecond);
            dispatcher.BeginInvoke(action);
        }

        public static void DoBackgroundWorkAsync(Dispatcher dispatcher, Action action, int millisecond)
        {
            System.Threading.Thread.Sleep(millisecond);
            dispatcher.Invoke(action, DispatcherPriority.Background);
        }
    }
}
