#include <stdlib.h>
#include "ibm.h"
#include "filters.h"
#include "lpt.h"
#include "lpt_dac.h"
#include "sound.h"

typedef struct dss_t {
        uint8_t fifo[16];
        int read_idx, write_idx;

        uint8_t dac_val;

        pc_timer_t timer;

        int16_t buffer[MAXSOUNDBUFLEN];
        int pos;
} dss_t;

static void dss_update(dss_t *dss) {
        for (; dss->pos < sound_pos_global; dss->pos++)
                dss->buffer[dss->pos] = (int8_t)(dss->dac_val ^ 0x80) * 0x40;
}

static void dss_write_data(uint8_t val, void *p) {
        dss_t *dss = (dss_t *)p;

        if ((dss->write_idx - dss->read_idx) < 16) {
                dss->fifo[dss->write_idx & 15] = val;
                dss->write_idx++;
        }
}

static void dss_write_ctrl(uint8_t val, void *p) {}

static uint8_t dss_read_status(void *p) {
        dss_t *dss = (dss_t *)p;

        if ((dss->write_idx - dss->read_idx) >= 16)
                return 0x40;
        return 0;
}

static void dss_get_buffer(int32_t *buffer, int len, void *p) {
        dss_t *dss = (dss_t *)p;
        int c;

        dss_update(dss);

        for (c = 0; c < len * 2; c += 2) {
                int16_t val = (int16_t)dss_iir((float)dss->buffer[c >> 1]);

                buffer[c] += val;
                buffer[c + 1] += val;
        }

        dss->pos = 0;
}

static void dss_callback(void *p) {
        dss_t *dss = (dss_t *)p;

        dss_update(dss);

        if ((dss->write_idx - dss->read_idx) > 0) {
                dss->dac_val = dss->fifo[dss->read_idx & 15];
                dss->read_idx++;
        }

        timer_advance_u64(&dss->timer, (TIMER_USEC * (1000000.0 / 7000.0)));
}

static void *dss_init() {
        dss_t *dss = malloc(sizeof(dss_t));
        memset(dss, 0, sizeof(dss_t));

        sound_add_handler(dss_get_buffer, dss);
        timer_add(&dss->timer, dss_callback, dss, 1);

        return dss;
}
static void dss_close(void *p) {
        dss_t *dss = (dss_t *)p;

        free(dss);
}

lpt_device_t dss_device = {"Disney Sound Source", 0,           dss_init,        dss_close, NULL, NULL, NULL, NULL, NULL,
                           dss_write_data,        dss_write_ctrl, dss_read_status};
