Namespace System
Namespace IO


/* ほんとはmiscに入れるかかファイルを分けたほうがいいかもしれないが一先ず実装 */
Enum FileOptions
	Asynchronous
	DeleteOnClose
	Encrypted
	None
	RandomAccess
	SequentialScan
	WriteThrough 
End Enum

Class FileStream
	Inherits Stream

	handle As HANDLE

	/*
	ファイルハンドルからこれらを取得できれば、これらは入らないが
	今のところは不明なので自前で実装するしかない
	*/
	filePath As String
	fileMode As DWord
	fileAccess As DWord
	fileShare As DWord
	fileOptions As DWord
	fileReadOverlapped As OVERLAPPED
	fileWriteOverlapped As OVERLAPPED

Public
	/* コンストラクタ.NETと同じように実装は難しい、一先ず動くものを実装したが変更が必要だと思う */
	Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare, options As FileOptions)
		Dim ac As DWord
		Dim mo As DWord
		Dim sh As DWord
		Dim op As DWord

		Select Case access
			Case FileAccess.Read
				ac=GENERIC_READ
			Case FileAccess.ReadWrite
				ac=GENERIC_READ or GENERIC_WRITE
			Case FileAccess.Write
				ac=GENERIC_WRITE
		End Select

		Select Case share
			Case FileShare.DeleteFile
				sh=FILE_SHARE_DELETE
			Case FileShare.None
				sh=0
			Case FileShare.Read
				sh=FILE_SHARE_READ
			Case FileShare.ReadWrite
				sh=FILE_SHARE_READ or FILE_SHARE_WRITE
			Case FileShare.Write
				sh=FILE_SHARE_WRITE
		End Select

		Select Case mode
			Case FileMode.Append
				mo=OPEN_ALWAYS
			Case FileMode.Create
				mo=CREATE_ALWAYS
			Case FileMode.CreateNew
				mo=CREATE_NEW
			Case FileMode.Open
				mo=OPEN_EXISTING
			Case FileMode.OpenOrCreate
				mo=OPEN_ALWAYS
			Case FileMode.Truncate
				mo=TRUNCATE_EXISTING
		End Select

		Select Case options
			Case FileOptions.Asynchronous
				op=FILE_FLAG_OVERLAPPED
			Case FileOptions.DeleteOnClose
				op=FILE_FLAG_DELETE_ON_CLOSE
			Case FileOptions.Encrypted
			Case FileOptions.None
				op=0
			Case FileOptions.RandomAccess
				op=FILE_FLAG_RANDOM_ACCESS
			Case FileOptions.SequentialScan
				op=FILE_FLAG_SEQUENTIAL_SCAN
			Case FileOptions.WriteThrough
				op=FILE_FLAG_WRITE_THROUGH
		End Select

		This.handle=CreateFile(path As PSTR,ac,sh,ByVal NULL,mo,op,0)
		If This.handle=INVALID_HANDLE_VALUE Then
		'エラー処理
		'Throw ArgumentException
		'Throw IOException
		'Throw System.IO.FileNotFoundException 
			This.handle=0
			Beep(220,500)
			Exit Sub
		End If

		This.filePath = path
		This.fileMode = mo
		This.fileAccess = ac
		This.fileShare = sh
		This.fileOptions = op
	End Sub
	Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare)
		This.FileStream(path,mode,access,share,FileOptions.None)
	End Sub
	Sub FileStream(path As String, mode As FileMode, access As FileAccess)
		This.FileStream(path,mode,access,FileShare.None,FileOptions.None)
	End Sub
	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
		This.FileStream(path,mode,access,FileShare.None,FileOptions.None)
	End Sub

	Sub ~FileStream()
		This.Flush()
		This.Close()
	End Sub

Public
	Override Function CanRead() As Boolean
		/* ファイルが読み込みに対応しているかを返す */
		If This.fileAccess And GENERIC_READ Then
			Return True
		Else
			Return False
		End If
	End Function

	Override Function CanSeek() As Boolean
		/* ファイルがシークに対応しているかを返す */
		If GetFileType(This.handle)=FILE_TYPE_DISK Then
			Return True
		Else
			Return False
		End If
	End Function

