using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using System.Management;
using Newtonsoft.Json;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using Microsoft.WindowsAPICodePack.Dialogs;

namespace barry
{
    public class methods
    {

        public static string current_folder()
        {
            return AppDomain.CurrentDomain.BaseDirectory;
        }

        public static string data_file_path()
        {
            return Path.Combine(current_folder(), "barry.dat");
        }

        /// <summary>
        /// returns the current drive letter
        /// </summary>
        /// <returns></returns>
        public static string current_drive_letter()
        {
            return System.IO.Path.GetPathRoot(current_folder()).ToUpper();
        }

        /// <summary>
        /// returns the current drive letter
        /// </summary>
        /// <returns></returns>
        public static string computer_name()
        {
            return System.Environment.MachineName;
        }


        /// <summary>
        /// returns the current drive label
        /// </summary>
        /// <returns></returns>
        public static string current_drive_label()
        {
            // current drive letter
            string driveLetter = current_drive_letter();

            System.IO.DriveInfo[] drives = System.IO.DriveInfo.GetDrives();
            for (int i = 0; i < drives.Count(); i++)
            {
                if (drives[i].Name == driveLetter)
                {
                    if (drives[i].IsReady)
                    {
                        return drives[i].VolumeLabel;
                    }
                }
            }
            return "<unknwown>";
        }

        /// <summary>
        /// Get the unique identifier of the windows installation (more reliable than UUID)
        /// </summary>
        /// <returns>UUID (32 cars)</returns>
        public static string get_machine_guid()
        {
            ManagementObject os = new ManagementObject("Win32_OperatingSystem=@");
            return (string)os["SerialNumber"];
        }

        // access to data file
        public static Dictionary<string, BackupConfig> load_config_from_file()
        {
            string json;
            try
            {
                json = File.ReadAllText(data_file_path());
            }
            catch(FileNotFoundException)
            {
                json = "{}";
            }
            return JsonConvert.DeserializeObject<Dictionary<string, BackupConfig> >(json);
            // catch: JsonReaderException
        }

        public static void save_config_to_file(Dictionary<string, BackupConfig> barry_config)
        {
            string json = JsonConvert.SerializeObject(barry_config, Formatting.Indented);
            File.WriteAllText(data_file_path(), json);
        }
        
        public static void register_this_cpu()
        {

            string machine_guid = get_machine_guid();

            Dictionary<string, BackupConfig> barry_config = load_config_from_file();

            if (barry_config.ContainsKey(machine_guid))
            {
                Console.WriteLine(">> this computer is already registered");
                return;
            }

            BackupConfig new_bkp = new BackupConfig();
            new_bkp.machine_guid = machine_guid;

            // add a select dir dialog box

            new_bkp.dir_path = @"";

            barry_config.Add(machine_guid, new_bkp);

            save_config_to_file(barry_config);

        }

        public static BackupConfig load_this_cpu()
        {
            Dictionary<string, BackupConfig> barry_config = load_config_from_file();
            try
            {
                return barry_config[get_machine_guid()];
            }
            catch(KeyNotFoundException)
            {
                return null;
            }
        }

        public static void save_this_cpu(BackupConfig bkp)
        {
            Dictionary<string, BackupConfig> barry_config = load_config_from_file();
            barry_config[get_machine_guid()] = bkp;
            save_config_to_file(barry_config);
        }

        /// <summary>
        /// returns true if dir1 is a subdir of dir2
        /// </summary>
        /// <param name="dir1"></param>
        /// <param name="dir2"></param>
        public static bool is_subdir(string dir1, string dir2)
        {
            DirectoryInfo di1 = new DirectoryInfo(dir1);
            DirectoryInfo di2 = new DirectoryInfo(dir2);
            while (di1.Parent != null)
            {
                if (di1.Parent.FullName == di2.FullName)
                {
                    return true;
                }
                else di1 = di1.Parent;
            }
            return false;
        }

        public static string select_directory_dialog(string title = "Select a directory", string initial_dir = "")
        {
            var dlg = new CommonOpenFileDialog();
            dlg.Title = title;
            dlg.IsFolderPicker = true;
            dlg.InitialDirectory = initial_dir;
            dlg.EnsurePathExists = true;
            dlg.ShowPlacesList = true;

            if (dlg.ShowDialog() == CommonFileDialogResult.Ok)
            {
                var folder = dlg.FileName;
                return (string)folder;
            }
            return "";
        }

        // generate the barryscan script
        // this powershell script will be launched by a scheduled task and will be triggered when a device (like usb key) is inserted
        // when this happen, 'prog_path' will be run
        public static void create_barryscan_script(string drive_name)
        {
            // create barryscan.ps1 in %appdata%\barry\
            string script = String.Format(@"Register-WmiEvent -Class win32_VolumeChangeEvent -SourceIdentifier volumeChange
do
{{
    $newEvent = Wait-Event -SourceIdentifier volumeChange
    $eventType = $newEvent.SourceEventArgs.NewEvent.EventType
    if ($eventType -eq 2)
    {{
        $driveLetter = $newEvent.SourceEventArgs.NewEvent.DriveName 
        $driveLabel = ([wmi]""Win32_LogicalDisk='$driveLetter'"").VolumeName
        if ($driveLabel -eq '{0}')
        {{
			cd $driveLetter""\_autorun\""
            start-process -FilePath $driveLetter""\_autorun\barry.exe"" -ArgumentList ""/autorun""
        }}
    }}
    Remove-Event -SourceIdentifier volumeChange
}} while (1 -eq 1)

Unregister-Event -SourceIdentifier volumeChange", drive_name);

            System.IO.Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), String.Format("barry")));
            string target_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), String.Format("barry\\{0}.ps1", drive_name));

            if (File.Exists(target_path)) { File.Delete(target_path); }
            File.WriteAllText(target_path, script);
        }

        // create a new scheduled task named 'BarryAutorun'
        // it launch '%appdata%\barry\{drive_name}.ps1' at Logon
        public static void add_scheduled_task(string drive_name)
        {
            // run BarrySceduler.exe with option 'run as admin'
            var psi = new ProcessStartInfo();
            psi.FileName = Path.Combine(methods.current_folder(), "\barryscheduler.exe");
            psi.Arguments = drive_name;
            psi.Verb = "runas";

            var process = new Process();
            process.StartInfo = psi;
            process.Start();
            process.WaitForExit();
        }

        /// <summary>
        /// makes a backup of source in destination
        /// if purge is set to true: destination will be a mirror of source: files which are not in the source will be removed
        /// </summary>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <param name="purge"></param>
        public void robocopy(string source, string destination, bool purge = false)
        {
            using (System.Diagnostics.Process p = new System.Diagnostics.Process())
            {
                string purge_opt = "";
                if (purge) { purge_opt = "/Purge"; }

                p.StartInfo.Arguments = String.Format("/C ROBOCOPY {0} {1} /E /XO /w:3 {2}", source, destination, purge_opt);
                p.StartInfo.FileName = "CMD.EXE";
                p.StartInfo.CreateNoWindow = false;
                p.StartInfo.UseShellExecute = false;
                p.Start();
                p.WaitForExit();
            }
        }



    }


}