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 MPQ.Updater;
using MPQ.FileSystem;
using System.IO;
using System.Configuration;
using CommonLang.IO;
using CommonLang.Concurrent;
using System.Net;
using System.IO.Compression;
using CommonLang.Xml;
using System.Xml;
using CommonLang;

using MPQFileEntry = MPQ.FileSystem.MPQFileSystem.MPQFileEntry;
using MPQStream = MPQ.FileSystem.MPQFileSystem.MPQStream;
using CommonMPQ.SharpZipLib;

namespace MPQFileSystemTest
{
    public partial class FormUpdater : Form, MPQUpdaterListener
    {
        private SaveData save = new SaveData();
        private MPQUpdater updater;
        private MPQFileSystem filesystem;
        public class SaveData
        {
           public string DEFAULT_URL;
           public string DEFAULT_SUFFIX;
           public string SAVE_PATH;
           public string ZIP_TYPE;
           public string MPQ_TYPE;
        }
        public FormUpdater()
        {
            InitializeComponent();
            {
                string[] urls = ConfigurationManager.AppSettings["REMOTE_URLS"].Split(',');
                foreach (string e in urls)
                {
                    string url = e.Trim();
                    if (!string.IsNullOrEmpty(url))
                    {
                        comboBox_RemoteDir.Items.Add(url);
                        comboBox_RemoteDir.Text = url;
                    }
                }
            }
            {
                string[] suffixs = ConfigurationManager.AppSettings["REMOTE_SUFFIXS"].Split(',');
                foreach (string e in suffixs)
                {
                    string suffix = e.Trim();
                    if (!string.IsNullOrEmpty(suffix))
                    {
                        comboBox_RemoteSuffix.Items.Add(suffix);
                        comboBox_RemoteSuffix.Text = suffix;
                    }
                }
            }
            try
            {
                XmlDocument xml = XmlUtil.LoadXML(Application.StartupPath + "/save.xml");
                save = (SaveData)XmlUtil.XmlToObject(xml);
                if (!string.IsNullOrEmpty(save.DEFAULT_URL))
                {
                    comboBox_RemoteDir.Text = save.DEFAULT_URL;
                }
                if (!string.IsNullOrEmpty(save.DEFAULT_SUFFIX))
                {
                    comboBox_RemoteSuffix.Text = save.DEFAULT_SUFFIX;
                }
                if (!string.IsNullOrEmpty(save.SAVE_PATH))
                {
                    textBox_SaveRoot.Text = save.SAVE_PATH;
                }
                if (!string.IsNullOrEmpty(save.ZIP_TYPE))
                {
                    comboBox_zipType.Text = save.ZIP_TYPE;
                }
                if (!string.IsNullOrEmpty(save.MPQ_TYPE))
                {
                    comboBox_mpqType.Text = save.MPQ_TYPE;
                }
            }
            catch (Exception err)
            {
            }
        }