'	Override Function CanTimeout() As Boolean
'		/* ファイルがタイムアウトに対応しているかを返す */
'		Return False /*今のところ対応していないのでFalse*/
'	End Function*/

	Override Function CanWrite() As Boolean
		/* ファイルが書き込みに対応しているかを返す */
		If This.fileAccess And GENERIC_WRITE Then
			Return True
		Else
			Return False
		End If
	End Function

	/*Handle*/

	Function IsAsync() As Boolean
		/* ファイルが非同期操作に対応しているかを返す */
		If This.fileOptions=FILE_FLAG_OVERLAPPED/*FileOptions.Asynchronous*/ Then
			Return True
		Else
			Return False
		End If
	End Function

	Override Function Length() As Int64
		If This.CanSeek() Then
			Dim length As LARGE_INTEGER
			length.LowPart=GetFileSize(This.handle,VarPtr(length.HighPart) As *DWord)
			Return MAKEQWORD(length.LowPart,length.HighPart)
		End If
	End Function

	Function Name() As String
		Return New String(This.filePath)
	End Function
	
	Override Sub Position(value As Int64)
		If This.CanSeek() Then
			If This.IsAsync() Then
				fileReadOverlapped.Offset=LODWORD(value)
				fileReadOverlapped.OffsetHigh=HIDWORD(value)
				fileWriteOverlapped.OffsetHigh=LODWORD(value)
				fileWriteOverlapped.OffsetHigh=HIDWORD(value)
			Else
				Dim position As LARGE_INTEGER
				position.LowPart=LODWORD(value)
				position.HighPart=HIDWORD(value)
				SetFilePointer(This.handle,position.LowPart,VarPtr(position.HighPart) As *DWord,FILE_BEGIN)
			End If
		End If
	End Sub
	Override Function Position() As Int64
		If This.CanSeek() Then
			If This.IsAsync() Then
				Return MAKEQWORD(fileReadOverlapped.Offset,fileReadOverlapped.OffsetHigh)
			Else
				Dim position As LARGE_INTEGER
				ZeroMemory(VarPtr(position),SizeOf(LARGE_INTEGER))
				position.LowPart=SetFilePointer(This.handle,position.LowPart,VarPtr(position.HighPart) As *DWord,FILE_CURRENT)
				Return MAKEQWORD(position.LowPart,position.HighPart)
			End If
		End If
	End Function

/*	Override Sub ReadTimeout(value As Long)
		'TODO
	End Sub
	Override Function ReadTimeout() As Long
		'TODO
	End Function*/

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

	Override Sub WriteTimeout(value As Long)
		'TODO
	End Sub
	Override Function WriteTimeout() As Long
		'TODO
	End Function
	

