'Classes/System/IO/FileStream.ab

Namespace System
Namespace IO

/*
(handle As IntPtr, access As FileAccess)
(handle As IntPtr, access As FileAccess, ownsHandle As Boolean)
(handle As IntPtr, access As FileAccess, ownsHandle As Boolean)
(handle As IntPtr, access As FileAccess, ownsHandle As Boolean, isAsync As Boolean)
(path As String, mode As FileMode)
(path As String, mode As FileMode, access As FileAccess)
(path As String, mode As FileMode, access As FileAccess, share As FileShare, useAsync As Boolean)
(path As String, mode As FileMode, access As FileAccess, share As FileShare, options As FileOptions)
(path As String, mode As FileMode, rights As FileSystemRights, share As FileShare, options As FileOptions)
*/

Class FileStream
	Inherits Stream
Protected
	Sub initialize(ac As DWord, owns As Boolean, isAsync As Boolean)
		Imports ActiveBasic
		ownsHandle = owns
		If (ac And GENERIC_READ) = 0 Then
			readFn = New Detail.Unreadable
		End If
		If (ac And GENERIC_WRITE) = 0 Then
			writeFn = New Detail.Unwritable
		End If
		If GetFileType(handle) <> FILE_TYPE_DISK Then
			seekFn = New Detail.Unseekable
		End If
		If isAsync Then
			If IsNothing(seekFn) Then
				seekFn = New Detail.OverlappedSeekable
			End If
			If IsNothing(readFn) Then
				readFn = New Detail.OverlappedReadable
			End If
			If IsNothing(writeFn) Then
				writeFn = New Detail.OverlappedWritable
			End If
		Else
			If IsNothing(seekFn) Then
				seekFn = New Detail.Seekable
			End If
			If IsNothing(readFn) Then
				readFn = New Detail.Readable
			End If
			If IsNothing(writeFn) Then
				writeFn = New Detail.Writable
			End If
		End If
	End Sub

	Sub openFile(path As String, mode As FileMode, rights As DWord, share As FileShare, ByRef options As FileOptions)
		Imports ActiveBasic
		If IsNothing(path) Then
			Throw New ArgumentNullException("path")
		ElseIf path.Length = 0 Then
			Throw New ArgumentException("path.Length = 0")
		End If
		Dim sh = share As DWord
		Dim mo = mode As DWord
		Dim op = options As DWord

		If mode = FileMode.Append Then
			mo = OPEN_ALWAYS
			seekFn = New Detail.Unseekable
		End If

		Dim os = Environment.OSVersion
		Dim platform = os.Platform As DWord
		If platform = PlatformID.Win32Windows As DWord Or platform = PlatformID.Win32S As DWord Then
			options = (op As DWord And Not FILE_FLAG_OVERLAPPED) As FileOptions
		End If

		handle = CreateFile(ToTCStr(path), rights, sh, ByVal NULL, mo, op, 0)
		If handle = INVALID_HANDLE_VALUE Then
		'エラー処理
		'Throw ArgumentException
		'Throw IOException
		'Throw System.IO.FileNotFoundException
			handle = 0
			Detail.ThrowWinLastErrorIOException("Failed to open/create file.")
		End If
	
		filePath = path

		If mode = FileMode.Append Then
			Seek(0, SeekOrigin.End)
		End If
	End Sub

	Sub initWithOpen(path As String, mode As FileMode, access As FileAccess, share As FileShare, options As FileOptions)
		openFile(path, mode, access As DWord, share, options)
		initialize(access, True, UseOverlappled(options))
	End Sub

	Sub initWithOpen2(path As String, mode As FileMode, rights As DWord, share As FileShare, options As FileOptions)
		openFile(path, mode, rights, share, options)
		Dim ac = 0 As DWord
		If (rights And GENERIC_READ) Or (rights And FILE_READ_DATA) Then
			ac = (ac As DWord Or GENERIC_READ) As FileAccess
		End If
		If (rights And GENERIC_WRITE) Or (rights And FILE_WRITE_DATA) Then
			ac = (ac As DWord Or GENERIC_WRITE) As FileAccess
		End If
		initialize(ac, True, UseOverlappled(options))
	End Sub

	Static Function UseOverlappled(options As FileOptions) As Boolean
		Dim op = options As DWord
		If op And FILE_FLAG_OVERLAPPED Then
			UseOverlappled = True
		Else
			UseOverlappled = False
		End If
	End Function

	Sub FileStream()
	End Sub
