//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Collections.Generic;

#if !WASM
namespace user_rtx { using u = user_rtx;
#else
namespace user_rtx.wasm { using u = user_rtx.wasm;
#endif

partial class XmlConfigurationBase {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="InvalidOperationException" />
protected string GetElementPath( XmlElement element )
{
   VerifyDocumentLoaded();
   var sb = new StringBuilder();
   for ( XmlNode? node=element; node!=null; node=node.ParentNode )
      sb.Insert( 0, "/" + node.Name );
   var path = sb.ToString();
   return path.StartsWith( "/#document/" ) ?
      path.Remove( 0, 10 ) : path;
}

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

/// <exception cref="InvalidOperationException" />
protected XmlNode? GetCurrentElement()
{
   VerifyDocumentLoaded();
   return _currentElement;
}

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

/// <exception cref="InvalidOperationException" />
protected string NormalizeElementPath( string elementPath )
{
   VerifyDocumentLoaded();
   return elementPath.StartsWith( "/" ) ? elementPath :
      string.Format( "/{0}/{1}", DocumentElement.Name, elementPath );
}

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

/// <exception cref="InvalidOperationException" />
protected bool HasAttribute( string name ) =>
   AttributeValueN( name ) != null;

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

/// <exception cref="InvalidOperationException" />
protected string? AttributeValueN( string name ) =>
   CurrentElement.Attributes[ name ]?.Value;

/// <exception cref="InvalidOperationException" />
/// <exception cref="EXmlConfiguration" />
[u.strict_result]
protected string StrictAttributeValue( string name ) =>
   StrictAttributeValueOf( CurrentElement.Attributes, name );

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

/// <exception cref="InvalidOperationException" />
protected string? SetAttributeValue( string name, string? value )
{
   CurrentElement.SetAttribute( name, value );
   return value;
}

/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
protected string? SetAttributeValueAddRemove( string name, string? value )
{
   if ( value != null )
      if ( HasAttribute( name ) )
         SetAttributeValue( name, value );
      else {
         var newAttribute = Document.CreateAttribute( name );
         newAttribute.Value = value;
         CurrentElement.Attributes.Append( newAttribute );
      }
   else
      RemoveAttribute( name, true );
   return value;
}

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

/// <exception cref="InvalidOperationException" />
protected XmlElement SelectDocumentElement() =>
   CurrentElement = Document.DocumentElement.NN();

/// <exception cref="InvalidOperationException" />
protected XmlElement SetCurentElement( XmlElement element ) =>
   CurrentElement = element;

/// <exception cref="InvalidOperationException" />
protected void ResetCurentElement()
{
   VerifyDocumentLoaded();
   _currentElement = null;
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="EXmlConfiguration" />
protected XmlElement NavigateTo( string elementPath )
{
   if ( !TryNavigateTo( elementPath ) )
      throw CreateExceptionWithFile(u.local.T(
         RC._XML_element_path_is_not_found_0,
         "Не найден путь к XML-элементу: “{0}”." ),
         NormalizeElementPath( elementPath ));
   return CurrentElement;
}

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

/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
protected bool TryNavigateTo( string elementPath )
{
   elementPath = NormalizeElementPath( elementPath );
   var sb = new StringBuilder();
   foreach (char ch in elementPath)
      if ( ch == '/' ) sb.Append( "/x:" );
      else sb.Append( ch );
   XmlNode? node = Document.SelectSingleNode(
      sb.ToString(), NamespaceManager );
   if ( node == null )
      return false;
   if ( !(node is XmlElement) )
      throw e_argument.New(u.local.T(
         RC._Incorrect_path_for_navigation_0__Path_to_XML_element_was_expected,
         "Некорректный путь навигации: “{0}”.\n"+
         "Ожидался маршрут к элементу XML." ),
         elementPath );
   CurrentElement = (XmlElement)node;
   return true;
}

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

/// <exception cref="InvalidOperationException" />
protected XmlNode[] GetChildElements() =>
   GetChildElementList().ToArray();

protected static XmlNode[] GetChildElements( XmlNode node ) =>
   GetChildElementList( node ).ToArray();

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

/// <exception cref="InvalidOperationException" />
protected List<XmlNode> GetChildElementList() =>
   GetChildElementList( CurrentElement );

protected static List<XmlNode> GetChildElementList( XmlNode node )
{
   var nodeList = new List<XmlNode>();
   foreach ( XmlNode childNode in node.ChildNodes )
      if ( childNode.NodeType == XmlNodeType.Element )
         nodeList.Add( childNode );
   return nodeList;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // XmlConfigurationBase

} // user_rtx (.wasm)
