using PbpCreator;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace BinToPbp
{
  class Program
  {

    class Process : IReporting
    {
      #region Private methods

      private void PrintLn(ConsoleColor color, string text, params object[] args)
      {
        Console.ForegroundColor = color;
        Console.WriteLine(text, args);
        Console.ResetColor();
      }

      private void Usage()
      {
        PrintLn(ConsoleColor.Yellow, "Usage: BinToPdb.exe <source folder> <destination folder>");
        PrintLn(ConsoleColor.White, "       <source folder> Root folder of *.bin files");
        PrintLn(ConsoleColor.White, "       <destination folder> Folder where to write *.pbp files");
        Environment.Exit(0);
      }

      private List<string> GetSortedSourceFiles(string sourceFolder)
      {
        HashSet<string> validExtentions = new HashSet<string>();
        foreach (string ext in "bin,iso".Split(','))
          validExtentions.Add('.' + ext);

        DirectoryInfo directoryInfo = new DirectoryInfo(sourceFolder);
        List<string> unfilteredItems = directoryInfo.EnumerateFiles(@"*.*", SearchOption.AllDirectories)
                                          .AsParallel()
                                          .Where(file => (file.Extension.Length != 0) && validExtentions.Contains(file.Extension.ToLowerInvariant()))
                                          .Select(file => file.FullName)
                                          .ToList();

        return unfilteredItems;
      }

      #endregion

      #region Public methods

      private static readonly Regex[] _CDSuffixes = new Regex[]
      {
        new Regex(@"(.*?)([\s,-][cC][dD][0-9])(.*?)", RegexOptions.Compiled),
        new Regex(@"(.*?)([\s\(][dD]is[ck]\s?[0-9]\)?)(.*?)", RegexOptions.Compiled),
      };

      public void Run(string source, string destination)
      {
        if (source == null || destination == null)
        {
          PrintLn(ConsoleColor.Red, "Missing arguments");
          Usage();
        }

        PrintLn(ConsoleColor.Green, "Scanning source files...");
        List<string> sourceFiles = GetSortedSourceFiles(source);
        PrintLn(ConsoleColor.White, "  {0} file(s) found", sourceFiles.Count);

        PbpManager manager = new PbpManager();

        Stopwatch watch = new Stopwatch();
        watch.Start();
        while (sourceFiles.Count != 0)
        {
          // Add primary file
          string primary = sourceFiles[0];
          sourceFiles.RemoveAt(0);
          List<string> sourceList = new List<string>
          {
            primary
          };
          string destinationFile = primary;
          string gameName = primary;

          // Look for other CD
          int suffixLength = -1;
          Regex regex = null;
          foreach (Regex r in _CDSuffixes)
          {
            Match match = r.Match(primary);
            if (match.Success)
            {
              suffixLength = match.Groups[2].Index;
              regex = r;
              break;
            }
          }
          // Suffix found?
          if (suffixLength >= 0)
          {
            string suffix = primary.Substring(0, suffixLength);
            // Reverse order for safe deletion
            for (int j = sourceFiles.Count; --j >= 0;)
            {
              string secondary = sourceFiles[j];
              if (secondary.StartsWith(suffix))
                if (regex.Match(secondary).Groups[2].Index == suffixLength)
                {
                  // Newt CD found, add to the source list, remove from file list
                  sourceList.Add(secondary);
                  sourceFiles.RemoveAt(j);
                }
            }
            // Update destination filename / game name
            destinationFile = regex.Replace(destinationFile, string.Format("$1({0} CD)$3", sourceList.Count));
            gameName = suffix.Trim('-', ' ');
          }

          // Sort (work with up to 9 CDs, not more)
          if (sourceList.Count > 1)
            sourceList.Sort();

          // Set configuration
          Configuration conf = new Configuration()
          {
            Sources = sourceList,
            DestinationFile = Path.ChangeExtension(Path.Combine(destination, Path.GetFileName(destinationFile)), ".pbp"),
            GameTitle = Path.GetFileName(gameName)
          };

          // Run!
          manager.ToPbp(conf, this, true);
          PrintLn(ConsoleColor.Green, "\r\n{0} file remaining!", sourceFiles.Count);
        }
        watch.Stop();
        PrintLn(ConsoleColor.Green, "\r\nCompleted in {0} seconds!", watch.Elapsed.ToString(@"mm\:ss"));
      }

      #endregion

      #region IReporting implementation

      void IReporting.Log(ConsoleColor color, string text, params object[] args)
      {
        PrintLn(color, text, args);
      }

      private int _PreviousFile = - 1;
      private int _PreviousPercent = - 1;

      void IReporting.Progress(int file, int progress, int total)
      {
        int percent = (progress * 100) / total;
        if (_PreviousFile != file) { _PreviousPercent = -1; _PreviousFile = file; }
        if (_PreviousPercent == percent) return;
        _PreviousPercent = percent;

        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.Write("\r  Progress: {0:##0}%", percent);
        Console.ResetColor();
      }

      #endregion
    }

    static void Main(string[] args)
    {
      string source = args.Length > 0 ? args[0] : null;
      string destination = args.Length > 1 ? args[1] : null;

      Process runner = new Process();
      runner.Run(source, destination);

      //Console.In.ReadLine();
     }
  }
}
