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

namespace ButcherFactory.AutoUpdate
{
  public partial class Form1 : Form
  {
    const string serUri = "http://172.28.1.194:7799/AutoUpdate/ClientVersion.xml";
    const string PuKey = "c756e02cedb42a33f78091a466411bde8447d854";
    private bool isFinished = false;
    private List<UpdateFile> downloadFileList = null;
    private ManualResetEvent evtDownload = null;
    private ManualResetEvent evtPerDonwload = null;
    private WebClient clientDownload = null;
    ClientVersion old;
    bool error = false;
    public Form1(string[] args)
    {
      InitializeComponent(); 
      if (args == null || args.Length == 0 || args[0] != PuKey)
        error = true;
      this.Load += Form1_Load;
    }

    void Form1_Load(object sender, EventArgs e)
    {
      if (error)
        Application.Exit();
      var versionFile = "ClientVersion.xml";
      old = new ClientVersion();
      if (File.Exists(versionFile))
        old = XmlUtil.DeserializeFromFile<ClientVersion>();
      if (string.IsNullOrEmpty(old.ServerUrl))
        old.ServerUrl = serUri;

      clientDownload = new WebClient();
      var version = XmlUtil.XmlDeserializeObject<ClientVersion>(clientDownload.DownloadString(old.ServerUrl));
      old.ServerUrl = version.ServerUrl;
      old.Version = version.Version;
      downloadFileList = version.UpdateFiles;

      evtDownload = new ManualResetEvent(true);
      evtDownload.Reset();
      Thread t = new Thread(new ThreadStart(ProcDownload));
      t.Name = "download";
      t.Start();
    }

    long total = 0;
    long nDownloadedTotal = 0;
    string startFile = "ButcherFactory.Login.exe";
    string oldFolder;
    private void ProcDownload()
    {
      evtPerDonwload = new ManualResetEvent(false);

      foreach (var file in this.downloadFileList)
      {
        total += file.Size;
      }
      oldFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "old");
      if (!Directory.Exists(oldFolder))
        Directory.CreateDirectory(oldFolder);

      while (!evtDownload.WaitOne(0, false))
      {
        if (this.downloadFileList.Count == 0)
          break;

        var file = this.downloadFileList[0];
        if (file.NeedRestart)
          startFile = file.FileName;

        this.ShowCurrentDownloadFileName(file.FileName);

        //下载
        clientDownload = new WebClient();

        clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
        clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);

        evtPerDonwload.Reset();
        if (file.FileName.Contains("/"))
          CreateDeepFolder(AppDomain.CurrentDomain.BaseDirectory, file.FileName);
        clientDownload.DownloadFileAsync(new Uri(file.Url), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileName + ".tmp"), file);

        //等待下载完成
        evtPerDonwload.WaitOne();

        clientDownload.Dispose();
        clientDownload = null;

        //移除已下载的文件
        this.downloadFileList.Remove(file);
      }

      if (this.downloadFileList.Count == 0)
      {
        Exit(true);
        evtDownload.Set();
        XmlUtil.SerializerObjToFile(old);
        Process.Start(Path.Combine(Application.StartupPath, startFile), "true");
        Application.Exit();
      }
      else
        Exit(false);

      evtDownload.Set();
    }

    void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
      var file = e.UserState as UpdateFile;
      nDownloadedTotal += file.Size;
      this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total));
      //备份文件
      string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileName);
      var oldFile = Path.Combine(oldFolder, file.FileName);
      if (File.Exists(filePath))
      {
        if (File.Exists(oldFile))
          File.Delete(oldFile);
        else if (file.FileName.Contains("/"))
        {
          CreateDeepFolder(oldFolder, file.FileName);
        }
        File.Move(filePath, oldFile);
      }

      File.Move(filePath + ".tmp", filePath);
      //继续下载其它文件
      evtPerDonwload.Set();
    }

    void CreateDeepFolder(string baseFolder,string fileName)
    {
      var idx = fileName.IndexOf(@"/");
      if (idx == -1)
        return;
      var folder = fileName.Substring(0, idx);
      baseFolder = Path.Combine(baseFolder, folder);
      if (!Directory.Exists(baseFolder))
        Directory.CreateDirectory(baseFolder);
      fileName = fileName.Substring(idx + 1);
      CreateDeepFolder(baseFolder, fileName);
    }

    void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
      this.SetProcessBar(e.ProgressPercentage, (int)((nDownloadedTotal + e.BytesReceived) * 100 / total));
    }

    delegate void ShowCurrentDownloadFileNameCallBack(string name);
    private void ShowCurrentDownloadFileName(string name)
    {
      if (this.labelCurrentItem.InvokeRequired)
      {
        ShowCurrentDownloadFileNameCallBack cb = new ShowCurrentDownloadFileNameCallBack(ShowCurrentDownloadFileName);
        this.Invoke(cb, new object[] { name });
      }
      else
      {
        this.labelCurrentItem.Text = name;
      }
    }

    delegate void SetProcessBarCallBack(int current, int total);
    private void SetProcessBar(int current, int total)
    {
      if (this.progressBarCurrent.InvokeRequired)
      {
        SetProcessBarCallBack cb = new SetProcessBarCallBack(SetProcessBar);
        this.Invoke(cb, new object[] { current, total });
      }
      else
      {
        this.progressBarCurrent.Value = current;
        this.progressBarTotal.Value = total;
      }
    }

    delegate void ExitCallBack(bool success);
    private void Exit(bool success)
    {
      if (this.InvokeRequired)
      {
        ExitCallBack cb = new ExitCallBack(Exit);
        this.Invoke(cb, new object[] { success });
      }
      else
      {
        this.isFinished = success;
        this.DialogResult = success ? DialogResult.OK : DialogResult.Cancel;
        this.Close();
      }
    }
  }
}