Public
	Override Function BeginRead(ByRef buffer[] As Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult
		If This.IsAsync() Then
		Else
			Read(buffer,offset,count)
		End If
	End Function

	Override Function BeginWrite(ByRef buffer[] As Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult
		If This.IsAsync() Then
		Else
			Write(buffer,offset,count)
		End If
	End Function

	Override Sub Close()
		CloseHandle(This.handle)
	End Sub

/*	CreateObjRef*/
/*	Dispose*/

	Override Function EndRead(ByRef asyncResult As System.IAsyncResult) As Long
		'TODO
	End Function

	Override Sub EndWrite(ByRef asyncResult As System.IAsyncResult)
		'TODO
	End Sub

/*	Equals*/

	Override Sub Flush()
		FlushFileBuffers(This.handle)
	End Sub

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

	Override Function GetHashCode() As Long
		Return ObjPtr(This) As Long
	End Function

/*	GetLifetimeService*/

/*	Override Function GetType() As TypeInfo
		Return Super.GetType()
	End Function*/

/*	InitializeLifetimeService*/

	Sub Lock(position As Int64, length As Int64)
	End Sub

	Override Function Read( buffer As *Byte, offset As Long, count As Long) As Long
		If This.CanRead() Then
			Dim ret As DWord
			If This.IsAsync() Then
				ReadFile(This.handle,VarPtr(buffer[offset]),count,VarPtr(ret),This.fileReadOverlapped)
				While This.fileReadOverlapped.Internal=STATUS_PENDING
				Wend
				fileReadOverlapped.Offset+=LODWORD(ret)
				fileReadOverlapped.OffsetHigh+=HIDWORD(ret)
				fileWriteOverlapped.Offset+=LODWORD(ret)
				fileWriteOverlapped.OffsetHigh+=HIDWORD(ret)
				Return ret
			Else
				ReadFile(This.handle,VarPtr(buffer[offset]),count,VarPtr(ret),ByVal NULL)
				Return ret
			End If
		End If
	End Function

/*	ReferenceEquals*/

	Override Function Seek(offset As Int64, origin As SeekOrigin) As Long
		If This.CanSeek() Then
			If This.IsAsync() Then
				Select Case origin
					Case SeekOrigin.Begin
						fileReadOverlapped.Offset=LODWORD(offset)
						fileReadOverlapped.OffsetHigh=HIDWORD(offset)
						fileWriteOverlapped.OffsetHigh=LODWORD(offset)
						fileWriteOverlapped.OffsetHigh=HIDWORD(offset)
					Case SeekOrigin.Current
						fileReadOverlapped.Offset+=LODWORD(offset)
						fileReadOverlapped.OffsetHigh+=HIDWORD(offset)
						fileWriteOverlapped.Offset+=LODWORD(offset)
						fileWriteOverlapped.OffsetHigh+=HIDWORD(offset)
					Case SeekOrigin.End
						fileReadOverlapped.Offset=LODWORD(This.Length()+offset)
						fileReadOverlapped.OffsetHigh=HIDWORD(This.Length()+offset)
						fileWriteOverlapped.Offset=LODWORD(This.Length()+offset)
						fileWriteOverlapped.OffsetHigh=HIDWORD(This.Length()+offset)
				End Select
			Else
				Dim seek As LARGE_INTEGER
				seek.LowPart=LODWORD(offset)
				seek.HighPart=HIDWORD(offset)
				If seek.HighPart=0 And offset<0 Then seek.LowPart=-seek.LowPart
				Select Case origin
					Case SeekOrigin.Begin
						Return SetFilePointer(This.handle,seek.LowPart,VarPtr(seek.HighPart) As *DWord,FILE_BEGIN)
					Case SeekOrigin.Current
						Return SetFilePointer(This.handle,seek.LowPart,VarPtr(seek.HighPart) As *DWord,FILE_CURRENT)
					Case SeekOrigin.End
						Return SetFilePointer(This.handle,seek.LowPart,VarPtr(seek.HighPart) As *DWord,FILE_CURRENT)
				End Select
			End If
		End If
	End Function

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

	Override Sub SetLength(value As Int64)
		If This.CanWrite() and This.CanSeek() Then
			If This.IsAsync() Then
			Else
				Dim current = This.Position()
				This.Position(value)
				SetEndOfFile(This.handle)
			End If
		End If
	End Sub

/*	Synchronized*/

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

	Sub Unlock(position As Int64, length As Int64)
	End Sub

	Override Sub Write(buffer As *Byte, offset As Long, count As Long)
		If This.CanWrite() Then
			Dim ret As DWord
			If This.IsAsync() Then
				WriteFile(This.handle,VarPtr(buffer[offset]),count,VarPtr(ret),This.fileWriteOverlapped)
				While This.fileReadOverlapped.Internal=STATUS_PENDING
				Wend
				This.fileReadOverlapped.Offset+=LODWORD(ret)
				This.fileReadOverlapped.OffsetHigh+=HIDWORD(ret)
				This.fileWriteOverlapped.Offset+=LODWORD(ret)
				This.fileWriteOverlapped.OffsetHigh+=HIDWORD(ret)
			Else
				WriteFile(This.handle,VarPtr(buffer[offset]),count,VarPtr(ret),ByVal NULL)
			End If
		End If
	End Sub

Protected
	Override Function CreateWaitHandle() As System.Threading.WaitHandle
		'調査した限りでは、System.Threading.EventWaitHandleクラスをNewする模様。
		'現状ではSystem.Threading.WaitHandleクラスをNewしてからHandleにて設定
		Dim wh As System.Threading.WaitHandle
		wh.Handle=CreateEvent(NULL,TRUE,FALSE,NULL)
		Return wh
	End Function

/*	Dispose
	Finalize
	MemberwiseClone*/
Private
End Class


End Namespace
End Namespace
