#include <stdint.h>
#include <stddef.h>
#include "dev.h"
#include "serialdev.h"
#include "errors.h"
#include "util/util.h"
#include "hshtb.h"
#include "sysdefs/dev.h"
#include "kprintf.h"
#include "hal/hal.h"

// https://wiki.osdev.org/Serial_Ports

#define PORT 0x3f8

void serial_init(void) {
  io_out8(PORT+1, 0x00);
  io_out8(PORT+3, 0x80);
  io_out8(PORT+0, 0x03);
  io_out8(PORT+1, 0x00);
  io_out8(PORT+3, 0x03);
  io_out8(PORT+2, 0xC7);
  io_out8(PORT+4, 0x0B);
  io_out8(PORT+4, 0x1E);
  io_out8(PORT+0, 0xAE);

  if (io_in8(PORT+0) != 0xAE) {
    ERR("serial", "serial is faulty!\n");
    return;
  }

  io_out8(PORT+4, 0x0F);
}

int serial_recvready(void) {
  return io_in8(PORT+5) & 1;
}

uint8_t serial_recvb(void) {
  return io_in8(PORT);
}

int serial_sendready(void) {
  return io_in8(PORT+5) & 0x20;
}

void serial_sendb(uint8_t b) {
  io_out8(PORT, b);
}

int32_t serialdev_sendb(struct Dev *dev, uint8_t *buffer, size_t len, uint64_t pid) {
  (void)dev; (void)len; (void)pid;
  serial_sendb(buffer[0]);
  return E_OK;
}

int32_t serialdev_sendready(struct Dev *dev, uint8_t *buffer, size_t len, uint64_t pid) {
  (void)dev; (void)buffer; (void)len; (void)pid;
  return serial_sendready();
}

int32_t serialdev_recvb(struct Dev *dev, uint8_t *buffer, size_t len, uint64_t pid) {
  (void)dev; (void)buffer; (void)len; (void)pid;
  return serial_recvb();
}

int32_t serialdev_recvready(struct Dev *dev, uint8_t *buffer, size_t len, uint64_t pid) {
  (void)dev; (void)buffer; (void)len; (void)pid;
  return serial_recvready();
}

void serialdev_init(void) {
  Dev *serialdev = NULL;
  HSHTB_ALLOC(DEVTABLE.devs, ident, "serialdev", serialdev);
  serialdev->fns[DEV_SERIALDEV_SENDB] = &serialdev_sendb;
  serialdev->fns[DEV_SERIALDEV_SENDREADY] = &serialdev_sendready;
  serialdev->fns[DEV_SERIALDEV_RECVB] = &serialdev_recvb;
  serialdev->fns[DEV_SERIALDEV_RECVREADY] = &serialdev_recvready;
  spinlock_init(&serialdev->spinlock);
  serial_init();
}
