/*!
@file	Classes/System/IO/Path.ab
@brief	ファイルパス文字列を操作する
@author	OverTaker
*/

Namespace System
Namespace IO

Class Path
Public
	Static Const AltDirectorySeparatorChar = &H2F As Char '/
	Static Const DirectorySeparatorChar    = &H5C As Char '\
	Static Const PathSeparator             = &H3B As Char ';
	Static Const VolumeSeparatorChar       = &H3A As Char ':

	'----------------------------------------------------------------
	' パブリック メソッド
	'----------------------------------------------------------------

	/*
	@brief  拡張子を変更する
	@param  パス
	@param  変更する拡張子(.を含む)
	@return 拡張子変更後のパス
	*/
	Static Function ChangeExtension(path As String, extension As String) As String
		path = CheckPath(path)
		If HasExtension(path) Then
			Return path.Remove(GetExtensionIndex(path)) + extension
		Else
			Return path + extension
		End If
	End Function

	/*
	@brief  二つのパスを結合する
	@param  結合されるパス
	@param  結合するパス
	@return パス
	*/
	Static Function Combine(path1 As String, path2 As String) As String
		path1 = CheckPath(path1)
		path2 = CheckPath(path2)
		If IsPathRooted(path2) Then
			Return path2
		Else
			If Not path1.EndsWith(DirectorySeparatorChar) Then
				path1 += Chr$(DirectorySeparatorChar)
			End If
			Return path1 + path2
		End If
	End Function

	/*
	@brief  パス文字列からファイル名を取得する
	@param  パス
	@return ファイル名
	*/
	Static Function GetFileName(path As String) As String
		path = CheckPath(path)
		Dim lastSeparatorIndex = GetLastSeparatorIndex(path) As Long
		If lastSeparatorIndex <> -1 Then
			Return path.Substring(lastSeparatorIndex + 1)
		Else
			Return path
		End If
	End Function

	/*
	@brief  パス文字列からファイル名を拡張子を除いて取得する
	@param  パス
	@return 拡張子を除いたファイル名
	*/
	Static Function GetFileNameWithoutExtension(path As String) As String
		path = GetFileName(path)
		If HasExtension(path) Then
			Return path.Remove(GetExtensionIndex(path))
		Else
			Return path
		End If
	End Function

	/*
	@brief  絶対パスを取得する
	@param  相対パス
	@return 絶対パス
	*/
	Static Function GetFullPath(path As String) As String
		path = CheckPath(path)
		If IsPathRooted(path) Then
			Return path
		Else
			Return Combine(System.Environment.CurrentDirectory, path)
		End If
	End Function

	/*
	@brief  パス文字列から拡張子を取得する
	@param  パス
	@return .を含む拡張子
	*/
	Static Function GetExtension(path As String) As String
		path = CheckPath(path)
		Dim extensionIndex = GetExtensionIndex(path)
		If extensionIndex <> -1 Then
			Return path.Substring(extensionIndex)
		Else
			Return New String()
		End If
	End Function

	/*
	@brief  ひとつ上のディレクトリを取得する
	@param  パス
	@return ディレクトリパス
	*/
	Static Function GetDirectoryName(path As String) As String
		path = CheckPath(path)
		Dim lastSeparatorIndex = GetLastSeparatorIndex(path)
		If lastSeparatorIndex = -1 Then
			Return New String()
		Else
			Return path.Substring(0, lastSeparatorIndex)
		End If
	End Function

	/*
	@brief  ルートディレクトリを取得する
	@param  パス
	@return ルートディレクトリ
	*/
	Static Function GetPathRoot(path As String) As String
		path = CheckPath(path)
		If IsPathRooted(path) Then
			If path.Contains(UniformNamingConventionString) Then
				Return path.Remove(path.IndexOf(DirectorySeparatorChar,
													  UniformNamingConventionString.Length) + 1)
			Else
				Return path.Remove(3)
			End If
		Else
			Return New String()
		End If
	End Function

	/*!
	@brief	ランダムなファイル名を取得する
	@param	ファイルパス
	@return ファイル名
	手抜き実装
	*/
	Static Function GetRandomFileName() As String
		Randomize
		Return Str$(((Rnd() * 900000000) As Long) + 10000000)
	End Function

	/*!
	@brief	一時ファイルを作成し、ファイルパスを取得する
	@return パス
	*/
	Static Function GetTempFileName() As String
		Dim tempFileName[ELM(MAX_PATH)] As TCHAR
		Dim len = WIN32API_GetTempFileName(GetTempPath(), "ABT", 0, tempFileName)
		If len <> 0 Then
			Return New String(tempFileName, len As Long)
		Else
			Throw New IOException("Path.GetTempFileName: Failed to GetTempFileName.")
		End If
	End Function


	/*
	@brief  システムの一時フォルダのパス取得する
	@return パス
	*/
	Static Function GetTempPath() As String
		Dim size = WIN32API_GetTempPath(0, 0)
		Dim buf = New Text.StringBuilder(size)
		buf.Length = size
		Dim len = WIN32API_GetTempPath(size, StrPtr(buf))
		If (len > size) or len = 0 Then
			Throw New IOException("Path.GetTempPath: Failed to GetTempPath.")
		Else
			buf.Length = len
			Return buf.ToString
		End If
	End Function


	/*
	@brief  パスに拡張子が含まれるかどうか
	@param  パス
	@retval True  含まれる
	@retval False 含まれない
	*/
	Static Function HasExtension(path As String) As Boolean
		path = CheckPath(path)
		Return GetExtensionIndex(path) <> -1
	End Function

	/*
	@brief  パスが絶対パスか取得する
	@param  パス
	@retval True  絶対パス
	@retval False 相対パス
	*/
	Static Function IsPathRooted(path As String) As Boolean
		path = CheckPath(path)
		Return path.StartsWith(UniformNamingConventionString) _
			or path.Contains(VolumeSeparatorChar) _
			or path.StartsWith(DirectorySeparatorChar)
	End Function

