#pragma once

#include "../../../includes.hpp"

#include "../../disk/ata/bus.hpp"
#include "../../disk/ata/controller.hpp"

#include "../../input/devices/controller_ps2.hpp"
#include "../../input/devices/interface_device_ps2.hpp"

#include "../../kernel.hpp"


namespace MOSS { namespace Interrupts {


//See Intel Manual, section 6.13.
//	This error code works for all exceptions that push error codes except page faults (see note in section)
class ErrorCode final { public:
	bool              external_event :  1; //1=The exception occurred during delivery of an event external to the program, such as an interrupt or an earlier exception.
	bool              descr_location :  1; //0=index refers to a descriptor in the GDT or LDT, 1=index refers to a gate descriptor in the IDT.
	bool                descr_in_LDT :  1; //Used iff descr_location=0.  0=index refers to descriptor in GDT, 1=index refers to segment/gate descriptor in LDT.
	uint16_t segement_selector_index : 13; //Current manual seems to imply this is the right size (the "upper half" is reserved, and it's 32 bits big altogether).  Confirmed by http://ftp.utcluj.ro/pub/users/nedevschi/PMP/protected86/protgemode/translate_c242.html.
	uint16_t                reserved : 16;
} __attribute__((packed));
static_assert(sizeof(Interrupts::ErrorCode)==4,"Interrupt error code (normal type) is the wrong size!");
//Page faults are special (see around pg. 285)
class ErrorCodePF final { public:
	bool            P :  1; //Caused by 0=non-present page, 1=page-level protection violation
	bool           WR :  1; //Access causing fault was 0=read, 1=write
	bool           US :  1; //Access causing fault happened while running in 0=supervisor mode, 1=user mode
	bool         RSVD :  1; //0=The fault was not caused by reserved bit violation, 1=The fault was caused by reserved bits set to 1 in a page directory
	bool           ID :  1; //Whether the fault was caused by an instruction fetch
	uint32_t reserved : 27;
} __attribute__((packed));
static_assert(sizeof(Interrupts::ErrorCodePF)==4,"Interrupt error code (for page faults) is the wrong size!");


//Exactly what the lower interrupts (0-31) are is very inconsistent across the web, and sources frequently disagree, especially about
//	certain interrupts.  The authoritative source for MOSS is taken to be the Intel Manual
//	(http://www.intel.com/Assets/en_US/PDF/manual/253668.pdf, around page 233), listed along with mnemonic (if available) and type.
//	Where conflicting (or extra), other sources' input is listed on tab-indented lines below.

//One alternate source: http://www.osdever.net/bkerndev/Docs/isrs.htm

//"#DE": Fault: Divide Error (generated by "div", "idiv")
//No error code
//	Also divide by 0 exception
inline void isr0(void) {
	assert_term(false,"Handling divide-by-zero (stub)");
}

//"#DB": (Fault/Trap): RESERVED (for Intel use only)
//No error code
//	Debug exceptions/single step (debugger) (trap xor fault)  Error code: "None.  Can be retrived from debug registers"; see http://www.brokenthorn.com/Resources/OSDev15.html.
inline void isr1(void) {
	assert_warn(false,"Got Intel-reserved exception 1!");
}

//     : (Interrupt): NMI Interrupt (generated by a nonmaskable, external interrupt)
//No error code
//	Intel reserved/non-maskable interrupt (NMI) pin (unclassed/trap).
inline void isr2(void) {
	assert_term(false,"Handling NMI (stub)");
}

//"#BP": (Trap): Breakpoint (generated by "int $0x3")
//No error code
//	Breakpoint exception (debugger).
inline void isr3(void) {
	assert_term(false,"Handling breakpoint (stub)");
}

//"#OF": (Trap): Overflow (generated by "int0")
//No error code
//	Overflow/"Into detected overflow" (trap).
inline void isr4(void) {
	assert_term(false,"Handling overflow (stub)");
}

//"#BR": (Fault): BOUND Range Exceeded (generated by "bound")
//No error code
//	Bounds check/out of bound exception (trap/fault?).
inline void isr5(void) {
	assert_term(false,"Handling bounds exceeded (stub)");
}

//"#UD": (Fault): Invalid Opcode (Undefined Opcode) (generated by "ud2" (Pentium Pro+) or reserved opcode)
//No error code
//	Invalid/undefined opcode exception (trap/fault?).
inline void isr6(void) {
	assert_term(false,"Handling invalid opcode (stub)");
}

//"#NM": (Fault): Device Not Available (No Math Coprocessor) (generated by a floating-point or "wait"/"fwait" instruction)
//No error code
//	Coprocessor not available/device not available exception (trap/fault?).
inline void isr7(void) {
	assert_term(false,"Handling no math (stub)");
}

//"#DF": (Abort): Double Fault (generated by any instruction that can generate an exception, NMI, or an interrupt)
//Has error code (always 0)
inline void isr8(ErrorCode/* error*/) {
	assert_term(false,"Handling double-fault (stub)");
}

//     : (Fault): Coprocessor Segment Overrun (reserved) (generated by a floating-point instruction)
//No error code
//Processors after the i386 do not generate this exception.
//	do not use (abort?/fault).
inline void isr9(void) {
	assert_term(false,"Got Intel-reserved exception 9!");
}

//"#TS": (Fault): Invalid TSS (generated by task switch or TSS access)
//Has error code
inline void isr10(ErrorCode/* error*/) {
	assert_term(false,"Handling TSS (stub)");
}

//"#NP": (Fault): Segment Not Present (generated by loading segment registers or accessing system segments)
//Has error code
inline void isr11(ErrorCode/* error*/) {
	assert_term(false,"Handling segment (stub)");
}

//"#SS": (Fault): Stack-Segment Fault (generated by stack operations or "ss" register loads)
//Has error code
//	Stack fault exception/stack segment overrun (fault).
inline void isr12(ErrorCode/* error*/) {
	assert_term(false,"Handling stack (stub)");
}

//"#GP": (Fault): General Protection (generated by any memory reference or other protection checks)
//Has error code
//	General protection exception/general protection fault/triple fault (fault xor trap).
inline void isr13(ErrorCode/* error*/) {
	assert_term(false,"Handling GPF (stub)");
}

//"#PF": (Fault): Page Fault (generated by any memory reference)
//Has error code
inline void isr14(ErrorCodePF/* error*/) {
	assert_term(false,"Handling PF (stub)");
}

//     : (Unlisted): Intel reserved.  Do not use.
//No error code
//	Intel reserved?/unassigned.
inline void isr15(void) {
	assert_term(false,"Got Intel-reserved exception 15!");
}

//"#MF": (Fault): x87 FPU Floating-Point Error (Math Fault) (generated by x87 FPU floating-point or "wait"/"fwait" instruction)
//No error code
//	Coprocessor error/x87 FPU floating-point error (math fault) (fault).  No error code (x87 FPU provides own error information).
inline void isr16(void) {
	assert_term(false,"Handling FPU (stub)");
}

//"#AC": (Fault): Alignment Check (generated by any data reference in memory)
//Has error code (always 0)
//i486+ only
//	exception.
//	Different sources disagree about whether it has an error code.  E.g.
//		http://www.acm.uiuc.edu/sigops/roll_your_own/i386/idt.html says none
//		http://www.brokenthorn.com/Resources/OSDev15.html says there is one and it's always 0
//		The last post here: http://forum.osdev.org/viewtopic.php?f=1&t=23998&sid=98cd3b1e6b1256f0dbdb0885e84ba05f&start=15 mentions this issue.
inline void isr17(ErrorCode/* error*/) {
	assert_term(false,"Handling alignment (stub)");
}

//"#MC": (Abort): Machine Check (generation method(s) unlisted)
//No error code, although Intel manual implies that there may be error codes, and if so, that they are model-dependent.
//Introduced in Pentium, improved in P6 family
//	Machine check exception (Pentium/586+ only) (abort).  No error code (MSRs provide error information).
inline void isr18(void) {
	assert_term(false,"Handling machine check (stub)");
}

//"#XM": (Fault): SIMD Floating-Point Exception (generated by SSE/SSE2/SSE3 floating-point instructions).
//No error code
//Pentium III+ only
//	SIMD FPU exception (fault).
inline void isr19(void) {
	assert_term(false,"Handling SIMD FPU (stub)");
}

//     : (Unlisted): Intel reserved.  Do not use.
//Whether has error code is unlisted
inline void isrs20through31(void) {
	assert_term(false,"Got Intel reserved interrupt [20,31]!");
}

//     : (Interrupt): User-Defined (Non-reserved) Interrupts (generated by external iterrupt or "int _" instruction)
//No error codes (either called by PICs or "int" instructions, neither of which push error codes.
//<All the rest of the ISRs>

//The first 16 of these are where the PICs' output is remapped, so the description for each ISR is the description of
//	the IRQ it handles (see http://wiki.osdev.org/Interrupts).

//ISR 32 = IRQ 0 (Programmable Interrupt Timer Interrupt)
inline bool isr32(void) {
	//For now, we just ignore the timer
	return true;
}

//ISR 33 = IRQ 1 (Keyboard Interrupt)
inline bool isr33(void) {
	//kernel->write("YAY!");
	//ASSERT(false,"Handling IRQ 1 (stub)\n"); return false; //TODO: The line below is not properly implemented yet
	return kernel->controller_ps2->device0->handle_irq();
}

//ISR 34 = IRQ 2 (Cascade (used internally by the two PICs. never raised))
inline bool isr34(void) {
	assert_term(false,"Handling IRQ 2 (stub)"); return false;
}

//ISR 35 = IRQ 3 (COM2 (if enabled))
inline bool isr35(void) {
	assert_term(false,"Handling IRQ 3 (stub)"); return false;
}

//ISR 36 = IRQ 4 (COM1 (if enabled))
inline bool isr36(void) {
	assert_term(false,"Handling IRQ 4 (stub)"); return false;
}

//ISR 37 = IRQ 5 (LPT2 (if enabled))
inline bool isr37(void) {
	assert_term(false,"Handling IRQ 5 (stub)"); return false;
}

//ISR 38 = IRQ 6 (Floppy Disk)
inline bool isr38(void) {
	assert_term(false,"Handling IRQ 6 (stub)"); return false;
}

//ISR 39 = IRQ 7 (LPT1 / Unreliable "spurious" interrupt (usually))
inline bool isr39(void) {
	assert_term(false,"Handling IRQ 7 (stub)"); return false;
}

//ISR 40 = IRQ 8 (CMOS real-time clock (if enabled))
inline bool isr40(void) {
	assert_term(false,"Handling IRQ 8 (stub)"); return false;
}

//ISR 41 = IRQ 9 (Free for peripherals / legacy SCSI / NIC)
inline bool isr41(void) {
	assert_term(false,"Handling IRQ 9 (stub)"); return false;
}

//ISR 42 = IRQ 10 (Free for peripherals / SCSI / NIC)
inline bool isr42(void) {
	assert_term(false,"Handling IRQ 10 (stub)"); return false;
}

//ISR 43 = IRQ 11 (Free for peripherals / SCSI / NIC)
inline bool isr43(void) {
	assert_term(false,"Handling IRQ 11 (stub)"); return false;
}

//ISR 44 = IRQ 12 (PS2 Mouse)
inline bool isr44(void) {
	return kernel->controller_ps2->device1->handle_irq();
}

//ISR 45 = IRQ 13 (FPU / Coprocessor / Inter-processor)
inline bool isr45(void) {
	assert_term(false,"Handling IRQ 13 (stub)"); return false;
}

//ISR 46 = IRQ 14 (Primary ATA Hard Disk)
inline bool isr46(void) {
	//assert_term(false,"Handling IRQ 14 (stub)\n"); return false;
	assert_term(kernel->controller_ata->buses[0]!=nullptr,"ATA controller bus 0 is null!");
	kernel->controller_ata->buses[0]->handle_irq();
	return true;
}

//ISR 47 = IRQ 15 (Secondary ATA Hard Disk)
inline bool isr47(void) {
	//assert_term(false,"Handling IRQ 15 (stub)"); return false;
	assert_warn(kernel->controller_ata->buses[1]!=nullptr,"ATA controller bus 1 is null!");
	//kernel->controller_ata->buses[1]->handle_irq();
	return true;
}

//Unassigned [48,255] (208)



/*typedef bool(*ISR)(void);
class IQR_ISR final { public:
	ISR def; //The default ISR
	ISR isr; //Any overriding ISR

	bool operator()(void) const;
};
bool IQR_ISR::operator()(void) const {
	return (isr==nullptr) ? def() : isr();
}

IQR_ISR irq_isrs[16];

void init_isrs(void) {
	//Modules/devices can come along and override the default ISRs for IRQs, but they need to be function pointers to allow that.

	for (int i=0;i<16;++i) {
		irq_isrs[i].isr = nullptr;
	}
	#define SETUP_ISR(DEFAULT_ISR_INDEX) irq_isrs[DEFAULT_ISR_INDEX-32].def = isr##DEFAULT_ISR_INDEX;
		SETUP_ISR(32) irq_isrs[0].isr=irq_isrs[0].def;
		SETUP_ISR(33)
		SETUP_ISR(34)
		SETUP_ISR(35)
		SETUP_ISR(36)
		SETUP_ISR(37)
		SETUP_ISR(38)
		SETUP_ISR(39)
		SETUP_ISR(40)
		SETUP_ISR(41)
		SETUP_ISR(42)
		SETUP_ISR(43)
		SETUP_ISR(44)
		SETUP_ISR(45)
		SETUP_ISR(46)
		SETUP_ISR(47)
	#undef SETUP_ISR
}*/


}}
