#include "idt.h"


char * interrupt_messages[] =
{
    "Divide By Zero",
    "Debug",
    "Non Maskable Interrupt",
    "Breakpoint",
    "Into Detected Overflow",
    "Out of Bounds",
    "Invalid Opcode",
    "No Coprocessor",
    "Double Fault",
    "Coprocessor Segment Overrun",
    "Bad TSS",
    "Segment Not Present",
    "Stack Fault",
    "General Protection Fault",
    "Page Fault",
    "Unknown Interrupt",
    "Coprocessor Fault",
    "Alignment Check",
    "Machine Check",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved"
};

void idtStart(void) {
    /* Set IDT Pointer */
    idt_pointer.limit =  (uint16_t)((sizeof(idt_entry_t) * 256) - 1);
    idt_pointer.base = (uint64_t)IDT;

    /* Clear Memory for IDT's */
    memset((uint8_t *)IDT, 0, sizeof(idt_entry_t) * 256);

    // Remap the irq table.
    outb(0x20, 0x11);
    outb(0xA0, 0x11);
    outb(0x21, 0x20);
    outb(0xA1, 0x28);
    outb(0x21, 0x04);
    outb(0xA1, 0x02);
    outb(0x21, 0x01);
    outb(0xA1, 0x01);
    outb(0x21, 0x0);
    outb(0xA1, 0x0);

    /* Set IDT Gates */
    idtSet(0, (uint64_t)isr0, 0x08, 0x8E);
    idtSet(1, (uint64_t)isr1, 0x08, 0x8E);
    idtSet(2, (uint64_t)isr2, 0x08, 0x8E);
    idtSet(3, (uint64_t)isr3, 0x08, 0x8E);
    idtSet(4, (uint64_t)isr4, 0x08, 0x8E);
    idtSet(5, (uint64_t)isr5, 0x08, 0x8E);
    idtSet(6, (uint64_t)isr6, 0x08, 0x8E);
    idtSet(7, (uint64_t)isr7, 0x08, 0x8E);
    idtSet(8, (uint64_t)isr8, 0x08, 0x8E);
    idtSet(9, (uint64_t)isr9, 0x08, 0x8E);
    idtSet(10, (uint64_t)isr10, 0x08, 0x8E);
    idtSet(11, (uint64_t)isr11, 0x08, 0x8E);
    idtSet(12, (uint64_t)isr12, 0x08, 0x8E);
    idtSet(13, (uint64_t)isr13, 0x08, 0x8E);
    idtSet(14, (uint64_t)isr14, 0x08, 0x8E);
    idtSet(15, (uint64_t)isr15, 0x08, 0x8E);
    idtSet(16, (uint64_t)isr16, 0x08, 0x8E);
    idtSet(17, (uint64_t)isr17, 0x08, 0x8E);
    idtSet(18, (uint64_t)isr18, 0x08, 0x8E);
    idtSet(19, (uint64_t)isr19, 0x08, 0x8E);
    idtSet(20, (uint64_t)isr20, 0x08, 0x8E);
    idtSet(21, (uint64_t)isr21, 0x08, 0x8E);
    idtSet(22, (uint64_t)isr22, 0x08, 0x8E);
    idtSet(23, (uint64_t)isr23, 0x08, 0x8E);
    idtSet(24, (uint64_t)isr24, 0x08, 0x8E);
    idtSet(25, (uint64_t)isr25, 0x08, 0x8E);
    idtSet(26, (uint64_t)isr26, 0x08, 0x8E);
    idtSet(27, (uint64_t)isr27, 0x08, 0x8E);
    idtSet(28, (uint64_t)isr28, 0x08, 0x8E);
    idtSet(29, (uint64_t)isr29, 0x08, 0x8E);
    idtSet(30, (uint64_t)isr30, 0x08, 0x8E);
    idtSet(31, (uint64_t)isr31, 0x08, 0x8E);
    idtSet(32, (uint64_t)irq0, 0x08, 0x8E);
    idtSet(33, (uint64_t)irq1, 0x08, 0x8E);
    idtSet(34, (uint64_t)irq2, 0x08, 0x8E);
    idtSet(35, (uint64_t)irq3, 0x08, 0x8E);
    idtSet(36, (uint64_t)irq4, 0x08, 0x8E);
    idtSet(37, (uint64_t)irq5, 0x08, 0x8E);
    idtSet(38, (uint64_t)irq6, 0x08, 0x8E);
    idtSet(39, (uint64_t)irq7, 0x08, 0x8E);
    idtSet(40, (uint64_t)irq8, 0x08, 0x8E);
    idtSet(41, (uint64_t)irq9, 0x08, 0x8E);
    idtSet(42, (uint64_t)irq10, 0x08, 0x8E);
    idtSet(43, (uint64_t)irq11, 0x08, 0x8E);
    idtSet(44, (uint64_t)irq12, 0x08, 0x8E);
    idtSet(45, (uint64_t)irq13, 0x08, 0x8E);
    idtSet(46, (uint64_t)irq14, 0x08, 0x8E);
    idtSet(47, (uint64_t)irq15, 0x08, 0x8E);

    /*printk("idt_pointer: 0x%x\n",idt_pointer);
    printk("idt_pointer.limit: %d\n",idt_pointer.limit);
    printk("idt_pointer.base: 0x%x\n",idt_pointer.base);
    printk("IDT: 0x%x\n",IDT);
    printk("IDT[0]: 0x%x\n",IDT[0].baseHigh);
    printk("IDT[0]: 0x%x\n",IDT[0].baseMid);
    printk("IDT[0]: 0x%x\n",IDT[0].baseLow);
    printk("IDT[1]: 0x%x\n",IDT[1].baseHigh);
    printk("IDT[1]: 0x%x\n",IDT[1].baseMid);
    printk("IDT[1]: 0x%x\n",IDT[1].baseLow);
    printk("IDT[2]: 0x%x\n",IDT[2].baseHigh);
    printk("IDT[2]: 0x%x\n",IDT[2].baseMid);
    printk("IDT[2]: 0x%x\n",IDT[2].baseLow);
    printk("IDT[3]: 0x%x\n",IDT[3].baseHigh);
    printk("IDT[3]: 0x%x\n",IDT[3].baseMid);
    printk("IDT[3]: 0x%x\n",IDT[3].baseLow);

    printk("sizeof idt pointer: %d\n",sizeof(idt_pointer));*/

    /*printk("cr3: 0x%x\n",read_cr3());
    write_cr3(0x0);*/

    /* Load IDT Table */
    idtInit();

}

