using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;

// [重要]このコードは、暫定版であり実験用コードを含みます。将来的に設計・実装が変更されます。
// これらのコードを生放送等で配信することは、構いませんが実装者の許可無く無断で
// 放送等で晒し目的で利用することを禁止します。改変・改良は、構いません。
// 不満点があれば、開発者に直接伝達を行うか各自で修正を行い、その理由をコメント等で明記して下さい。

namespace Nlgp1.Common {
	public static class Utility {
		/// <summary>
		/// 文字列を数値に変換します。
		/// またBoolの場合は、True , "1" が tureを返します。
		/// False , "0" は、fasleを返します。
		/// 変換できなかった場合は、型の初期値が返ります。
		/// </summary>
		/// <typeparam name="structT">変換する型</typeparam>
		/// <param name="number">数字</param>
		/// <returns>数値</returns>
		public static TStruct TryParse<TStruct>( string number ) where TStruct : struct {
			if( string.IsNullOrEmpty( number ) ) {
				return default( TStruct );
			} else if( typeof( int ) == typeof( TStruct ) ) {
				int temp = 0;
				if( int.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( long ) == typeof( TStruct ) ) {
				long temp = 0;
				if( long.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( double ) == typeof( TStruct ) ) {
				double temp = 0;
				if( double.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( short ) == typeof( TStruct ) ) {
				short temp = 0;
				if( short.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( ushort ) == typeof( TStruct ) ) {
				ushort temp = 0;
				if( ushort.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( uint ) == typeof( TStruct ) ) {
				uint temp = 0;
				if( uint.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( ulong ) == typeof( TStruct ) ) {
				ulong temp = 0;
				if( ulong.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( float ) == typeof( TStruct ) ) {
				float temp = 0;
				if( float.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( decimal ) == typeof( TStruct ) ) {
				decimal temp = 0;
				if( decimal.TryParse( number , NumberStyles.Any , null , out temp ) )
					return (TStruct)(object)temp;
			} else if( typeof( bool ) == typeof( TStruct ) ) {
				bool temp = false;
				if( bool.TryParse( number , out temp ) )
					return (TStruct)(object)temp;
				if( number == null || number == "0" )
					return (TStruct)(object)false;
				else
					return (TStruct)(object)true;
			}
			return default( TStruct );
		}

		/// <summary>
		/// 絶対パスを相対パスに変換する
		/// </summary>
		/// <param name="absolutePath">絶対パス</param>
		/// <param name="currentPath">相対パスの基準となるパス</param>
		/// <returns></returns>
		public static string GetRelativePath( string absolutePath , string currentPath ) {
			if( Path.GetFileName( currentPath ) != string.Empty ) {
				currentPath += Path.DirectorySeparatorChar;
			}
			Uri currentUri = new Uri( currentPath );
			Uri absoluteUri = new Uri( absolutePath );

			return currentUri.MakeRelativeUri( absoluteUri ).ToString().Replace( Path.AltDirectorySeparatorChar , Path.DirectorySeparatorChar );
		}

		/// <summary>
		/// 列挙型のフィールドにラベル文字列を付加するカスタム属性です。
		/// </summary>
		[AttributeUsage( AttributeTargets.Field , AllowMultiple = false , Inherited = false )]
		public class EnumLabelAttribute : Attribute {
			/// <summary>
			/// ラベル文字列。
			/// </summary>
			private string label;
			/// <summary>
			/// LabeledEnumAttribute クラスの新しいインスタンスを初期化します。
			/// </summary>
			/// <param name="label">ラベル文字列</param>
			public EnumLabelAttribute( string label ) {
				this.label = label;
			}
			/// <summary>
			/// 属性で指定されたラベル文字列を取得する。
			/// </summary>
			/// <param name="value">ラベル付きフィールド</param>
			/// <returns>ラベル文字列</returns>
			public static string GetLabel( Enum value ) {
				Type enumType = value.GetType();
				string name = Enum.GetName( enumType , value );
				if( name != null ) {
					EnumLabelAttribute[] attrs = (EnumLabelAttribute[])enumType.GetField( name ).GetCustomAttributes( typeof( EnumLabelAttribute ) , false );
					if( attrs.Length > 0 )
						return attrs[0].label;
					else
						return value.ToString();
				} else {
					return null;
				}
			}
		}
	}

	public static class XmlExtension{
		/// <summary>
		/// ファイルからXMLドキュメントを読み込みます。
		/// </summary>
		/// <param name="xml"></param>
		/// <param name="path">XMLファイルへのパス</param>
		/// <returns>読み込みに成功したかどうか</returns>
		public static bool TryFileLoadXml( this XmlDocument xml , string path ) {
			if( string.IsNullOrEmpty( path.Trim() ) || File.Exists( path ) == false )
				return false;
			try {
				xml.Load( path );
				return true;
			} catch( Exception ) {
				return false;
			}
		}
		/// <summary>
		/// xpathを指定してInnerTextの取得を試みます。
		/// 取得できない場合は、nullを返します。
		/// </summary>
		/// <param name="node">基準とするノード</param>
		/// <param name="xpath">xpath式</param>
		/// <param name="nsmgr">XmlNamespaceManagerオブジェクト</param>
		/// <returns>取得したInnerText文字列。取得できない場合は、null</returns>
		public static string TryXmlPath( this XmlNode node , string xpath , XmlNamespaceManager nsmgr ) {
			if( node == null )
				return default( string );

			XmlNode temp = node.SelectSingleNode( xpath , nsmgr );
			if( temp != null )
				return temp.InnerText;
			return null;
		}
		/// <summary>
		/// xpathを指定してInnerTextの取得を試みます。
		/// 取得できない場合は、nullを返します。
		/// </summary>
		/// <param name="node">基準とするノード</param>
		/// <param name="xpath">xpath式</param>
		/// <returns>取得したInnerText文字列。取得できない場合は、null</returns>
		public static string TryXmlPath( this XmlNode node , string xpath  ) {
			return node.TryXmlPath( xpath , null );
		}
		/// <summary>
		/// xpathを指定してInnerTextの取得を試み、それを指定した型に変換します。
		/// </summary>
		/// <typeparam name="T">変換先の型</typeparam>
		/// <param name="node">基準となるノード</param>
		/// <param name="xpath">xpath式</param>
		/// <param name="nsmgr">XmlNamespaceManagerオブジェクト</param>
		/// <returns>取得した値。取得できない場合は、変換先の型の規定値</returns>
		public static T TryXmlPath<T>( this XmlNode node , string xpath , XmlNamespaceManager nsmgr ) where T : struct {
			if( node == null )
				return default( T );

			string temp = TryXmlPath( node , xpath , nsmgr );
			if( temp != null )
				return Utility.TryParse<T>( temp );
			return default( T );
		}
		/// <summary>
		/// xpathを指定してInnerTextの取得を試み、それを指定した型に変換します。
		/// </summary>
		/// <typeparam name="T">変換先の型</typeparam>
		/// <param name="node">基準となるノード</param>
		/// <param name="xpath">xpath式</param>
		/// <param name="nsmgr">XmlNamespaceManagerオブジェクト</param>
		/// <returns>取得した値。取得できない場合は、変換先の型の規定値</returns>
		public static T TryXmlPath<T>( this XmlNode node , string xpath  ) where T : struct {
			return node.TryXmlPath<T>( xpath , null );
		}
		/// <summary>
		/// xPath式でノードのコレクションを取得します。
		/// </summary>
		/// <param name="node">基準のノード</param>
		/// <param name="xpath">xPath式</param>
		/// <returns>XmlNodeのコレクション</returns>
		public static IEnumerable<XmlNode> TryXmlPathCollection( this XmlNode node , string xpath ) {
			if( node == null )
				yield break;

			XmlNodeList nodes = node.SelectNodes( xpath );
			if( nodes != null )
				lock( nodes )
					foreach( XmlNode temp in nodes )
						yield return temp;
		}
		/// <summary>
		/// xpathを指定して属性値を取得します。
		/// 取得できない場合は、nullを返します。
		/// </summary>
		/// <param name="node">基準とするノード</param>
		/// <param name="xpath">xPath式。基準ノードの属性を取得する場合は、nullを指定します。</param>
		/// <param name="attributeName">属性名</param>
		/// <returns>取得したInnerText文字列。取得できない場合は、null</returns>
		public static string TryAttrXmlPath( this XmlNode node , string xpath , string attributeName ) {
			if( node == null )
				return default( string );

			if( xpath == null )
				return node.Attributes.TryGet( attributeName );

			XmlNode temp = node.SelectSingleNode( xpath );
			if( temp != null )
				return temp.Attributes.TryGet( attributeName );
			return null;
		}
		/// <summary>
		/// xPathを使用して属性値を指定の型で取得します。
		/// 取得できない場合は、型の規定値を返します。
		/// </summary>
		/// <typeparam name="T">変換先の型</typeparam>
		/// <param name="node">基準のxmlノード</param>
		/// <param name="xpath">xPath式</param>
		/// <param name="attributeName">属性名</param>
		/// <returns>取得した値</returns>
		public static T TryAttrXmlPath<T>( this XmlNode node , string xpath , string attributeName ) where T : struct {
			if( node == null )
				return default( T );
			string temp = TryAttrXmlPath( node , xpath , attributeName );
			if( temp != null )
				return Utility.TryParse<T>( temp );
			return default( T );
		}
		/// <summary>
		/// xmlノードから属性値を取得します。
		/// </summary>
		/// <param name="node"></param>
		/// <param name="attributeName">属性名</param>
		/// <returns>属性値。取得できない場合は、null値</returns>
		public static string TryGet( this XmlAttributeCollection node , string attributeName ) {
			if( node[attributeName] != null )
				return node[attributeName].InnerText;
			return null;
		}

		public static T[] ToSequence< T >( this IDictionary< int , T > source ) {
			T[] target = new T[ source.Max( item => item.Key ) ];
			foreach( var item in source ) {
				target[ item.Key ] = item.Value;
			}
			return target;
		}

		public static int ToInt32( this string source ) {
			return int.Parse( source );
		}

		public static double ToDouble( this string source ) {
			return double.Parse( source );
		}

		public static TEnum ToEnum< TEnum >( this string source ) {
			return ( TEnum )( Enum.Parse( typeof( TEnum ) , source ) );
		}

		public static IEnumerable< XmlElement > GetChildElements( this XmlNode node , string name ) {
			return node.OfType< XmlNode >().Where( childNode => childNode.NodeType == XmlNodeType.Element && childNode.Name == name ).Cast< XmlElement >();
		}

		public static XmlElement GetChildElement( this XmlNode node , string name ) {
			return ( XmlElement )( node.OfType< XmlNode >().SingleOrDefault( childNode => childNode.NodeType == XmlNodeType.Element && childNode.Name == name ) );
		}

		public static string GetText( this XmlNode node ) {
			XmlNode textNode = node.GetTextNode();
			if( textNode != null ) {
				return textNode.Value;
			}
			else {
				return null;
			}
		}

		public static XmlText GetTextNode( this XmlNode node ) {
			return ( XmlText )( node.OfType< XmlNode >().SingleOrDefault( childNode => childNode.NodeType == XmlNodeType.Text ) );
		}

		public static XmlElement AddChildElement( this XmlNode node , string name ) {
			XmlElement element = node.GetDocument().CreateElement( name );
			node.AppendChild( element );
			return element;
		}

		public static void SetText( this XmlNode node , string text ) {
			XmlText textNode = node.GetTextNode();
			if( textNode != null ) {
				textNode.Value = text;
			}
			else {
				node.AppendChild( node.GetDocument().CreateTextNode( text ) );
			}
		}

		public static XmlDocument GetDocument( this XmlNode node ) {
			if( node.NodeType == XmlNodeType.Document ) {
				return ( XmlDocument )( node );
			}
			else {
				return node.OwnerDocument;
			}
		}
	}
}
