// SPDX-License-Identifier: MPL-2.0
#include "std/stdlib.h"
#include "irq/irq.h"
#include "irq/gsi.h"
#include "devs/apic/lapic.h"
#include "cpu/pic.h"
sh_uint64 tsc_delta;
sh_uint64 tsc_start;
sh_uint64 tsc_end;
sh_uint8 tsc_state=2;
sh_bool irq_managed_by_ioapic=SH_FALSE;
sh_irq_HANDLER_PTR *legacy_irq_handler_array;
sh_SPIN_LOCK frame_presentation_lock=SH_LOCK();
void sh_irq_start_tsc() {
  tsc_delta=0;
  tsc_start=0;
  tsc_end=0;
  tsc_state=0;
}
sh_uint64 sh_irq_get_tsc_delta() {
  return tsc_delta;
}
void sh_irq_start_timer() {
  sh_smp_gs_base()->per_cpu->timer_state=SH_TRUE;
  return;
}
sh_bool sh_irq_get_timer_state() {
  return sh_smp_gs_base()->per_cpu->timer_state;
}
SH_STATUS sh_irq_switch_irq_management() {
  if (irq_managed_by_ioapic) return SH_STATUS_ALREADY_INITIALIZED;
  irq_managed_by_ioapic=SH_TRUE;
  legacy_irq_handler_array=sh_malloc(sizeof(sh_irq_HANDLER_PTR)*16);
  if (!legacy_irq_handler_array) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  sh_mem_set_8((sh_uint8*)legacy_irq_handler_array,0x0,sizeof(sh_irq_HANDLER_PTR)*16);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_irq_legacy_register_handler(sh_uint8 legacy_irq,sh_irq_HANDLER_PTR handler) {
  if (legacy_irq>15 || handler==SH_NULLPTR || legacy_irq==0) return SH_STATUS_INVALID_PARAMETER;
  if (legacy_irq_handler_array==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  if (legacy_irq_handler_array[legacy_irq]!=SH_NULLPTR) return SH_STATUS_ALREADY_INITIALIZED;
  legacy_irq_handler_array[legacy_irq]=handler;
  SH_STATUS status=sh_gsi_irq_unmask(legacy_irq);
  if (sh_status_error(status)) return status;
  return SH_STATUS_SUCCESS;
}
void sh_irq_present_frame(sh_irq_INTERRUPT_FRAME *frame) {
  sh_spin_lock(&frame_presentation_lock);
  if (frame->error_code) {
    sh_iprintf(SH_FAULT,"IDT Entry Index (vector): %8u | Error code: %8u\n",frame->vector,frame->error_code);
  } else {
    sh_iprintf(SH_FAULT,"IDT Entry Index (vector): %8u | No error code provided.\n",frame->vector);
  }
  sh_iprintf(SH_FAULT,"RIP: 0x%x | R8 : 0x%x\n",frame->rip,frame->r8);
  sh_iprintf(SH_FAULT,"RAX: 0x%x | R9 : 0x%x\n",frame->rax,frame->r9);
  sh_iprintf(SH_FAULT,"RCX: 0x%x | R10: 0x%x\n",frame->rcx,frame->r10);
  sh_iprintf(SH_FAULT,"RDX: 0x%x | R11: 0x%x\n",frame->rdx,frame->r11);
  sh_iprintf(SH_FAULT,"CS : 0x%x | RFLAGS: 0x%x\n",frame->cs,frame->rflags);
  sh_spin_unlock(&frame_presentation_lock);
  return;
}
void sh_irq_dispatch(sh_irq_INTERRUPT_FRAME *frame) {
  switch (frame->vector) {
    case 0:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_DIV_BY_ZERO_FAULT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 1:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_DEBUG_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 2:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_NMI_FAULT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 3:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_BREAKPOINT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      // usage of breakpoints is authorized
      break;
    case 4:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_OVERFLOW_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 5:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_BOUND_RANGE_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 6:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_INVALID_OPCODE_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 7:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_DEVICE_NOT_AVAILABLE_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 8:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_DOUBLE_FAULT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 9:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_COPROCESSOR_SEGMENT_OVRERRUN_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 10:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_INVALID_TSS_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 11:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_SEGMENT_NOT_PRESENT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 12:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_STACK_SEGMENT_FAULT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 13:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_GENERAL_PROTECTION_FAULT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 14:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_PAGE_FAULT_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 16:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_X87_FLOATING_POINT_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 17:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_ALIGNMENT_CHECK_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 18:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_MACHINE_CHECK_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 19:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_SIMD_FLOATING_POINT_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 20:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_VIRTUALIZATION_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 21:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_CONTROL_PROTECTION_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 28:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_HYPERVISOR_INJECTION_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 29:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_VMM_COMMUNICATION_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 30:
      sh_iprintf(SH_FAULT,"CPU Fault Hapenned: %s\n",SH_IRQ_SECURITY_EXCEPTION_STRING);
      if (frame->vector<32) sh_irq_present_frame(frame);
      while (SH_TRUE) {};
      break;
    case 32:
      if (frame->vector<32) sh_irq_present_frame(frame);
      if (irq_managed_by_ioapic) {
        sh_iprint(SH_LOG,"Interrupt: PIT timer outside of TSC frequency estimation, managed by IOAPIC.\n");
        sh_lapic_eoi(sh_lapic_get_dev_apic_id((sh_uint64)sh_smp_gs_base()->lapic_id));
        break;
      } else {
        if (tsc_state==0) {
          tsc_start=sh_tsc_read_tsc();
          tsc_state++;
        } else if (tsc_state==1) {
          tsc_end=sh_tsc_read_tsc();
          tsc_delta=tsc_end-tsc_start;
          tsc_state=2;
        } else {
          sh_iprint(SH_LOG,"Interrupt: PIT timer outside of TSC frequency estimation.\n");
        }
        sh_pic_send_eoi(0);
      }
      break;
    case 33:
    case 34:
    case 35:
    case 36:
    case 37:
    case 38:
    case 39:
    case 40:
    case 41:
    case 42:
    case 43:
    case 44:
    case 45:
    case 46:
    case 47:
      if (frame->vector<32) sh_irq_present_frame(frame);
      if (irq_managed_by_ioapic) {
        if (legacy_irq_handler_array[frame->vector-32]!=SH_NULLPTR) {
          legacy_irq_handler_array[frame->vector-32]();
          sh_lapic_eoi(sh_lapic_get_dev_apic_id((sh_uint64)sh_smp_gs_base()->lapic_id));
        } else {
          sh_lapic_eoi(sh_lapic_get_dev_apic_id((sh_uint64)sh_smp_gs_base()->lapic_id));
        }
        break;
      } else {
        sh_iprintf(SH_LOG,"Interrupt: received interrupt on vector %1u, legacy IRQ not managed by IOAPIC.\n",frame->vector);
        while (SH_TRUE) {};
      }
    case 254:
      if (sh_smp_gs_base()->per_cpu->timer_state==SH_FALSE) sh_iprint(SH_LOG,"Interrupt: LAPIC One Shot Timer finished");
      if (frame->vector<32) sh_irq_present_frame(frame);
      if (sh_smp_gs_base()->per_cpu->timer_state==SH_TRUE) sh_smp_gs_base()->per_cpu->timer_state=SH_FALSE;
      sh_lapic_eoi(sh_lapic_get_dev_apic_id(sh_smp_gs_base()->lapic_id));
      break;
    case 255:
      sh_iprint(SH_LOG,"Interrupt: Spurious vector");
      if (frame->vector<32) sh_irq_present_frame(frame);
      break;
  }
}
