// ==========================================================================
// Author:  Yee Hsu
// Date:    6/3/2012
//
// Desc:    Duplicate File Remover. Demostrate usage of Background worker
//          with delegate callback. This program removes duplicate files
//          base on same hash of the file and removes it from the system.
// ==========================================================================

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.IO;

namespace DupFileRemover
{
    public struct DupData
    {
        public string sFullName { get; set; }
        public string sScanned { get; set; }
        public string sScannedNum { get; set; }
        public string sMd5 { get; set; }
        public string sRemove { get; set; }
        public string sRemoveNum { get; set; }
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.folderBrowserDialog1.ShowDialog();
            this.textBox1.Text = this.folderBrowserDialog1.SelectedPath;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy != true)
            {
                DupData data = new DupData();
                this.backgroundWorker1.RunWorkerAsync(data);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (backgroundWorker1.WorkerSupportsCancellation == true)
            {
                backgroundWorker1.CancelAsync();
            }
        }

        private static System.IO.FileStream GetFileStream(string pathName)
        {
            return (new System.IO.FileStream(pathName, System.IO.FileMode.Open,
                    System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite));
        }

        public static string GetSHA1Hash(string pathName)
        {
            string strResult = "";
            string strHashData = "";

            byte[] arrbytHashValue;
            System.IO.FileStream oFileStream = null;

            System.Security.Cryptography.SHA1CryptoServiceProvider oSHA1Hasher=
                        new System.Security.Cryptography.SHA1CryptoServiceProvider();

            try
            {
                oFileStream = GetFileStream(pathName);
                arrbytHashValue = oSHA1Hasher.ComputeHash(oFileStream);
                oFileStream.Close();

                strHashData = System.BitConverter.ToString(arrbytHashValue);
                strHashData = strHashData.Replace("-", "");
                strResult = strHashData;
            }
            catch(System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message, "Error!", 
                            System.Windows.Forms.MessageBoxButtons.OK, 
                            System.Windows.Forms.MessageBoxIcon.Error, 
                            System.Windows.Forms.MessageBoxDefaultButton.Button1);
            }
            return(strResult);
        }

        public static string GetMD5Hash(string pathName)
        {
        string strResult = "";
        string strHashData = "";

        byte[] arrbytHashValue;
        System.IO.FileStream oFileStream = null;

        System.Security.Cryptography.MD5CryptoServiceProvider oMD5Hasher=
                    new System.Security.Cryptography.MD5CryptoServiceProvider();

        try
        {
        oFileStream = GetFileStream(pathName);
        arrbytHashValue = oMD5Hasher.ComputeHash(oFileStream);
        oFileStream.Close();

        strHashData = System.BitConverter.ToString(arrbytHashValue);
        strHashData = strHashData.Replace("-", "");
        strResult = strHashData;
        }
        catch(System.Exception ex)
        {
        System.Windows.Forms.MessageBox.Show(ex.Message, "Error!", 
                    System.Windows.Forms.MessageBoxButtons.OK, 
                    System.Windows.Forms.MessageBoxIcon.Error, 
                    System.Windows.Forms.MessageBoxDefaultButton.Button1);
        }

        return(strResult);
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            string path = this.textBox1.Text;
            DupData argumentData = (DupData) e.Argument;
            DirectoryInfo dir = new DirectoryInfo(path);
            long scanNum = 0;
            long removeNum = 0;

            Dictionary<string, string> dic = new Dictionary<string, string>();

            foreach (FileInfo f in dir.GetFiles("*.*"))
            {
                e.Result = argumentData;

                this.Invoke((MethodInvoker) delegate
                {
                    // currently scan full path file
                    this.textBox2.Text = Convert.ToString(argumentData.sFullName = f.FullName);

                    // set md5
                    string md5 = this.textBox3.Text = GetMD5Hash(this.textBox2.Text);

                    if (dic.ContainsKey(md5) == false)
                    {
                        // add to map
                        dic.Add(md5, null);

                        // update scanned list
                        this.richTextBox1.Text += Convert.ToString(argumentData.sScanned = f.Name + "\n");
                        this.richTextBox1.SelectionStart = this.richTextBox1.Text.Length;
                        this.richTextBox1.ScrollToCaret();

                        // update scanned num
                        this.groupBox2.Text = string.Format("File Scanned: {0}", ++scanNum);
                    }
                    else
                    {
                        // update remove list
                        this.richTextBox2.Text += Convert.ToString(argumentData.sRemove = f.Name + "\n");
                        this.richTextBox2.SelectionStart = this.richTextBox2.Text.Length;
                        this.richTextBox2.ScrollToCaret();

                        // update scanned num
                        this.groupBox3.Text = string.Format("File Removed: {0}", ++removeNum);

                        // delete the duplicate file!
                        File.Delete(this.textBox2.Text);
                    }
                    this.Update();
                });
            }
        }
    }
}