#pragma once
#include "Sync.h"
#include "Handle.h"
#include "Thread.h"
#include "UThread.h"
#include "LinuxIO.h"

namespace os {

	class IORequest;
	class IOHandle;

	/**
	 * Sleep structure for IORequests that have a timeout.
	 */
	class IOTimeoutSleep : public UThreadState::SleepData {
	public:
		IOTimeoutSleep() : SleepData(0), request(null) {}

		IORequest *request;

		virtual void signal();
	};


	/**
	 * IO request. These are posted to an IOHandle when the requests are completed.
	 */
#if defined(WINDOWS)

	class IORequest : public OVERLAPPED {
	public:
		// Note the thread that the request is associated with.
		IORequest(Handle handle, const Thread &thread, nat timeout = 0);
		~IORequest();

		// Sema used for notifying when the request is complete.
		Sema wake;

		// Handle we are working with. So that we can cancel IO.
		Handle handle;

		// Number of bytes read.
		nat bytes;

		// Error code (if any).
		int error;

		// Called on completion.
		void complete(nat bytes);

		// Called on failure.
		void failed(nat bytes, int error);

	private:
		// No copy.
		IORequest(const IORequest &);
		IORequest &operator =(const IORequest &);

		// Owning thread.
		Thread thread;

		// Sleep structure.
		IOTimeoutSleep sleep;
	};

#elif defined(LINUX_IO_URING)

	class IORequest {
	public:
		IORequest(Handle handle, const Thread &thread);
		~IORequest();

		// The request. The fd is filled in from 'handle'.
		struct io_uring_sqe request;

		// Result from the IO operation.
		int result;

		// Did the timeout elapse?
		// Note that 'result' can indicate a success even though timeout is true.
		bool timeout;

		// Submit the request. Waits until completion. Returns 'result' for convenience.
		int submit(nat timeout = 0);

		// Submit the request and ask to detach the handle at the same time.
		int submitAndDetach(nat timeout = 0);

		// Submit the request, ask to detach, and deallocate the IO request once finished.
		void submitDetachDelete(nat timeout = 0);

		// Acces the IO handle.
		IOHandle &io();

		// Called by the IOHandle once the request is finished.
		void onFinish(int result);

	private:
		// No copy.
		IORequest(const IORequest &);
		IORequest &operator =(const IORequest &);

		// Deallocate (using delete) once completed?
		bool deallocate;

		// Event signalled when the request is completed.
		Event wake;

		// Owning thread.
		Thread thread;

		// Sleep structure.
		IOTimeoutSleep sleep;
	};

#elif defined(POSIX)

	class IORequest {
	public:
		// Read/write?
		enum Type {
			read, write
		};

		// Note the thread that the request is associated with.
		IORequest(Handle handle, Type type, const Thread &thread, nat timeout = 0);
		~IORequest();

		// Event used for notifying when the file descriptor is ready for the desired operation, or
		// when it is closed.
		Event wake;

		// Request type (read/write).
		Type type;

		// Closed?
		bool closed;

		// Timeout?
		bool timeout;

	private:
		// No copy.
		IORequest(const IORequest &);
		IORequest &operator =(const IORequest &);

		// Handle used.
		Handle handle;

		// Owning thread.
		Thread thread;

		// Sleep structure.
		IOTimeoutSleep sleep;
	};

#else
#error "Implement IORequest for your platform!"
#endif

}