        private void FormUpdater_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                save.DEFAULT_URL = comboBox_RemoteDir.Text;
                save.DEFAULT_SUFFIX = comboBox_RemoteSuffix.Text;
                save.SAVE_PATH = textBox_SaveRoot.Text;
                save.ZIP_TYPE = comboBox_zipType.Text;
                save.MPQ_TYPE = comboBox_mpqType.Text;
                XmlDocument xml = XmlUtil.ObjectToXml(save);
                XmlUtil.SaveXML(Application.StartupPath + "/save.xml", xml);
            }
            catch (Exception err)
            {
            }
            if (filesystem != null) {
                filesystem.Dispose();
            }
            if (updater != null) {
                updater.Dispose();
            }
        }

        //--------------------------------------------------------------------------------------------
        #region AUTO_UPDATER

        public void onEvent(MPQUpdater updater, MPQUpdaterEvent e)
        {
            if (e.EventType == MPQUpdaterEvent.TYPE_COMPLETE)
            {
                RefreshMPQ();
            }
            else if (e.EventType == MPQUpdaterEvent.TYPE_ERROR || e.EventType == MPQUpdaterEvent.TYPE_NOT_ENOUGH_SPACE)
            {
                MessageBox.Show(e.ToString());
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (updater != null)
            {
                updater.Update();
                try
                {
                    progressBar_Download.Minimum = 0;
                    progressBar_Download.Maximum = (int)updater.TotalDownloadBytes;
                    progressBar_Download.Value = (int)updater.CurrentDownloadBytes;
                    label_Download.Text = updater.CurrentDownloadFile +
                        " (" + updater.CurrentDownloadBytes + "/" + updater.TotalDownloadBytes + ") " +
                        " " + (updater.CurrentDownloadSpeed / 1024) + "KB/S";

                    progressBar_Decompress.Minimum = 0;
                    progressBar_Decompress.Maximum = (int)updater.TotalUnzipBytes;
                    progressBar_Decompress.Value = (int)updater.CurrentUnzipBytes;
                    label_Unzip.Text = updater.CurrentUnzipFile +
                        " (" + updater.CurrentUnzipBytes + "/" + updater.TotalUnzipBytes + ") " +
                        " " + (updater.CurrentUnzipSpeed / 1024) + "KB/S";
                }
                catch (Exception err) { }
               
                textBox_VersionText.Lines = updater.VersionText.Split(new char[] { '\n' });
                progressBar_Running.Visible = updater.IsRunning;
            }
            else
            {
                progressBar_Download.Minimum = 0;
                progressBar_Download.Maximum = 1;
                progressBar_Download.Value = 0;
                label_Download.Text = "";

                progressBar_Decompress.Minimum = 0;
                progressBar_Decompress.Maximum = 1;
                progressBar_Decompress.Value = 0;
                label_Unzip.Text = "";

                textBox_VersionText.Text = "";

                progressBar_Running.Visible = false;
            }
        }

        private void button_Start_Click(object sender, EventArgs e)
        {
            if (updater != null)
            {
                updater.Dispose();
            }

            try
            {
                progressBar_Download.Minimum = 0;
                progressBar_Download.Maximum = 1;
                progressBar_Download.Value = 0;
                label_Download.Text = "";

                Uri url = new Uri(comboBox_RemoteDir.Text);

                updater = new MPQUpdater(new SharpZipLibMPQDirver());
                updater.Init(
                   new string[] { comboBox_RemoteDir.Text },
                   comboBox_RemoteSuffix.Text,
                   new DirectoryInfo(textBox_SaveRoot.Text + url.LocalPath),
                   new DirectoryInfo(textBox_BundleDir.Text),
                   true,
                   this);
                MPQUpdater.MPQ_EXT = comboBox_mpqType.Text.Trim();
                MPQUpdater.ZIP_EXT = comboBox_zipType.Text.Trim();
                //updater.RedirectDownloadSingle = RunDownloadSingle;
                //updater.RedirectUnzipSingle = RunUnzipSingle;
                updater.Start();
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private bool RunDownloadSingle(MPQUpdater updater, MPQ.Updater.MPQUpdater.RemoteFileInfo inf, long exist_size, long need_bytes, AtomicLong process)
        {
            Uri url = new Uri(updater.UrlRoots[0] + inf.key);
            var webRequest = (HttpWebRequest)HttpWebRequest.Create(url);
            webRequest.AddRange((int)exist_size, (int)(exist_size + need_bytes));
            webRequest.Method = "GET";
            WebResponse webResponse = webRequest.GetResponse();
            try
            {
                if (webResponse.ContentLength == need_bytes)
                {
                    byte[] io_buffer = new byte[1024 * 4];
                    using (FileStream fos = new FileStream(inf.file.FullName, FileMode.Append, FileAccess.Write))
                    {
                        Stream input = webResponse.GetResponseStream();
                        try
                        {
                            long total_readed = 0;
                            while (total_readed < need_bytes)
                            {
                                if (updater.IsDisposing) return false;
                                int readed = input.Read(io_buffer, 0, (int)Math.Min(io_buffer.Length, need_bytes - total_readed));
                                total_readed += readed;
                                process += readed;
                                fos.Write(io_buffer, 0, readed);
                            }
                            fos.Flush();
                        }
                        finally
                        {
                            fos.Close();
                        }
                    }
                }
                else
                {
                    throw new Exception("Bad response with ContentLength=" + webResponse.ContentLength);
                }
                return true;
            }
            finally
            {
                webResponse.Close();
            }
        }
        private bool RunUnzipSingle(MPQUpdater updater, MPQ.Updater.MPQUpdater.RemoteFileInfo zip, MPQ.Updater.MPQUpdater.RemoteFileInfo mpq, AtomicLong process)
        {
            byte[] io_buffer = new byte[1024 * 4];
            using (FileStream fis = new FileStream(zip.file.FullName, FileMode.Open, FileAccess.Read))
            {
                using (FileStream fos = new FileStream(mpq.file.FullName, FileMode.Create, FileAccess.Write))
                {
                    GZipStream gstream = new GZipStream(fis, CompressionMode.Decompress);
                    long total_readed = 0;
                    long total_size = mpq.size;
                    while (total_readed < total_size)
                    {
                        if (updater.IsDisposing) return false;
                        int readed = gstream.Read(io_buffer, 0, (int)Math.Min(io_buffer.Length, total_size - total_readed));
                        total_readed += readed;
                        process += readed;
                        fos.Write(io_buffer, 0, readed);
                    }
                    fos.Flush();
                    gstream.Close();
                    fos.Close();
                    fis.Close();
                }
            }
            return true;
        }

        private void button_Stop_Click(object sender, EventArgs e)
        {
            if (updater != null)
            {
                updater.Dispose();
                updater = null;
            }
            UnloadMPQ();
        }

        private void button_clear_Click(object sender, EventArgs e)
        {
            try
            {
                if (updater != null)
                {
                    updater.Dispose();
                    updater = null;
                }
                UnloadMPQ();
                DirectoryInfo local = new DirectoryInfo(textBox_SaveRoot.Text);
                if (local.Exists)
                {
                    local.Delete(true);
                    local.Create();
                }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }


      

       

      

      

        
        #endregion

       
        //--------------------------------------------------------------------------------------------
        #region MPQ_FILE_SYSTEM

        public class MPQFileSystemInfo
        {
            internal Dictionary<MPQ.Updater.MPQUpdater.RemoteFileInfo, MPQFileInfo> files =
                new Dictionary<MPQ.Updater.MPQUpdater.RemoteFileInfo, MPQFileInfo>();

            [Description("文件容量")]
            public long FileSize
            {
                get
                {
                    long ret = 0;
                    foreach (MPQFileInfo f in files.Values)
                    {
                        ret += f.FileSize;
                    }
                    return ret;
                }
                set { }
            }
            [Description("文件个数")]
            public int EntryCount
            {
                get
                {
                    int ret = 0;
                    foreach (MPQFileInfo f in files.Values)
                    {
                        ret += f.EntryCount;
                    }
                    return ret;
                }
                set { }
            }
            [Description("冗余容量")]
            public long ReplacedSize
            {
                get
                {
                    long ret = 0;
                    foreach (MPQFileInfo f in files.Values)
                    {
                        ret += f.ReplacedSize;
                    }
                    return ret;
                }
                set { }
            }
            [Description("冗余文件数")]
            public int ReplacedFileCount
            {
                get
                {
                    int ret = 0;
                    foreach (MPQFileInfo f in files.Values)
                    {
                        ret += f.ReplacedFileCount;
                    }
                    return ret;
                }
                set { }
            }
        }

        public class MPQFileInfo
        {
            internal List<MPQFileEntry> entries = new List<MPQFileEntry>();
            internal FileInfo fileinfo;
            internal int replaced_count;
            internal long replaced_size;

            public FileInfo File
            {
                get { return fileinfo; }
                set { }
            }

            [Description("文件容量")]
            public long FileSize
            {
                get { return fileinfo.Length; }
                set { }
            }
            [Description("文件个数")]
            public int EntryCount
            {
                get { return entries.Count; }
                set { }
            }
            [Description("冗余容量")]
            public long ReplacedSize
            {
                get { return replaced_size; }
                set { }
            }
            [Description("冗余文件数")]
            public int ReplacedFileCount
            {
                get { return replaced_count; }
                set { }
            }
        }
        private void UnloadMPQ()
        {
            if (this.filesystem != null)
            {
                TreeNode root = treeView_MPQ.Nodes[0];
                root.Tag = null;
                root.Nodes.Clear();

                this.filesystem.Dispose();
                this.filesystem = null;
            }
        }
        private void RefreshMPQ()
        {
            this.Enabled = false;
            try
            {
                TreeNode root = treeView_MPQ.Nodes[0];
                root.Tag = null;
                root.Nodes.Clear();

                if (filesystem != null)
                {
                    filesystem.Dispose();
                } 
                if (updater != null)
                {
                    this.filesystem = new MPQFileSystem();
                    //this.filesystem.init(updater);

                    MPQFileSystemInfo fsinfo = new MPQFileSystemInfo();
                    root.Tag = fsinfo;

                    foreach (MPQ.Updater.MPQUpdater.RemoteFileInfo rmf in updater.GetAllRemoteFiles())
                    {
                        if (rmf.key.EndsWith(MPQUpdater.MPQ_EXT))
                        {
                            MPQFileInfo mpq_info = new MPQFileInfo();
                            mpq_info.fileinfo = rmf.file;
                            MPQStream mpq_stream = filesystem.load(rmf.file.FullName);
                            mpq_stream.loadEntrys(mpq_info.entries);
                            foreach (MPQFileEntry e in mpq_info.entries)
                            {
                                MPQFileEntry exist_fe = filesystem.findEntry(e.Key);
                                bool is_old = !e.Equals(exist_fe);
                                if (is_old)
                                {
                                    mpq_info.replaced_size += e.Size;
                                    mpq_info.replaced_count += 1;
                                }
                            }
                            fsinfo.files[rmf] = mpq_info;
                        }
                    }
                }
                RefreshTreeNode();
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
            finally
            {
                this.Enabled = true;
                treeView_MPQ.Invalidate(true);
            }
        }

        private void RefreshTreeNode()
        {
            TreeNode root = treeView_MPQ.Nodes[0];
            root.Nodes.Clear();
            
            if (filesystem != null && root.Tag is MPQFileSystemInfo)
            {
                MPQFileSystemInfo fsinfo = (MPQFileSystemInfo)root.Tag;
                foreach (MPQ.Updater.MPQUpdater.RemoteFileInfo rmf in fsinfo.files.Keys)
                {
                    MPQFileInfo mpq_info = fsinfo.files[rmf];

                    TreeNode mpq_file_node = new TreeNode(rmf.key);
                    mpq_file_node.Tag = mpq_info;
                    foreach (MPQFileEntry e in mpq_info.entries)
                    {
                        MPQFileEntry exist_fe = filesystem.findEntry(e.Key);
                        bool is_old = !e.Equals(exist_fe);
                        if (!toolStripButton_ViewReplaced.Checked || is_old)
                        {
                            TreeNode enode = new TreeNode(e.Key);
                            enode.Tag = e;
                            if (is_old)
                            {
                                enode.Text = "(old) " + e.Key;
                                enode.ForeColor = Color.Gray;
                            }
                            enode.ContextMenuStrip = this.menu_EntryNode;
                            mpq_file_node.Nodes.Add(enode);
                        }
                    }
                    mpq_file_node.Text += " (" + (mpq_info.EntryCount - mpq_info.ReplacedFileCount) + "/" + mpq_info.EntryCount + ")";
                    root.Nodes.Add(mpq_file_node);
                }
            }
        }

        private void toolStripMenuItem_RefreshMPQ_Click(object sender, EventArgs e)
        {
            RefreshMPQ();
        }

        private void 导出文件ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TreeNode sn = treeView_MPQ.SelectedNode;
            if (sn != null && sn.Tag is MPQFileEntry)
            {
                MPQFileEntry entry = (MPQFileEntry)sn.Tag;
                SaveFileDialog sfd = new SaveFileDialog();
                string name = entry.Key.Replace('\\', '/');
                int index = entry.Key.LastIndexOf('/');
                if (index >= 0) { sfd.FileName = entry.Key.Substring(index+1); }
                else { sfd.FileName = entry.Key; }
                if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    byte[] data = filesystem.getEntryData(entry);
                    using (Stream stream = sfd.OpenFile())
                    {
                        stream.Write(data, 0, data.Length);
                    }
                }
            }
        }

       
        private void treeView_MPQ_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.Tag is MPQFileSystemInfo)
            {
                MPQFileSystemInfo mpq = (MPQFileSystemInfo)e.Node.Tag;
                property_MPQInfo.SelectedObject = mpq;
            }
            else if (e.Node.Tag is MPQFileInfo)
            {
                MPQFileInfo mpq = (MPQFileInfo)e.Node.Tag;
                property_MPQInfo.SelectedObject = mpq;
            } 
            else if (e.Node.Tag is MPQFileEntry)
            {
                MPQFileEntry mpq = (MPQFileEntry)e.Node.Tag;
                property_MPQInfo.SelectedObject = mpq;
            }
        }
        private void treeView_MPQ_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.Tag is MPQFileEntry)
            {
                MPQFileSystem.MPQFileEntry mpq = (MPQFileSystem.MPQFileEntry)e.Node.Tag;
                try
                {
                    new FormEntry(mpq, filesystem).ShowDialog();
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.Message);
                }
            }
        }

        
        private void toolStripButton_ViewReplaced_Click(object sender, EventArgs e)
        {
            this.Enabled = false;
            try
            {
                RefreshTreeNode();
            }
            finally
            {
                this.Enabled = true;
                treeView_MPQ.Refresh();
            }
        }

        #endregion

        //--------------------------------------------------------------------------------------------

    }
}