//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace CommonHelpers {

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

//**************************************************************************************************
partial class LnkProperties {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

LnkPropertyType PropertyType(LnkProperty property)
{
   if (HOps.IsOneOf(property,_StringProperties_))
      return LnkPropertyType.String;
   else
   if (HOps.IsOneOf(property,_IntegerProperties_))
      return LnkPropertyType.Integer;
   else
   if (HOps.IsOneOf(property,_UShortProperties_))
      return LnkPropertyType.UShort;
   else
   if (HOps.IsOneOf(property,_FlagProperties_))
      return LnkPropertyType.Flag;
   else
   if (HOps.IsOneOf(property,_ByteArrayProperties_))
      return LnkPropertyType.ByteArray;
   throw HAssertionFailure.DEAD_BRANCH();
}

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

/// <exception cref="HOperationFailedException" />
bool ParsePropertyLine(string textLine)
{
   if ( textLine == null )
      return false;
   int sp = 0;
   if ( HString.SkipWhiteSpaces( textLine, ref sp ) == textLine.Length ||
        textLine[ sp ] == LineCommentChar )
      return false;

   int equalityPos = textLine.IndexOf( '=' );
   if ( equalityPos <= 0 ) goto NOT_PROPERTY_LINE;

   int valueStartPos = equalityPos + 1;
   HString.SkipWhiteSpaces( textLine, ref valueStartPos );

   string
      propertyName = textLine.Substring( 0, equalityPos ).Trim(),
      stringValue = textLine.Substring( valueStartPos ).TrimEnd();
   if ( propertyName == "" )
      goto NOT_PROPERTY_LINE;

   LnkPropertyType propertyType;

   if (HString.IsOneOf(true,propertyName,_StringPropertyNames_))
      propertyType = LnkPropertyType.String;
   else
   if (HString.IsOneOf(true,propertyName,_IntegerPropertyNames_))
      propertyType = LnkPropertyType.Integer;
   else
   if (HString.IsOneOf(true,propertyName,_UShortPropertyNames_))
      propertyType = LnkPropertyType.UShort;
   else
   if (HString.IsOneOf(true,propertyName,_FlagPropertyNames_))
      propertyType = LnkPropertyType.Flag;
   else
   if (HString.IsOneOf(true,propertyName,_ByteArrayPropertyNames_))
      propertyType = LnkPropertyType.ByteArray;
   else
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.UNKNOWN_SHORTCUT_PROPERTY_0,
         propertyName );

   propertyName = HString.StrictNormilizeCaseByList( propertyName, _PropertyNames_ );

   object value;

   try
   {
      if ( propertyType == LnkPropertyType.ByteArray )
         throw HOpFailedExceptionCreator.Create(
            HLnkConverter.Verbose.UNSUPPORTED_SHORTCUT_PROPERTY_0__UNSUPPORTED_TYPE_1,
            propertyName, propertyType );
      if ( stringValue == "" && propertyType != LnkPropertyType.Flag )
         value = null;
      else
         switch ( propertyType )
         {
         case LnkPropertyType.String:
            value = stringValue;
            break;
         case LnkPropertyType.Integer:
            value = HInt32.ParseInvariant( stringValue );
            break;
         case LnkPropertyType.UShort:
            value = HHexParser.Parse<ushort>( stringValue, HHexParseOptions.RequirePrefix );
            break;
         case LnkPropertyType.Flag:
            value = HFlagParser.Parse( stringValue );
            break;
         default:
            throw HAssertionFailure.DEAD_BRANCH();
         }
   }
   catch (HParseValueException e)
   {
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.CAN_NOT_OBTAIN_VALUE_OF_PROPERTY_0_OF_TYPE_1__2,
         propertyName, propertyType, e.Message );
   }

   var property = (LnkProperty)Enum.Parse(
      typeof(LnkProperty), propertyName, true );

   if ( _Properties_[ property ] != null )
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.REPEATING_PROPERTY_0,
         propertyName );

   if ( propertyType == LnkPropertyType.String &&
        stringValue != "" && _VariableProcessor_ != null )
      try {
         value = _VariableProcessor_.ProcessVariables( stringValue, valueStartPos ); }
      catch (HOperationFailedException e) {
         throw HOpFailedExceptionCreator.Create(
            HLnkConverter.Verbose.CAN_NOT_PROCESS_ENVIRONMENT_VARIABLES_FOR_PROPERTY_0__1,
            propertyName, e.Message ); }

   _Properties_[ property ] = value;

   return true;

NOT_PROPERTY_LINE:
   throw new HOperationFailedException(
      HLnkConverter.Verbose.THE_LINE_IS_NOT_OF_NAME_VALUE_PATTERN );
}

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