Public
	/*!
	@brief ファイルを開く。
	@param[in] path ファイルパス
	@param[in] mode 開き方
	@param[in] rights 使用するアクセス権の指定
	@param[in] share 共有方法
	@param[in] option オプション
	@date 2008/08/21
	@auther Egtra
	*/
	Sub FileStream(path As String, mode As FileMode, rights As Security.AccessControl.FileSystemRights, share As FileShare, options As FileOptions)
		initWithOpen2(path, mode, rights As DWord, share, options)
	End Sub
	/*!
	@brief ファイルを開く。
	@param[in] path ファイルパス
	@param[in] mode 開き方
	@param[in] access ファイルアクセスの指定
	@param[in] share 共有方法
	@param[in] option オプション
	*/
	Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare, options As FileOptions)
		initWithOpen(path, mode, access, share, options)
	End Sub
	/*!
	@brief ファイルを開く。
	@param[in] path ファイルパス
	@param[in] mode 開き方
	@param[in] access ファイルアクセスの指定
	@param[in] share 共有方法
	@param[in] useAsync 非同期にするならTrue
	@date 2008/08/21
	@auther Egtra
	*/
	Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare, useAsync As Boolean)
		Dim options = FileOptions.None
		If useAsync Then
			options = FileOptions.Asynchronous
		End If
		initWithOpen(path, mode, access, share, options)
	End Sub
	/*!
	@brief ファイルを開く。
	@param[in] path ファイルパス
	@param[in] mode 開き方
	@param[in] access ファイルアクセスの指定
	@param[in] share 共有方法
	*/
	Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare)
		initWithOpen(path,mode,access,share,FileOptions.None)
	End Sub
	/*!
	@brief ファイルを開く。
	@param[in] path ファイルパス
	@param[in] mode 開き方
	@param[in] access ファイルアクセスの指定
	*/
	Sub FileStream(path As String, mode As FileMode, access As FileAccess)
		initWithOpen(path,mode,access,FileShare.None,FileOptions.None)
	End Sub
	/*!
	@brief ファイルを開く。
	@param[in] path ファイルパス
	@param[in] mode 開き方
	*/
	Sub FileStream(path As String, mode As FileMode)
		Dim access As FileAccess
		Select Case mode
			Case FileMode.Append
				access=FileAccess.Write
			Case FileMode.Create
				access=FileAccess.ReadWrite
			Case FileMode.CreateNew
				access=FileAccess.ReadWrite
			Case FileMode.Open
				access=FileAccess.ReadWrite
			Case FileMode.OpenOrCreate
				access=FileAccess.ReadWrite
			Case FileMode.Truncate
				access=FileAccess.Write
		End Select
		initWithOpen(path,mode,access,FileShare.None,FileOptions.None)
	End Sub
	/*!
	@brief 既存ハンドルを基にストリームを作る。
	@param[in] h ハンドル
	@param[in] access ファイルアクセスの指定
	@param[in] owns 所有権を保持するかどうか（TrueならDispose時に基のハンドルも閉じる）
	@param[in] isAsync 重複IOを使用するかどうか
	@date 2008/02/26
	@auther Egtra
	@todo SafeFileHandle版に置き換えること
	*/
	Sub FileStream(h As HANDLE, access As FileAccess, owns = True As Boolean, isAsync = False As Boolean)
		handle = h
		initialize(access, owns, isAsync)
	End Sub
Public
	/*!
	@brief	ファイルが読み込みに対応しているかを返す
	*/
	Override Function CanRead() As Boolean
		Return readFn.CanRead
	End Function

	/*!
	@brief	ファイルがシークに対応しているかを返す
	*/
	Override Function CanSeek() As Boolean
		Return seekFn.CanSeek
	End Function

	/*!
	@brief	ファイルが書き込みに対応しているかを返す
	*/
	Override Function CanWrite() As Boolean
		Return writeFn.CanWrite
	End Function

	/*!
	@brief	保持しているハンドルを返す
	*/
	Function Handle() As HANDLE
		Return handle
	End Function

	Override Function Length() As QWord
		disposedCheck()
		Length = seekFn.Length(This)
	End Function

	Function Name() As String
		Return This.filePath
	End Function

	Override Sub Position(value As QWord)
		disposedCheck()
		seekFn.Position(This, value)
	End Sub
	Override Function Position() As QWord
		disposedCheck()
		Position = seekFn.Position(This)
	End Function

Public
	/* Safe〜Handle系の実装は要相談！！ */