void idtSet(uint8_t number, uint64_t base, uint16_t selector, uint8_t flags)
{
   /* Set Base Address */
   IDT[number].baseLow = (uint16_t)(base & 0xFFFF);
   IDT[number].baseMid = (uint16_t)((base >> 16) & 0xFFFF);
   IDT[number].baseHigh = (uint32_t)((base >> 32) & 0xFFFFFFFF);
   //printk("base: 0x%x\n",base);
   /* Set Selector */
   IDT[number].selector = selector;
   IDT[number].flags = flags;

   /* Set Reserved Areas to Zero */
   IDT[number].reservedIst = 0;
   IDT[number].reserved = 0;
}

void register_all_interrupt()
{
    // Nullify all the interrupt handlers.
    memset(&interrupt_handlers, 0, sizeof(isr_t)*256);
    int x=0;
    printk("register_all_interrupt\n");
    for (x=0 ;x<32;x++)
    {
            register_interrupt_handler(x, ISR_handle);
    }
    for (x=32 ;x<48;x++)
    {
            register_interrupt_handler(x, IRQ_handle);
    }

}

void ISR_handle(registers *regs)
{
    char* msg = interrupt_messages[regs->intNo];
    reg_dump(regs);
    panic(msg);
}



void IRQ_handle(registers *regs)
{
    reg_dump(regs);
    //panic("IRQ ");
    //asm volatile ("sti");
}

void register_interrupt_handler(uint8_t n, isr_t handler)
{
    interrupt_handlers[n] = handler;
}

void reg_dump(registers *regs)
{
  printk("Exception: %s\n", regs->intNo);
  printk("  rax=%016x\n", regs->rax);
  printk("  rbx=%016x\n", regs->rbx);
  printk("  rcx=%016x\n", regs->rcx);
  printk("  rdx=%016x\n", regs->rdx);
  printk("  rsi=%016x\n", regs->rsi);
  printk("  rdi=%016x\n", regs->rdi);
  printk("  rip=%016x\n", regs->rip);
  printk("  rsp=%016x\n", regs->userrsp);
  printk("  rbp=%016x\n", regs->rbp);
  printk("  cs=%02x\n", regs->cs);
  printk("  ss=%02x\n", regs->ss);
}

// This gets called from our ASM interrupt handler stub.
void isr_handler(registers* regs)
{
    // This line is important. When the processor extends the 8-bit interrupt number
    // to a 32bit value, it sign-extends, not zero extends. So if the most significant
    // bit (0x80) is set, regs.intNo will be very large (about 0xffffff80).
    uint8_t intNo = regs->intNo & 0xFF;
    printk("inside isr_handler isr %d\n",intNo);

    if (interrupt_handlers[intNo] != 0)
    {
        isr_t handler = interrupt_handlers[intNo];
        handler(regs);
    }
    else
    {
        printk("unhandled ISR: %d\n",intNo);
        panic ("Interruption non prise en charge\n");
        while (1);
        asm("hlt");
    }
}

// This gets called from our ASM interrupt handler stub.
void irq_handler(registers* regs)
{
    printk("IRQ : %d\n",regs->intNo-32);
    // Send an EOI (end of interrupt) signal to the PICs.
    // If this interrupt involved the slave.
    if (regs->intNo >= 40)
    {
        // Send reset signal to slave.
        outb(0xA0, 0x20);
    }
    // Send reset signal to master. (As well as slave, if necessary).
    outb(0x20, 0x20);
    if (interrupt_handlers[regs->intNo] != 0)
    {
        isr_t handler = interrupt_handlers[regs->intNo];
        handler(&regs);
    }
    else
    {
        printk("unhandled IRQ: %d\n",regs->intNo-32);
        panic ("Interruption non prise en charge\n");
        while (1);

        asm("hlt");
    }
    printk("End of IRQ : %d\n",regs->intNo-32);
}
