#ifndef __OS_H
#define __OS_H

#ifdef __cplusplus
extern "C"
{
#endif

#include <sys/types.h>

/*!
 *	\ingroup	kernelu
 *	@{
 */

typedef struct module_info_t module_info_t;

//! Specifies user-mode information on a specific module.
/*!
 *	This structure is part of the module itself (it has its own section -- 
 *		.info). The crt0 code (crt0.c) is responsible for declaring this 
 *		variable. When the module loader loads a section with the name ".info"
 *		it assumes that it comprises a structure of this format, and fills it
 *		in accordingly.
 *
 *	This structure is available through the _info variable in each module.
 */
struct module_info_t
{
	//! Specifies the version number of the module loader that loaded this 
	//!		module.
	dword loader_version;
	//! Points to the module loaded immediately after this one, or NULL if this
	//!		was the last module to be loaded.
	module_info_t* next;
	//! Specifies the base address of the module.
	/*!
	 *	The base address is the address where the module's headers start.
	 */
	dword base, 
	//! Specifies the overall length of the module, in bytes.
	/*!
	 *	Includes headers, all sections and alignment padding.
	 */
		length;
	//! Specifies the name of the module.
	wchar_t name[40];
};

extern module_info_t _info;

#include <kernel/i386.h>

#pragma pack(push,1)

//! The layout of the stack frame generated by the assembly-language interrupt 
//!		handler.
/*!
 *	The isr_and_switch routine (in start.asm), along with the individual 
 *		interrupt handlers and the processor, build this structure on the stack
 *		before isr() is called.
 */
struct context_t
{
	/*
	//! The user-mode registers pushed by pusha.
	pusha_t regs;
	//! The user-mode selectors pushed by isr_and_switch.
	dword ds, es, fs, gs;
	//! The interrupt or IRQ number pushed by the interrupt handler.
	dword intr, 
	//! The error code pushed by the processor.
		error;
	//! The current context pushed by the processor.
	dword eip, cs, eflags, esp, ss;
	*/

	dword kernel_esp;
	pusha_t regs;
	dword gs, fs, es, ds;
	dword intr, error;
	dword eip, cs, eflags, esp, ss;
};
typedef struct context_t context_t;
#pragma pack(pop)

typedef enum _ExceptionDisposition {
    ExceptionContinueExecution,
    ExceptionContinueSearch,
    ExceptionNestedException,
    ExceptionCollidedUnwind
} ExceptionDisposition;

typedef struct exception_info_t exception_info_t;
struct exception_info_t
{
	dword code;
	addr_t address;
	addr_t eip;
};

typedef ExceptionDisposition (*except_handler)(
	exception_info_t *ExceptionRecord,
	void * EstablisherFrame,
	context_t *ContextRecord,
	void * DispatcherContext);

typedef struct exception_registration_t exception_registration_t;
struct exception_registration_t
{
	exception_registration_t* prev;
	except_handler handler;
};

typedef struct process_info_t process_info_t;
typedef struct thread_info_t thread_info_t;

//! Specifies user-mode thread information.
/*!
 *	When a thread is created, the kernel allocates space in the process's 
 *		address space for a thread_info_t structure. This structure is freely 
 *		readable by the process itself and includes important information about
 *		the thread's environment. The use of a structure like this, in user-
 *		accessible memory, is more efficient than a series of syscalls to query
 *		the relevant information.
 *
 *	The thread_info_t structure forms each thread's FS segment. A pointer to 
 *		the thread_info_t structure, in DS-addressable form, is provided, to
 *		make the structure more easily accessible from high-level languages.
 */
struct thread_info_t
{
	//! Points to a structure specifying the exception handler for the current
	//!		block of code.
	exception_registration_t* except;
	//! Points to the thread_info_t structure itself, allowing the structure to
	//!		be accessed without referring to the FS selector.
	thread_info_t* info;
	//! Specifies the thread's ID
	dword tid;
	//! Specifies the ID of the thread's parent process
	dword pid;
	//! Describes the error condition which caused the last operating system 
	//!		call to fail, if any. Analogous to the C run-time errno variable.
	dword last_error;
	//! Points to the thread's parent process's information structure.
	process_info_t* process;
	dword queue;
};

#define EXCEPTION_DEBUG			1
#define EXCEPTION_GPF			13
#define EXCEPTION_PAGE_FAULT		14
#define EXCEPTION_MISSING_DLL		0x80000000
#define EXCEPTION_MISSING_IMPORT	0x80000001
#define EXCEPTION_DLLMAIN_FAILED	0x80000002
#define EXCEPTION_ASSERT_FAILED		0x80000003

//! Specifies user-mode process information.
/*!
 *	When a process is created, the kernel allocates space in its address space
 *		for a process_info_t structure. This structure is freely readable by
 *		the process itself and includes important information about its 
 *		environment. The use of a structure like this, in user-accessible 
 *		memory, is more efficient than a series of syscalls to query the 
 *		relevant information.
 *
 *	The process_info_t structure is accessible through the process member of
 *		each thread's thread_info_t structure, which forms each thread's FS 
 *		segment.
 */
struct process_info_t
{
	//! The ID of the process; unique within the system
	dword id;
	//! A pointer to the process's root IFolder object
	void* root;
	//! Points to a buffer containing the process's initial command line
	wchar_t* cmdline;
	//! Points to the the module_info_t structure relating to the first module 
	//!		loaded. Subsequent module_info_t's form a linked list through each
	//!		module's next member.
	module_info_t* module_first;
	//! Contains the process's current working directory, as a fully-qualified path
	wchar_t cwd[256];
	wchar_t *environment;
	addr_t base;
	addr_t input, output;
};

#ifdef _MSC_VER

#pragma warning(disable:4035)
__inline thread_info_t* thrGetInfo() { __asm mov eax, fs:[4] }
#pragma warning(default:4035)

#else
//! Retrieves a pointer to the current thread's thread_info_t structure.
static inline thread_info_t* thrGetInfo()
{
	thread_info_t* ret;
	asm("mov %%fs:(4), %0" : "=r" (ret));
	return ret;
}
#endif

#ifndef callconv_defined
#ifdef _MSC_VER
#define STDCALL	__stdcall
#define CDECL	__cdecl
#else
#define STDCALL	__attribute__((stdcall))
#define CDECL	__attribute__((cdecl))
#endif
#define callconv_defined
#endif

#ifndef KERNEL

/*!
 *	\ingroup	kernelu
 *	\defgroup	proc	Processes
 *	@{
 */

dword	procLoad(const wchar_t* filespec, const wchar_t* cmdline, 
				 unsigned priority, addr_t input, addr_t output);
void	procExit();
dword	procCurrentId();
wchar_t*	procCmdLine();
wchar_t*	procCwd();
addr_t		procBase();

//@}

/*!
 *	\ingroup	kernelu
 *	\defgroup	sys	General System
 *	@{
 */
addr_t	sysOpen(const wchar_t* filespec);
dword	sysInvoke(void* obj, dword method, ...);
void	sysSetErrno(int errno);
int		sysErrno();
dword	sysUpTime();
bool	sysGetErrorText(status_t stat, wchar_t* text, size_t max);

//@}

/*!
 *	\ingroup	kernelu
 *	\defgroup	thr	Threads
 *	@{
 */

#define THREAD_PRIORITY_NORMAL	16

void	thrSetTls(dword value);
dword	thrGetTls();
void	thrCall(void* addr, void* params, size_t sizeof_params);
void	thrSleep(dword ms);
unsigned	thrWaitHandle(addr_t* hnd, unsigned num_handles, bool wait_all);
thread_info_t*	thrGetInfo();
dword	thrGetId();
addr_t	thrCreate(void* entry_point, dword param, unsigned priority);
void	thrExit(int code);
//@}

typedef struct { dword unused; } *marshal_t;

marshal_t	objMarshal(void* ptr);
void*	objUnmarshal(marshal_t mshl);
void	objNotifyDelete(marshal_t mshl);

/*!
 *	\ingroup	kernelu
 *	\defgroup	vmm	Virtual Memory Manager
 *	@{
 */

void*	vmmAlloc(size_t pages, addr_t start, dword flags);
//@}


//! Defines the entry point to a dynamic link library (DLL).
/*!
 *	Every DLL is notified of certain events relevant to its execution, such
 *		as being loaded into and unloaded from a process's address space,
 *		and the creation and deletion of a process's threads. The DllMain() 
 *		function provides a way of the kernel notifying the library of such
 *		events.
 *	\param	reserved1	Reserved on The Möbius; corresponds to the HINSTANCE
 *		parameter on Win32.
 *	\param	reason		Specifies the reason for calling DllMain
 *	\param	reserved2	Reserved on The Möbius
 *	\return	true if successful (e.g. if the DLL is to be allowed to load); 
 *		false otherwise.
 */
bool STDCALL DllMain(dword reserved1, dword reason, void* reserved2);

#endif

/*
 * All the following functions are implemented both in the kernel and in 
 *  kernelu.dll. They may be called from either kernel or user mode.
 */

/*!
 *	\defgroup	res Resources
 *	\ingroup	kernelu
 *
 *	Because it uses the Portable Executable format, The Möbius is able to embed
 *		resources into executables. These can be accessed easily from user 
 *		mode, since they are stored in their own section in the module, and are
 *		organised in a tree structure, by type/ID/language.
 *
 *	The resXXX() functions will load the different resource types; resFind() 
 *		can be used to load all resources, including ones with user-defined 
 *		types.
 *
 *	@{
 */

const void*		resFind(dword base, word type, word id, word language);
bool	resLoadString(dword base, word id, wchar_t* str, size_t str_max);

/*!
 *	\defgroup	RT	Resource Type Identifiers
 *
 *	These are the same as the standard Windows numerical resource IDs. They 
 *		identify module resource types and can be used with the resFind() 
 *		function.
 *
 *	It is also possible to define new resource types.
 *	\note	The Möbius does not currently support string resource or type IDs.
 *	@{
 */

//! Cursor
#define RT_CURSOR		1
//! Bitmap
#define RT_BITMAP		2
//! Icon
#define RT_ICON			3
//! Menu
#define RT_MENU			4
//! Dialog box
#define RT_DIALOG		5
//! Block of 16 strings
#define RT_STRING		6
//! Font list
#define RT_FONTDIR		7
//! Font
#define RT_FONT			8
//! Acclerator table
#define RT_ACCEL		9
//! Arbitrary binary data
#define RT_RCDATA		10
//! Table of error messages
#define RT_MSGTABLE		11
//! Group of cursors describing the same image at different resolutions and 
//!		colour depths
#define RT_GROUP_CURSOR	12
//! Group of icons describing the same image at different resolutions and 
//!		colour depths
#define RT_GROUP_ICON	13
//! Module version information block
#define RT_VERSION		16
/*! @} @} */

/*!
 *	\ingroup vmm
 *	@{
 */
//! Specifies that the area can only be accessed by the kernel
#define MEM_KERNEL	0
//! Specifies that the area can be accessed in user mode
#define MEM_USER	3
//! Causes the area to be committed when it is allocated
#define MEM_COMMIT	0x4
//! Allows the area to be read from
#define MEM_READ	0x8
//! Allows the area to be written to
#define MEM_WRITE	0x10
//! Causes the area to be zeroed when it is committed
#define MEM_ZERO	0x20
//! Specifies that the start parameter to vmmAlloc() refers to a physical 
//!		address which has already been allocated in the process's address space.
#define MEM_LITERAL	0x40
//! For memory-mapped files, specifies that the file is an executable image 
//!	and that the memory alignment is independent of the file alignment
#define MEM_FILE_IMAGE	0x80
//@}

//@}

addr_t	hndGetOwner(addr_t handle);

#ifndef KERNEL

typedef struct semaphore_t semaphore_t;
struct semaphore_t
{
	addr_t owner;
	int locks;
};

void semInit(semaphore_t* sem);
void semAcquire(semaphore_t* sem);
void semRelease(semaphore_t* sem);
#endif

#ifdef __cplusplus
}
#endif

#endif