#include <mungal4.hpp>
#pragma hdrstop

#if !defined(FILESTRM_HPP)
#	include <filestrm.hpp>
#endif

#if !defined(L4NET_HPP)
#	include <l4net.hpp>
#endif

#if !defined(NETNUB_HPP)
#	include <netnub.hpp>
#endif

#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>

#define NETNUB_MARK 0x10000

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	FileStream::OpenImplementation(
		const char* filename,
		Logical read_only
	)
{
	Check_Pointer(filename);

	if (Net_Common_Ptr && !read_only)
	{
		OpenFileRequestPtr request =
			(OpenFileRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
		OpenFileReturnPtr status =
			(OpenFileReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;

		Net_Common_Ptr->Function		=	NETNUB_OPEN_FILE;
		Net_Common_Ptr->Buffer_Length	=	sizeof(*request);
		Str_Copy(request->File_Name, filename, sizeof(request->File_Name));
		request->Access = O_CREAT|O_TRUNC|O_BINARY|2;
		request->Model = S_IWRITE;

		NetNub::SendCommand();

		int handle = status->File_Handle;
		return handle|NETNUB_MARK;
	}
	else if (read_only)
	{
		return open(filename, O_BINARY|O_RDONLY);
	}
	else
	{
		return open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, S_IWRITE);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	FileStream::CloseImplementation(int handle)
{
	if (handle&NETNUB_MARK)
	{
		Verify(Net_Common_Ptr);
		CloseFileRequestPtr request =
			(CloseFileRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;

		Net_Common_Ptr->Function		=	NETNUB_CLOSE_FILE;
		Net_Common_Ptr->Buffer_Length	=	sizeof(*request);
		request->File_Handle = (short)(handle&~NETNUB_MARK);

		NetNub::SendCommand();
	}
	else
	{
		close(handle);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
size_t
	FileStream::SeekImplementation(
		int file_handle,
		size_t where,
		Logical from_end
	)
{
	if (file_handle&NETNUB_MARK)
	{
		Verify(Net_Common_Ptr);
		SeekFileRequestPtr request =
			(SeekFileRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
		SeekFileReturnPtr status =
			(SeekFileReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;

		Net_Common_Ptr->Function		=	NETNUB_SEEK_FILE;
		Net_Common_Ptr->Buffer_Length	=	sizeof(*request);
		request->File_Handle = (short)(file_handle&~NETNUB_MARK);
		request->Seek_Mode = (short)((from_end) ? SEEK_END : SEEK_SET);
		request->Seek_Offset = where;

		NetNub::SendCommand();

		return status->Seek_Position;
	}
	else
	{
		return lseek(file_handle, (long)where, (from_end) ? SEEK_END : SEEK_SET);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
size_t
	FileStream::WriteImplementation(
		int file_handle,
		const void *buffer,
		size_t length
	)
{
	Check_Pointer(buffer);

	if (file_handle&NETNUB_MARK)
	{
		Verify(Net_Common_Ptr);
		WriteFileRequestPtr request =
			(WriteFileRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
		#if DEBUG_LEVEL>0
			WriteFileReturnPtr status =
				(WriteFileReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
		#endif

		size_t remaining = length;
		char *where = (char*)buffer;
		while (remaining)
		{
			size_t chunk = remaining;
			Max_Clamp(chunk, MAX_TRANSFER_SIZE);

			Net_Common_Ptr->Function = NETNUB_WRITE_FILE;
			Net_Common_Ptr->Buffer_Length = (unsigned short)(chunk + 4);
			request->File_Handle = (short)(file_handle&~NETNUB_MARK);
			request->Write_Length = (unsigned short)chunk;
			Mem_Copy(request->Write_Data, where, chunk, SHARED_MEMORY_SIZE);

			NetNub::SendCommand();
			Verify(status->Bytes_Written == chunk);

			where += chunk;
			remaining -= chunk;
		}
		return length;
	}
	else
	{
		return write(file_handle, buffer, length);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
size_t
	FileStream::ReadImplementation(
		int file_handle,
		void *buffer,
		size_t length
	)
{
	Check_Pointer(buffer);

	if (file_handle&NETNUB_MARK)
	{
		Verify(Net_Common_Ptr);
		ReadFileRequestPtr request =
			(ReadFileRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
		ReadFileReturnPtr status =
			(ReadFileReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;

		size_t remaining = length;
		char *where = (char*)buffer;
		while (remaining)
		{
			size_t chunk = remaining;
			Max_Clamp(chunk, MAX_TRANSFER_SIZE);

			Net_Common_Ptr->Function = NETNUB_READ_FILE;
			Net_Common_Ptr->Buffer_Length	=	sizeof(*request);
			request->File_Handle = (short)(file_handle&~NETNUB_MARK);
			request->Read_Length = (unsigned short)chunk;

			NetNub::SendCommand();
			Verify(chunk == Net_Common_Ptr->Buffer_Length);

			Mem_Copy(where, status->Read_Data, chunk, SHARED_MEMORY_SIZE);

			where += chunk;
			remaining -= chunk;
		}
		return length;
	}
	else
	{
		return read(file_handle, buffer, length);
	}
}

