#include <pcem/defines.h>
#include <pcem/devices.h>
#include <pcem/logging.h>
#include <string.h>
#include <pcem/config.h>

extern void *device_priv[256];
extern device_t *devices[DEV_MAX];
extern device_t *current_device;
extern char *current_device_name;

extern struct device_t *model_getdevice(int model);

int model;
device_t *devices[DEV_MAX];
MODEL *models[ROM_MAX];
VIDEO_CARD *video_cards[GFX_MAX];
SOUND_CARD *sound_cards[SOUND_MAX];
HDD_CONTROLLER *hdd_controllers[HDDCONTROLLERS_MAX];
NETWORK_CARD *network_cards[NETWORK_CARD_MAX];
LPT_DEVICE *lpt_devices[LPT_MAX];
device_t *current_device;
char *current_device_name = NULL;
void *device_priv[DEV_MAX];

void (*_sound_speed_changed)();

device_t *model_getdevice(int model) { return models[model]->device; }

void device_init() { memset(devices, 0, sizeof(devices)); }

void device_add(device_t *d) { pcem_add_device(d); }

void device_close_all() {
        int c;

        for (c = 0; c < 256; c++) {
                if (devices[c] != NULL) {
                        if (devices[c]->close != NULL)
                                devices[c]->close(device_priv[c]);
                        devices[c] = device_priv[c] = NULL;
                }
        }
}

int device_available(device_t *d) {
#ifdef RELEASE_BUILD
        if (d->flags & DEVICE_NOT_WORKING)
                return 0;
#endif
        if (d->available)
                return d->available();

        return 1;
}

void device_speed_changed() {
        int c;

        for (c = 0; c < 256; c++) {
                if (devices[c] != NULL) {
                        if (devices[c]->speed_changed != NULL) {
                                devices[c]->speed_changed(device_priv[c]);
                        }
                }
        }

        _sound_speed_changed();
}

void device_force_redraw() {
        int c;

        for (c = 0; c < 256; c++) {
                if (devices[c] != NULL) {
                        if (devices[c]->force_redraw != NULL) {
                                devices[c]->force_redraw(device_priv[c]);
                        }
                }
        }
}

void device_add_status_info(char *s, int max_len) {
        int c;

        for (c = 0; c < 256; c++) {
                if (devices[c] != NULL) {
                        if (devices[c]->add_status_info != NULL)
                                devices[c]->add_status_info(s, max_len, device_priv[c]);
                }
        }
}

int device_get_config_int(char *s) {
        device_config_t *config = current_device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_int(CFG_MACHINE, current_device->name, s, config->default_int);

                config++;
        }
        return 0;
}

char *device_get_config_string(char *s) {
        device_config_t *config = current_device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_string(CFG_MACHINE, current_device->name, s, config->default_string);

                config++;
        }
        return NULL;
}

int model_get_config_int(char *s) {
        device_t *device = model_getdevice(model);
        device_config_t *config;

        if (!device)
                return 0;

        config = device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_int(CFG_MACHINE, device->name, s, config->default_int);

                config++;
        }
        return 0;
}

char *model_get_config_string(char *s) {
        device_t *device = model_getdevice(model);
        device_config_t *config;

        if (!device)
                return 0;

        config = device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_string(CFG_MACHINE, device->name, s, config->default_string);

                config++;
        }
        return NULL;
}

int pcem_device_get_config_int(device_t *device, char *s) {
        device_config_t *config = device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_int(CFG_MACHINE, device->name, s, config->default_int);

                config++;
        }
        return 0;
}

char *pcem_device_get_config_string(device_t *device, char *s) {
        device_config_t *config = device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_string(CFG_MACHINE, device->name, s, config->default_string);

                config++;
        }
        return NULL;
}

int pcem_model_get_config_int(device_t *device, char *s) {
        device_config_t *config;

        if (!device)
                return 0;

        config = device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_int(CFG_MACHINE, device->name, s, config->default_int);

                config++;
        }
        return 0;
}

char *pcem_model_get_config_string(device_t *device, char *s) {
        device_config_t *config;

        if (!device)
                return 0;

        config = device->config;

        while (config->type != -1) {
                if (!strcmp(s, config->name))
                        return config_get_string(CFG_MACHINE, device->name, s, config->default_string);

                config++;
        }
        return NULL;
}

int model_count() {
        int ret = 0;

        while (models[ret] != NULL && ret < ROM_MAX)
                ret++;

        return ret;
}

void pcem_add_model(MODEL *model) {
        // TODO: Add sanity check to not go past MAX amount
        models[model_count()] = model;
}

int video_count() {
        int ret = 0;

        while (video_cards[ret] != NULL && ret < GFX_MAX)
                ret++;

        return ret;
}

void pcem_add_video(VIDEO_CARD *video) {
        // TODO: Add sanity check to not go past MAX amount
        video_cards[video_count()] = video;
}

int lpt_count() {
        int ret = 0;

        while (lpt_devices[ret] != NULL && ret < LPT_MAX)
                ret++;

        return ret;
}

void pcem_add_lpt(LPT_DEVICE *lpt) {
        // TODO: Add sanity check to not go past MAX amount
        lpt_devices[lpt_count()] = lpt;
}

int sound_count() {
        int ret = 0;

        while (sound_cards[ret] != NULL && ret < SOUND_MAX)
                ret++;

        return ret;
}

void pcem_add_sound(SOUND_CARD *sound) {
        // TODO: Add sanity check to not go past MAX amount
        sound_cards[sound_count()] = sound;
}

int hdd_controller_count() {
        int ret = 0;

        while (hdd_controllers[ret] != NULL && ret < HDDCONTROLLERS_MAX)
                ret++;

        return ret;
}

void pcem_add_hddcontroller(HDD_CONTROLLER *hddcontroller) {
        // TODO: Add sanity check to not go past MAX amount
        hdd_controllers[hdd_controller_count()] = hddcontroller;
}

int network_card_count() {
        int ret = 0;

        while (network_cards[ret] != NULL && ret < NETWORK_CARD_MAX)
                ret++;

        return ret;
}

void pcem_add_networkcard(NETWORK_CARD *netcard) {
        // TODO: Add sanity check to not go past MAX amount
        network_cards[network_card_count()] = netcard;
}

void pcem_add_device(device_t *d) {
        int c = 0;
        void *priv = NULL;

        while (devices[c] != NULL && c < 256)
                c++;

        if (c >= 256)
                fatal("device_add : too many devices\n");

        current_device = d;
        current_device_name = d->name;

        if (d->init != NULL) {
                priv = d->init();
                if (priv == NULL)
                        fatal("device_add : device init failed\n");
        }

        devices[c] = d;
        device_priv[c] = priv;
        current_device_name = NULL;
}
