using Microsoft.WindowsAPICodePack.Taskbar;
using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace StrangeFileCopy
{
    public partial class MainForm : Form
    {
        private string _status = "Ready";
        private int _bufProgress;
        private int _reOpenProgress;
        private int _totalProgress;
        private bool _pause;
        private bool _error;

        public MainForm() => InitializeComponent();

        private void FireButton_Click(object sender, EventArgs e)
        {
            var srcPath = FromTextBox.Text;
            var dstPath = ToTextBox.Text;

            if (string.IsNullOrWhiteSpace(srcPath) || string.IsNullOrWhiteSpace(dstPath))
            {
                MessageBox.Show("Before start,fill `File From' and `To Folder'");
                return;
            }

            var blkSize = MaxCheckBox.Checked ? 2147483591 : (int)BufSizeUpDown.Value * 1024 * 1024;
            var reOpenTimes = (int)Math.Ceiling(ReOpenSizeUnDown.Value * 1024 * 1024 * 1024 / blkSize);
            var flush = FlushCheckBox.Checked;

            FireButton.Enabled = false;
            ConfigGroupBox.Enabled = false;

            new Thread(() =>
            {
                FileStream src = null;
                FileStream dst = null;
                try
                {
                    _status = "Opening Source";
                    src = File.OpenRead(srcPath);
                    var srcLen = src.Length;

                    _status = "Pre allocating Dest - Opening";
                    dst = File.OpenWrite(dstPath);
                    _status = "Pre allocating Dest - Seeking";
                    dst.Position = src.Length - 1;
                    _status = "Pre allocating Dest - Writing";
                    dst.WriteByte(0);
                    _status = "Pre allocating Dest - Flushing";
                    dst.Flush(true);
                    _status = "Pre allocating Dest - Closing";
                    dst.Close();

                    long pos = 0;

                    var buf = new byte[blkSize];

                    while (pos < srcLen)
                    {
                        _status = "Opening Dest";
                        dst = File.OpenWrite(dstPath);
                        dst.Position = pos;

                        for (var i = 0; i < reOpenTimes; i++)
                        {
                            _status = "Reading Source";
                            var bi = 0;
                            var read = 0;
                            do
                            {
                                read = src.Read(buf, bi, blkSize - read);
                                bi += read;

                                _bufProgress = (int)(100 * (float)bi / blkSize);
                            } while (read > 0 && bi < blkSize);

                            _status = "Writing Dst";
                            dst.Write(buf, 0, bi);

                            _status = "Flushing Dst";
                            if (flush) dst.Flush(true);

                            _reOpenProgress = (int)(100 * (float)i / reOpenTimes);
                            _totalProgress = (int)(100 * (float)src.Position / srcLen);

                            if (_pause)
                            {
                                _status = "Paused";
                                while (_pause)
                                {
                                    Thread.Sleep(250);
                                }
                            }

                            if (src.Position == srcLen) break;
                        }

                        pos = src.Position;
                        _status = "Closing Dst";
                        dst.Close();
                    }
                    _status = "Done";
                }
                catch (Exception exception)
                {
                    _status = $"Error:{exception.Message}";
                    _error = true;
                }
                finally
                {
                    src?.Close();
                    try
                    {
                        dst?.Close();
                    }
                    catch
                    {
                        //ignore
                    }
                }
            }).Start();
        }

        private void UiUpdateTimer_Tick(object sender, EventArgs e)
        {
            CurrentOperationLabel.Text = _status;
            BufProgressBar.Value = _bufProgress;
            ReOpenProgressBar.Value = _reOpenProgress;
            TotalProgressBar.Value = _totalProgress;
            if (_error)
            {
                TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Error);
            }
            else
            {
                TaskbarManager.Instance.SetProgressState(_pause ? TaskbarProgressBarState.Paused : TaskbarProgressBarState.Normal);
                TaskbarManager.Instance.SetProgressValue(_totalProgress, 100);
            }
        }

        private void FromTextBox_DragEnter(object sender, DragEventArgs e)
        {
            string[] files;
            e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop)
                       && (files = (string[])e.Data.GetData(DataFormats.FileDrop)).Length == 1
                       && File.Exists(files[0])
                ? DragDropEffects.Copy
                : DragDropEffects.None;
        }

        private void FromTextBox_DragDrop(object sender, DragEventArgs e) => FromTextBox.Text = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];

        private void ToTextBox_DragEnter(object sender, DragEventArgs e)
        {
            string[] files;
            e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop)
                       && (files = (string[])e.Data.GetData(DataFormats.FileDrop)).Length == 1
                       && Directory.Exists(files[0])
                ? DragDropEffects.Copy
                : DragDropEffects.None;
        }

        private void ToTextBox_DragDrop(object sender, DragEventArgs e)
        {
            var to = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];

            new Thread(() =>
            {
                Invoke(new Action(delegate
                {
                    if (string.IsNullOrWhiteSpace(FromTextBox.Text))
                    {
                        MessageBox.Show("Fill `File from' first!");
                        return;
                    }

                    var filename = Path.GetFileName(FromTextBox.Text);
                    var dest = Path.Combine(to, filename);
                    if (File.Exists(dest))
                    {
                        if (DialogResult.Yes == MessageBox.Show("Target file exist,override it?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2))
                        {
                            ToTextBox.Text = dest;
                        }
                        else
                        {
                            ToTextBox.Clear();
                        }
                    }
                    else
                    {
                        ToTextBox.Text = dest;
                    }
                }));
            }).Start();
        }

        private void MaxCheckBox_CheckedChanged(object sender, EventArgs e) => BufSizeUpDown.Enabled = !MaxCheckBox.Checked;

        private void PauseCheckBox_CheckedChanged(object sender, EventArgs e) => _pause = PauseCheckBox.Checked;
    }
}