using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

using CommonHelpers;

namespace SqlCatPP {

//**************************************************************************************************
partial class OrderList {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public readonly string OrderFilePath;
[HOptionalValue]
public readonly string SubfolderPathOrNull;

//==================================================================================================

/// <exception cref="OperationFailedException" />
public OrderList(string subfolderPath=null)
{
   if ( subfolderPath != null )
      FileNameHlp.VerifyFolderExists( subfolderPath );
   OrderFilePath = subfolderPath != null ?
      Path.Combine( subfolderPath, Program.OrderListFileName ) :
      Program.OrderListFileName;
   SubfolderPathOrNull = subfolderPath;
}

//==================================================================================================

/// <exception cref="OperationFailedException" />
public string[] PrepareFileOrder(IEnumerable<string> fileNames)
{
   HArgChecking.VerifyNotNull( fileNames );
   foreach (string fileName in fileNames)
      _FileNameOccurance_[ fileName ] = true;
   if ( FileNameHlp.CheckFileExists( OrderFilePath ) )
      try
      {
         LoadAndSiftItems();
      }
      catch (Exception e)
      {
         if ( e.Data[_FullMessageExceptionKey_] != null )
            throw;
         throw OpFailedExceptionCreator.Create(
            "Can not load file: \"{0}\".\n"+
            "{1}", OrderFilePath, e.Message );
      }
// Instruct CreateListException & RaiseListError so as out of list context:
   int itemCount = HOps.DefaultExchange(ref _LineNo_);
   var list = _FoundItems_;
   var rest = new List<string>();
   foreach (string fileName in fileNames)
      if ( !_ItemOccurance_.ContainsKey( fileName ) )
         if ( fileName.StartsWith( "!" ) )
            list.Insert( 0, fileName );
         else
         if ( fileName.StartsWith( "." ) && !HString.Equals( fileName, ".sql", true ) )
            RaiseListError(
               "Dot prefixed (\".\") file \"{0}\" is not specified in the order list.",
               GetFileDisplay( fileName ) );
         else
            rest.Add( fileName );
   list.AddRange( rest );
   return list.ToArray();
}

//==================================================================================================

/// <exception cref="SystemException" />
/// <exception cref="OperationFailedException" />
void LoadAndSiftItems()
{
   StreamReader reader = null;
   Exception error = null;
   try
   {
      reader = new StreamReader( OrderFilePath, Program.InputEncoding, true );
      string str;
      while ( (str = reader.ReadLine()) != null )
      {
         _LineNo_++;
         if ( str=="" || str[0]==';' )
            continue;
         if ( HString.HasBoundaryWhiteSpace( str ) )
         {
            RaiseListError(
               "Translation order items can not have leading and/or trailing space(s)." );
            continue;
         }
         if ( _ItemOccurance_.TryGetValue( str, out int occuranceLineNo ) )
         {
            RaiseListError(
               "Translation order item has allready been specified in line {0}.",
               LineNo );
            continue;
         }
         if ( _FileNameOccurance_.ContainsKey( str ) )
            _FoundItems_.Add( str );
         else
            RaiseListError(
               "Reference to unexisting file name: \"{0}\".{1}",
               str,
               SubfolderPathOrNull != null ?
                  string.Format( "\n(Processing subfolder: \"{0}\".)", SubfolderPathOrNull ) :
                  "" );
         _ItemOccurance_[ str ] = occuranceLineNo;
         _ListItems_.Add( str );
      }
   }
   catch (Exception e)
   {
      error = e; throw;
   }
   finally
   {
      if ( reader != null )
         try
         {
            reader.Close(); reader.Dispose();
         }
         catch (Exception e)
         {
            if ( error == null )
               throw OpFailedExceptionCreator.Create( error = e );
         }
   }
}

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

/// <exception cref="ArgumentNullException" />
[HNotNull]
string GetFileDisplay(string fileName)
{
   HArgChecking.VerifyNotNull( fileName );
   return SubfolderPathOrNull != null ? 
      Path.Combine( SubfolderPathOrNull, fileName ) :
      fileName;
}

//==================================================================================================

int LineNo => _LineNo_;

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

readonly Dictionary<string,bool>
   _FileNameOccurance_ = new Dictionary<string,bool>( StringComparer.InvariantCultureIgnoreCase );
readonly Dictionary<string,int>
   _ItemOccurance_ = new Dictionary<string,int>( StringComparer.InvariantCultureIgnoreCase );
readonly List<string>
   _ListItems_ = new List<string>(),
   _FoundItems_ = new List<string>();
readonly object
   _FullMessageExceptionKey_ = new object();

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

int _LineNo_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // OrderList
//**************************************************************************************************

} // SqlCatPP
