#include "stdafx.h"
#include "ConsoleStream.h"

namespace IO
{
	ConsoleStream::ConsoleStream(HANDLE StreamHandle, FileAccess Access)
		: _Handle(StreamHandle)
	{
		this->_CanRead = (((uint8_t)Access & (uint8_t)FileAccess::Read) == (uint8_t)FileAccess::Read);
		this->_CanRead = (((uint8_t)Access & (uint8_t)FileAccess::Write) == (uint8_t)FileAccess::Write);
		this->_IsPipe = (GetFileType(StreamHandle) == FILE_TYPE_PIPE);
	}

	ConsoleStream::~ConsoleStream()
	{
		this->Close();
	}

	void ConsoleStream::Seek(uint64_t Offset, SeekOrigin Origin)
	{
		IOError::StreamNoSeekSupport();
	}

	uint64_t ConsoleStream::Read(uint8_t* Buffer, uint64_t Offset, uint64_t Count)
	{
		// Wait for available input...
		ConsoleStream::WaitForAvailableConsoleInput(this->_Handle, this->_IsPipe);

		DWORD bRead = 0;
		ReadFile(this->_Handle, Buffer + Offset, (DWORD)Count, &bRead, NULL);

		return bRead;
	}

	uint64_t ConsoleStream::Read(uint8_t * Buffer, uint64_t Offset, uint64_t Count, uint64_t Position)
	{
		IOError::StreamNoSeekSupport();

		return 0;
	}

	void ConsoleStream::Write(uint8_t* Buffer, uint64_t Offset, uint64_t Count)
	{
		DWORD bWrite = 0;
		WriteFile(this->_Handle, Buffer + Offset, (DWORD)Count, &bWrite, NULL);
	}

	void ConsoleStream::Write(uint8_t * Buffer, uint64_t Offset, uint64_t Count, uint64_t Position)
	{
		IOError::StreamNoSeekSupport();
	}

	HANDLE ConsoleStream::GetStreamHandle() const
	{
		return this->_Handle;
	}

	void ConsoleStream::Close()
	{
		this->_Handle = nullptr;
		this->_CanRead = false;
		this->_CanWrite = false;
	}

	void ConsoleStream::Flush()
	{
	}

	bool ConsoleStream::CanRead()
	{
		return this->_CanRead;
	}

	bool ConsoleStream::CanWrite()
	{
		return this->_CanWrite;
	}

	bool ConsoleStream::CanSeek()
	{
		return false;
	}

	bool ConsoleStream::GetIsEndOfFile()
	{
		return false;
	}

	uint64_t ConsoleStream::GetLength()
	{
		return 0;
	}
	uint64_t ConsoleStream::GetPosition()
	{
		return 0;
	}

	void ConsoleStream::SetLength(uint64_t Length)
	{
		IOError::StreamNoSeekSupport();
	}

	void ConsoleStream::SetPosition(uint64_t Position)
	{
		IOError::StreamNoSeekSupport();
	}

	void ConsoleStream::WaitForAvailableConsoleInput(HANDLE hHandle, bool IsPipe)
	{
		bool sWait = false;

		if (IsPipe)
		{

			DWORD cBytesRead, cTotalBytesAvailable, cBytesLeftThisMessage;
			auto Result = PeekNamedPipe(hHandle, NULL, 0, &cBytesRead, &cTotalBytesAvailable, &cBytesLeftThisMessage);
			if (Result != 0)
			{
				sWait = (cTotalBytesAvailable > 0);
			}
			else
			{
				auto eCode = GetLastError();
				sWait = eCode == ERROR_BROKEN_PIPE || eCode == ERROR_NO_DATA || eCode == ERROR_PIPE_NOT_CONNECTED;
			}
		}

		if (!sWait)
			WaitForSingleObjectEx(hHandle, INFINITE, TRUE);
	}
}