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

using CommonHelpers;

namespace SqlCatPP {

//**************************************************************************************************
partial class Scripter {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
/// <exception cref="OperationFailedException" />
public void CompleteOutput(string outputFilePath)
{
   HArgChecking.VerifyNotNull( outputFilePath );
   VerifyHasScriptWriter();

   var scriptText = _ScriptWriter_.ToString();

   ResetParseState();
   _ScriptWriter_ = null;
   _SetVariables_ = null;

   CompleteOutputEx( outputFilePath, scriptText );
   string
      outputFolderPath = Program.OutputFolderPath,
      outputFileName = Program.OutputFileName;
   if ( outputFolderPath==null || outputFileName==null )
      return;
   OpFailedExceptionCreator.Do(() =>
      outputFilePath = Path.Combine( outputFolderPath, outputFileName ));
   bool createFolder;
   try
   {
      createFolder = !Directory.Exists( outputFolderPath );
   }
   catch (Exception e)
   {
      throw OpFailedExceptionCreator.Create(
         "Can not check existence of explicit output-file folder: \"{0}\".\n"+
         "{1}", outputFolderPath, e.Message );
   }
   if ( createFolder )
      OpFailedExceptionCreator.Do(() => Directory.CreateDirectory( outputFolderPath ));
   OpFailedExceptionCreator.Do(() => HFile.RemoveReadOnlyFile( outputFilePath, false ));
   CompleteOutputEx( outputFilePath, scriptText );
   OpFailedExceptionCreator.Do(() =>
      HFile.MakeFileReadOnly( outputFilePath ));
   if ( WithPosteriorCleanup() )
      CleanGeneratedPosterior();
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
/// <exception cref="OperationFailedException" />
void CompleteCurrentOutput(string outputFilePath,string scriptTextEmbedding=null)
{
   HArgChecking.VerifyNotNull( outputFilePath );
   if ( scriptTextEmbedding == null )
      VerifyHasCurrentScriptWriter();
   string
      scriptText = scriptTextEmbedding ?? HOps.NullExchange(ref _CurrentScriptWriter_).ToString(),
      folderPath = null, fileName = null;
   OpFailedExceptionCreator.Do(() => {
      folderPath = Path.GetDirectoryName( outputFilePath );
      fileName = Path.GetFileName( outputFilePath );
   });
   bool exists = HFile.TryCheckExistence( outputFilePath, out var error );
   if ( !exists && !(error is FileNotFoundException) )
      throw OpFailedExceptionCreator.Create( error );
   if ( scriptTextEmbedding==null && EmbedFileNamesToOrder() )
   {
      VerifyHasGenFolderPath();
      string[] filePaths = null;
      OpFailedExceptionCreator.Do(() => {
         string fn = _InputFileName_;
         if ( fn.StartsWith(".") || fn.StartsWith("!") )
            fn = fn.Remove( 0, 1 );
         filePaths = Directory.GetFiles( GenFolderPath, string.Format( "*.{0}", fn ) );
      });
      Array.Sort( filePaths, CaseInsensitiveComparerStr.Instance );
      bool wasEmbedded = false;
      foreach (string filePath in filePaths)
      {
         string fn = Path.GetFileName( filePath );
         if ( HasOrderPrefix( fn, out var isSameHeadingChar ) && isSameHeadingChar )
         {
            CompleteCurrentOutput( Path.Combine(folderPath,fn), scriptText );
            wasEmbedded = true;
         }
      }
      if ( !exists && wasEmbedded )
         return;
      if ( !exists )
         fileName = _InputFileName_;
   }
   DateTime? lastWriteTimeUTC = null;
   if ( RestoreDateIfSameContent() && exists )
   {
      StreamReader reader = null;
      OpFailedExceptionCreator.Do(() =>
         reader = new StreamReader( outputFilePath, Program.InputEncoding, true ));
      string previousContent = null;
      try
      {
         previousContent = reader.ReadToEnd();
         HOps.NullExchange(ref reader).Close();
      }
      catch (Exception e)
      {
         if ( reader != null )
            try { reader.Close(); }
            catch {}
         if (!( e is FileNotFoundException ))
            throw OpFailedExceptionCreator.Create(
               "Can not read file \"{0}\" for content collation.\n"+
               "{1}", outputFilePath, e.Message );
      }
      if ( scriptText == previousContent )
         OpFailedExceptionCreator.Do(() =>
            lastWriteTimeUTC = File.GetLastWriteTimeUtc( outputFilePath ) );
   }
   CompleteOutputEx( outputFilePath, scriptText );
   if ( lastWriteTimeUTC.HasValue )
      OpFailedExceptionCreator.Do(() =>
         File.SetLastWriteTimeUtc( outputFilePath, lastWriteTimeUTC.Value ));
   OpFailedExceptionCreator.Do(() =>
      HFile.MakeFileReadOnly( outputFilePath ));
   if ( _GenFileTable_.TryGetValue( fileName, out var genFileData ) )
      genFileData.Generated = true;
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
/// <exception cref="OperationFailedException" />
void CompleteOutputEx(string outputFilePath,string scriptText)
{
   HArgChecking.VerifyNotNulls( outputFilePath, scriptText );

   OpFailedExceptionCreator.Do(() =>
      HFile.RemoveReadOnlyFile( outputFilePath, false ));

   StreamWriter writer;
   try
   {
      writer = new StreamWriter( outputFilePath, false,
         Program.OutputAsANSI ? Encoding.Default : new UTF8Encoding( true ) );
   }
   catch (Exception e)
   {
      throw OpFailedExceptionCreator.Create(
         "Can not create script file for writing: \"{0}\".\n"+
         "{1}", outputFilePath, e.Message );
   }
   try
   {
      try
      {
         writer.Write( scriptText );
         writer.Flush();
      }
      catch (Exception e)
      {
         throw OpFailedExceptionCreator.Create(
            "Can not write to script file: \"{0}\".\n"+
            "{1}", outputFilePath, e.Message );
      }
      try
      {
         HOps.NullExchange(ref writer).Close();
      }
      catch (Exception e)
      {
         throw OpFailedExceptionCreator.Create(
            "Can not close script file: \"{0}\".\n"+
            "{1}", outputFilePath, e.Message );
      }
   }
   finally
   {
      HDispose.Free( writer );
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // Scripter
//**************************************************************************************************

} // SqlCatPP
