#include "ibm.h"
#include "io.h"

#include "lpt.h"
#include "lpt_dac.h"
#include "lpt_dss.h"

#include <pcem/devices.h>
#include <pcem/defines.h>

#ifdef USE_EXPERIMENTAL_PRINTER
#include "lpt_epsonlx810.h"
#endif

char lpt1_device_name[16];

int lpt1_current = 0;

extern LPT_DEVICE *lpt_devices[LPT_MAX];

LPT_DEVICE l_none = {"None", "none", NULL};
LPT_DEVICE l_dss = {"Disney Sound Source", "dss", &dss_device};
LPT_DEVICE l_lpt_dac = {"LPT DAC / Covox Speech Thing", "lpt_dac", &lpt_dac_device};
LPT_DEVICE l_lpt_dac_stereo = {"Stereo LPT DAC", "lpt_dac_stereo", &lpt_dac_stereo_device};

char *lpt_device_get_name(int id) {
        if (lpt_devices[id] == NULL || strlen(lpt_devices[id]->name) == 0)
                return NULL;
        return lpt_devices[id]->name;
}
char *lpt_device_get_internal_name(int id) {
        if (lpt_devices[id] == NULL || strlen(lpt_devices[id]->internal_name) == 0)
                return NULL;
        return lpt_devices[id]->internal_name;
}

static lpt_device_t *lpt1_device;
static void *lpt1_device_p;

void lpt1_device_init() {
        int c = 0;

        while (lpt_devices[c] != NULL && strcmp(lpt_devices[c]->internal_name, lpt1_device_name) &&
               strlen(lpt_devices[c]->internal_name) != 0)
                c++;

        if (lpt_devices[c] == NULL || strlen(lpt_devices[c]->internal_name) == 0)
                lpt1_device = NULL;
        else if (lpt_devices[c] != NULL) {
                lpt1_device = lpt_devices[c]->device;
                if (lpt1_device)
                        lpt1_device_p = lpt1_device->init();
        }
}

void lpt1_device_close() {
        if (lpt1_device)
                lpt1_device->close(lpt1_device_p);
        lpt1_device = NULL;
}

static uint8_t lpt1_dat, lpt2_dat;
static uint8_t lpt1_ctrl, lpt2_ctrl;

void lpt1_write(uint16_t port, uint8_t val, void *priv) {
        switch (port & 3) {
        case 0:
                if (lpt1_device)
                        lpt1_device->write_data(val, lpt1_device_p);
                lpt1_dat = val;
                break;
        case 2:
                if (lpt1_device)
                        lpt1_device->write_ctrl(val, lpt1_device_p);
                lpt1_ctrl = val;
                break;
        }
}

int lpt_device_has_config(int devId) {
        if (lpt_devices[devId] == NULL || !lpt_devices[devId]->device)
                return 0;
        return lpt_devices[devId]->device->config ? 1 : 0;
}

lpt_device_t *lpt_get_device(int devId) {
        if (lpt_devices[devId] == NULL || !lpt_devices[devId]->device)
                return 0;

        return lpt_devices[devId]->device;
}

int lpt_device_get_from_internal_name(char *s) {
        int c = 0;

        while (lpt_devices[c] != NULL && strlen(lpt_devices[c]->internal_name)) {
                if (!strcmp(lpt_devices[c]->internal_name, s))
                        return c;
                c++;
        }

        return 0;
}

uint8_t lpt1_read(uint16_t port, void *priv) {
        switch (port & 3) {
        case 0:
                return lpt1_dat;
        case 1:
                if (lpt1_device)
                        return lpt1_device->read_status(lpt1_device_p);
                return 0;
        case 2:
                return lpt1_ctrl;
        }
        return 0xff;
}

void lpt2_write(uint16_t port, uint8_t val, void *priv) {
        switch (port & 3) {
        case 0:
                lpt2_dat = val;
                break;
        case 2:
                lpt2_ctrl = val;
                break;
        }
}
uint8_t lpt2_read(uint16_t port, void *priv) {
        switch (port & 3) {
        case 0:
                return lpt2_dat;
        case 1:
                return 0;
        case 2:
                return lpt2_ctrl;
        }
        return 0xff;
}

void lpt_init() {
        io_sethandler(0x0378, 0x0003, lpt1_read, NULL, NULL, lpt1_write, NULL, NULL, NULL);
        io_sethandler(0x0278, 0x0003, lpt2_read, NULL, NULL, lpt2_write, NULL, NULL, NULL);
}

void lpt1_init(uint16_t port) {
        if (port)
                io_sethandler(port, 0x0003, lpt1_read, NULL, NULL, lpt1_write, NULL, NULL, NULL);
}
void lpt1_remove() {
        io_removehandler(0x0278, 0x0003, lpt1_read, NULL, NULL, lpt1_write, NULL, NULL, NULL);
        io_removehandler(0x0378, 0x0003, lpt1_read, NULL, NULL, lpt1_write, NULL, NULL, NULL);
        io_removehandler(0x03bc, 0x0003, lpt1_read, NULL, NULL, lpt1_write, NULL, NULL, NULL);
}
void lpt2_init(uint16_t port) {
        if (port)
                io_sethandler(port, 0x0003, lpt2_read, NULL, NULL, lpt2_write, NULL, NULL, NULL);
}
void lpt2_remove() {
        io_removehandler(0x0278, 0x0003, lpt2_read, NULL, NULL, lpt2_write, NULL, NULL, NULL);
        io_removehandler(0x0378, 0x0003, lpt2_read, NULL, NULL, lpt2_write, NULL, NULL, NULL);
        io_removehandler(0x03bc, 0x0003, lpt2_read, NULL, NULL, lpt2_write, NULL, NULL, NULL);
}

void lpt2_remove_ams() { io_removehandler(0x0379, 0x0002, lpt2_read, NULL, NULL, lpt2_write, NULL, NULL, NULL); }

void lpt_init_builtin() {
        pcem_add_lpt(&l_none);
        pcem_add_lpt(&l_dss);
        pcem_add_lpt(&l_lpt_dac);
        pcem_add_lpt(&l_lpt_dac_stereo);
}