/*	Function SafeFileHandle() As SafeFileHandle
	End Function*/

Public
	Override Function BeginRead(buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult
		disposedCheck()
		If buffer = 0 Then
			Throw New ArgumentNullException("FileStream.BeginRead", "buffer")
		End If
		BeginRead = readFn.BeginRead(This, buffer, offset, count, callback, state)
	End Function

	Override Function BeginWrite(buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult
		disposedCheck()
		If buffer = 0 Then
			Throw New ArgumentNullException("FileStream.BeginWrite", "buffer")
		End If
		BeginWrite = writeFn.BeginWrite(This, buffer, offset, count, callback, state)
	End Function

	Override Function EndRead(asyncResult As System.IAsyncResult) As Long
		disposedCheck()
		EndRead = readFn.EndRead(This, asyncResult)
	End Function

	Override Sub EndWrite(asyncResult As System.IAsyncResult)
		disposedCheck()
		writeFn.EndWrite(This, asyncResult)
	End Sub

	Override Sub Flush()
		disposedCheck()
		Dim ret = FlushFileBuffers(This.handle)
		If ret = FALSE Then
			Detail.ThrowWinLastErrorIOException("FileStream.Flush: Failed to flush.")
		End If
	End Sub

/*	Function GetAccessControl() As FileSecurity
	FileSecurityの実装がまだできてない。
	End Function*/

	Sub Lock(position As QWord, length As QWord)
		disposedCheck()
		If position < 0 Then
			Throw New ArgumentOutOfRangeException("FileStream.Lock: An argument is negative value.", New System.UInt64(position), "position")
		ElseIf length < 0 Then
			Throw New ArgumentOutOfRangeException("FileStream.Lock: An argument is negative value.", New System.UInt64(length), "length")
		End If
		If LockFile(handle, LODWORD(position), HIDWORD(position), LODWORD(length), HIDWORD(length)) = FALSE Then
			Detail.ThrowWinLastErrorIOException("FileStream.Lock: Failed to lock.")
		End If
	End Sub

	Override Function Read(buffer As *Byte, offset As Long, count As Long) As Long
		disposedCheck()
		If buffer = 0 Then
			Throw New ArgumentNullException("FileStream.Read", "buffer")
		End If
		Read = readFn.Read(This, buffer, offset, count)
	End Function

	/*!
	@brief	ストリームの現在位置を移動させる。
	@param[in] offset	originからの移動量
	@param[in] origin	移動の基準位置
	@return	移動後の新しい現在位置
	@exception DisposedException	既にストリームが閉じられている場合
	@exception ArgumentException	移動後の位置が負の位置（ファイル先頭より手前）になる場合
	@exception IOException	その他エラーが発生した場合
	*/
	Override Function Seek(offset As Int64, origin As SeekOrigin) As Int64
		disposedCheck()
		Seek = seekFn.Seek(This, offset, origin)
	End Function

/*	Sub SetAccessControl(fileSecurity As FileSecurity)
	FileSecurityの実装がまだできてない。
	End Sub*/

	Override Sub SetLength(value As QWord)
		disposedCheck()
		seekFn.SetLength(This, value)
	End Sub

	Override Function ToString() As String
		Return This.Name()
	End Function

	Sub Unlock(position As QWord, length As QWord)
		disposedCheck()
		Dim ret = UnlockFile(handle, LODWORD(position), HIDWORD(position), LODWORD(length), HIDWORD(length))
		If ret = FALSE Then
			Detail.ThrowWinLastErrorIOException("FileStream.Unlock failed")
		End If
	End Sub

	Override Sub Write(buffer As *Byte, offset As Long, count As Long)
		disposedCheck()
		If buffer = 0 Then
			Throw New ArgumentNullException("FileStream.Write", "buffer")
		End If
		writeFn.Write(This, buffer, offset, count)
	End Sub

Protected
	Override Sub Dispose(disposing As Boolean)
		If handle <> 0 Then
			CloseHandle(InterlockedExchangePointer(ByVal VarPtr(handle), NULL))
		End If
	End Sub

	Sub disposedCheck()
		If handle = 0 Then
			Throw New ObjectDisposedException("FileStream: This stream has already closed.")
		End If
	End Sub

Private
	handle As HANDLE
	seekFn As Detail.SeekFunctions
	readFn As Detail.ReadFunctions
	writeFn As Detail.WriteFunctions
	filePath As String
	ownsHandle As Boolean
End Class

End Namespace
End Namespace