/// <exception cref="HOperationFailedException" />
string ValueToPropertyLine(LnkProperty property,[HOptional] object value)
{
   LnkPropertyType
      propertyType = PropertyType( property );
   string
      propertyName = HEnum.GetNameOf<LnkProperty>( property );

   if ( propertyType == LnkPropertyType.ByteArray )
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.UNSUPPORTED_SHORTCUT_PROPERTY_0__UNSUPPORTED_TYPE_1,
         propertyName, propertyType );

   string stringValue;

   if ( value != null )
      switch ( propertyType )
      {
      case LnkPropertyType.String:
         stringValue = (string)value;
         break;
      case LnkPropertyType.Integer:
         stringValue = HInt32.ToNormalString( (int)value );
         break;
      case LnkPropertyType.UShort:
         stringValue = HUInt16.ToHexString( (UInt16)value, true );
         break;
      case LnkPropertyType.Flag:
         stringValue = HBool.FormatAsFlag( (bool)value );
         break;
      default:
         throw HAssertionFailure.DEAD_BRANCH();
      }
   else
      switch ( propertyType )
      {
      case LnkPropertyType.Flag:
         stringValue = "0";
         break;
      default:
         stringValue = "";
         break;
      }

   return string.Format( "{0} = {1}", property, stringValue );
}

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

void Clear()
{
   _Properties_.Clear();
}

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

/// <exception cref="HOperationFailedException" />
void LoadFromTextFile(string filePath)
{
   Clear();
   StreamReader
      reader;
   try
   {
      reader = new StreamReader( filePath, true );
   }
   catch (Exception e)
   {
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.CAN_NOT_OPEN_FILE_0__1,
         GetFilePathForErrorInfo( filePath ), e.Message );
   }
   int lineNo = 1;
   try
   {
      while (!(reader.EndOfStream))
      {
         ParsePropertyLine( reader.ReadLine() );
         lineNo++;
      }
   }
   catch (Exception e)
   {
      try
      {
         if (e is IOException)
            throw HOpFailedExceptionCreator.Create(
               HLnkConverter.Verbose.FILE_READING_ERROR_0__1,
               GetFilePathForErrorInfo( filePath ), e.Message );
         if (e is HOperationFailedException)
            throw HOpFailedExceptionCreator.Create(
               HLnkConverter.Verbose.SYNTAX_ERROR_IN_FILE_0_LINE_1__2,
               GetFilePathForErrorInfo( filePath ), lineNo, e.Message );
         throw;
      }
      finally
      {
         try { reader.Close(); } catch {}
      }
   }
   try
   {
      reader.Close();
   }
   catch (Exception e)
   {
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.CAN_NOT_CLOSE_FILE_0__1,
         GetFilePathForErrorInfo( filePath ), e.Message );
   }
}

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

/// <exception cref="HOperationFailedException" />
public void SaveToTextFile(string filePath)
{
   StreamWriter
      writer;
   try
   {
      writer = new StreamWriter( filePath, false, new UTF8Encoding(true) );
   }
   catch (Exception e)
   {
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.CAN_NOT_CREATE_FILE_0__1,
         GetFilePathForErrorInfo( filePath ), e.Message );
   }
   Action<KeyValuePair<LnkProperty,object>> writeLine =
      delegate ( KeyValuePair<LnkProperty,object> record )
         { writer.WriteLine( ValueToPropertyLine( record.Key, record.Value ) ); };
   try
   {
      //var list = new SortedList( _Properties_, HAsStringComparer.CreateInvariant(true) );
      //foreach (DictionaryEntry entry in list)
      //   writer.WriteLine( ValueToPropertyLine( (LnkProperty)entry.Key, entry.Value ) );
      //foreach (var record in _Properties_.ToList(HAsStringComparer.CreateInvariant(true)))
      //   writer.WriteLine( ValueToPropertyLine( record.Key, record.Value ) );
      var recordList = _Properties_.ToList( HAsStringComparer.CreateInvariant(true) );
      foreach (var record in recordList)
         if ( !IsSpecialProperty( record.Key ) )
            writeLine( record );
      foreach (var record in recordList)
         if ( IsSpecialProperty( record.Key ) )
            writeLine( record );
   }
   catch (Exception e)
   {
      try
      {
         if (e is IOException)
            throw HOpFailedExceptionCreator.Create(
               HLnkConverter.Verbose.ERROR_OF_WRITING_IN_FILE_0__1,
               GetFilePathForErrorInfo( filePath ), e.Message );
         throw;
      }
      finally
      {
         try { writer.Close(); } catch {}
      }
   }
   try
   {
      writer.Close();
   }
   catch (Exception e)
   {
      throw HOpFailedExceptionCreator.Create(
         HLnkConverter.Verbose.CAN_NOT_CLOSE_FILE_0__1,
         GetFilePathForErrorInfo( filePath ), e.Message );
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // CommonHelpers
//**************************************************************************************************

} // LnkConverter
