/*
 * \brief  Representation of an open file
 * \author Norman Feske
 * \date   2011-02-17
 */

/*
 * Copyright (C) 2011-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

#ifndef _INCLUDE__VFS__VFS_HANDLE_H_
#define _INCLUDE__VFS__VFS_HANDLE_H_

#include <vfs/directory_service.h>

namespace Genode::Vfs {
	struct Read_ready_response_handler;
	struct Watch_response_handler;
	class Vfs_handle;
	class Vfs_watch_handle;
	class File_io_service;
	class File_system;
}


/**
 * Object for encapsulating application-level
 * response to VFS I/O
 *
 * These responses should be assumed to be called
 * during I/O signal dispatch.
 */
struct Genode::Vfs::Read_ready_response_handler : Interface
{
	/**
	 * Respond to a resource becoming readable
	 */
	virtual void read_ready_response() = 0;
};


/**
 * Object for encapsulating application-level
 * handlers of VFS responses.
 *
 * This response should be assumed to be called
 * during I/O signal dispatch.
 */
struct Genode::Vfs::Watch_response_handler : Interface
{
	virtual void watch_response() = 0;
};


class Genode::Vfs::Vfs_handle
{
	public:

		static constexpr unsigned long INVALID_CONTEXT_ID = ~0UL;

	private:

		Directory_service &_ds;
		File_io_service   &_fs;
		Allocator         &_alloc;
		file_size          _seek = 0;
		int                _status_flags;

		/*
		 * An optional id for VFS plugins which support multiple
		 * "sessions" for the same file name with their individual
		 * context file directories named according to this id.
		 */
		unsigned long _context_id { INVALID_CONTEXT_ID };

		Read_ready_response_handler *_handler_ptr = nullptr;

		/*
		 * Noncopyable
		 */
		Vfs_handle(Vfs_handle const &);
		Vfs_handle &operator = (Vfs_handle const &);

	protected:

		void context_id(unsigned long id) { _context_id = id; }

	public:

		class Guard
		{
			private:

				/*
				 * Noncopyable
				 */
				Guard(Guard const &);
				Guard &operator = (Guard const &);

				Vfs_handle * const _handle;

			public:

				Guard(Vfs_handle *handle) : _handle(handle) { }

				~Guard()
				{
					if (_handle)
						_handle->close();
				}
		};

		enum { STATUS_RDONLY = 0, STATUS_WRONLY = 1, STATUS_RDWR = 2 };

		Vfs_handle(Directory_service &ds,
		           File_io_service   &fs,
		           Allocator         &alloc,
		           int                status_flags)
		:
			_ds(ds), _fs(fs),
			_alloc(alloc),
			_status_flags(status_flags)
		{ }

		virtual ~Vfs_handle() { }

		Directory_service &ds() { return _ds; }
		File_io_service   &fs() { return _fs; }
		Allocator      &alloc() { return _alloc; }

		File_io_service const &fs() const { return _fs; }

		int status_flags() const { return _status_flags; }
		void status_flags(int flags) { _status_flags = flags; }

		unsigned long context_id() const { return _context_id; }

		/**
		 * Return seek offset in bytes
		 */
		file_size seek() const { return _seek; }

		/**
		 * Set seek offset in bytes
		 */
		void seek(file_offset seek) { _seek = seek; }

		/**
		 * Advance seek offset by 'incr' bytes
		 */
		void advance_seek(file_size incr) { _seek += incr; }

		/**
		 * Set response handler, unset with nullptr
		 */
		virtual void handler(Read_ready_response_handler *handler_ptr)
		{
			_handler_ptr = handler_ptr;
		}

		/**
		 * Apply to response handler if present
		 *
		 * XXX: may not be necesarry if the method above is virtual.
		 */
		void apply_handler(auto const &fn) const {
			if (_handler_ptr) fn(*_handler_ptr); }

		/**
		 * Notify application through response handler
		 */
		void read_ready_response() {
			if (_handler_ptr) _handler_ptr->read_ready_response(); }

		/**
		 * Close handle at backing file-system.
		 *
		 * This leaves the handle pointer in an invalid and unsafe state.
		 */
		inline void close() { ds().close(this); }
};


class Genode::Vfs::Vfs_watch_handle
{
	private:

		Directory_service      &_fs;
		Allocator              &_alloc;
		Watch_response_handler *_handler = nullptr;

		/*
		 * Noncopyable
		 */
		Vfs_watch_handle(Vfs_watch_handle const &);
		Vfs_watch_handle &operator = (Vfs_watch_handle const &);

	public:

		Vfs_watch_handle(Directory_service &fs, Allocator &alloc)
		:
			_fs(fs), _alloc(alloc)
		{ }

		virtual ~Vfs_watch_handle() { }

		Directory_service &fs() { return _fs; }
		Allocator &alloc() { return _alloc; }

		/**
		 * Set response handler, unset with nullptr
		 */
		virtual void handler(Watch_response_handler *handler) {
			_handler = handler; }

		/**
		 * Notify application through response handler
		 */
		void watch_response()
		{
			if (_handler)
				_handler->watch_response();
		}

		/**
		 * Close handle at backing file-system.
		 *
		 * This leaves the handle pointer in an invalid and unsafe state.
		 */
		inline void close() { fs().close(this); }
};

#endif /* _INCLUDE__VFS__VFS_HANDLE_H_ */