Private
	Static Const ExtensionSeparatorChar = &H2E As Char
	Static Const InvalidPathChars = Ex"\q<>|\0\t" As String
	Static Const UniformNamingConventionString = "\\" As String

	'----------------------------------------------------------------
	' プライベート　メソッド
	'----------------------------------------------------------------

	/*
	@brief  パスに不正な文字が含まれていないか調べる。
	@param  パス
	@return 正しく直されたパス
	パス末端にディレクトリ区切り記号があった場合除去する
	..\などが含まれるパスについては未実装
	*/
	Static Function CheckPath(path As String) As String
		path = path.Replace(AltDirectorySeparatorChar, DirectorySeparatorChar)

		Dim i As Long
		For i = 0 To ELM(InvalidPathChars.Length)
			If path.Contains(InvalidPathChars[i]) Then
				Throw New IOException("Path.CheckPath: The path contains invalidPathChars.")
			End If
		Next

		Dim continuousIndex = path.IndexOf("\\", 1)
		While continuousIndex <> -1
			path = path.Remove(continuousIndex, 1)
			continuousIndex = path.IndexOf("\\", continuousIndex)
		Wend

		If path.EndsWith(DirectorySeparatorChar) Then
			Return path.Remove(path.Length - 1)
		Else
			Return path
		End If
	End Function

	/*
	@brief  パスの最も後ろにあるディレクトリ区切り記号の位置を取得する
	@param  パス
	@return インデックス
	*/
	Static Function GetLastSeparatorIndex(path As String) As Long
		Return System.Math.Max(path.LastIndexOf(DirectorySeparatorChar),
							   path.LastIndexOf(AltDirectorySeparatorChar)) As Long
	End Function

	/*
	@brief  拡張子の位置を取得する。
	@param  パス
	@return インデックス
	*/
	Static Function GetExtensionIndex(path As String) As Long
		Dim extensionIndex = path.LastIndexOf(ExtensionSeparatorChar) As Long
		If extensionIndex > GetLastSeparatorIndex(path) Then
			Return extensionIndex
		Else
			Return -1
		End If
	End Function
End Class

'メソッドと名前が被るAPI
Function WIN32API_GetTempPath(nBufferLength As DWord, lpBuffer As PTSTR) As DWord
	Return GetTempPath(nBufferLength, lpBuffer)
End Function

Function WIN32API_GetTempFileName (pPathName As PCTSTR, pPrefixString As PCTSTR, uUnique As DWord, pTempFileName As PTSTR) As DWord
	Return GetTempFileName(pPathName, pPrefixString, uUnique, pTempFileName)
End Function


End Namespace
End Namespace
