#include <stdlib.h>
#include "ibm.h"
#include "io.h"
#include "keyboard.h"
#include "lpt.h"
#include "mouse.h"
#include "video.h"
#include "fdc.h"
#include "x86.h"

#include "amstrad.h"

static uint8_t amstrad_dead;
int amstrad_latch = 0;
static uint8_t amstrad_language;

uint8_t amstrad_read(uint16_t port, void *priv)
{
        uint8_t temp;
        
        pclog("amstrad_read : %04X\n",port);
        switch (port)
        {
                case 0x378:
                return lpt1_read(port, NULL);
                case 0x379:
                return lpt1_read(port, NULL) | (amstrad_language & 7);
                case 0x37a:
                temp = lpt1_read(port, NULL) & 0x1f;
                if (romset == ROM_PC1512) return temp | 0x20;
                if (romset == ROM_PC200 || romset == ROM_PPC512)
                {
                        if (fdc_discchange_read())
                                temp |= 0x20;
/* DIP switches 4,5: Initial video mode */
                        if (video_is_cga())
                                temp |= 0x80;	/* CGA 80 */
                        else if (video_is_mda())
                                temp |= 0xc0;	/* MDA */
                        return temp;
                }
                if (romset == ROM_PC1640)
                {
                        if (video_is_cga())
                                temp |= 0x80;                        
                        else if (video_is_mda())
                                temp |= 0xc0;

                        switch (amstrad_latch)
                        {
                                case AMSTRAD_NOLATCH:
                                temp &= ~0x20;
                                break;
                                case AMSTRAD_SW9:
                                temp &= ~0x20;
                                break;
                                case AMSTRAD_SW10:
                                temp |= 0x20;
                                break;
                        }
                }
                return temp;
                case 0x3de:
                if (romset == ROM_PC200 || romset == ROM_PPC512)
		{
/* Read DIP switches / internal display adapter status. This code is only
 * reached if the IDA is disabled; if it is enabled this read will be
 * handled by the video card */
		}
                return 0x20; /* Disable IDA */
                case 0xdead:
                return amstrad_dead;
        }
        return 0xff;
}

void amstrad_write(uint16_t port, uint8_t val, void *priv)
{
        switch (port)
        {
                case 0x66:
                softresetx86();
                break;
                
                case 0x378:
                case 0x379:
                case 0x37a:
                lpt1_write(port, val, NULL);
                break;
                
                case 0xdead:
                amstrad_dead = val;
                break;
        }
}

static uint8_t mousex, mousey;
static void amstrad_mouse_write(uint16_t addr, uint8_t val, void *p)
{
//        pclog("Write mouse %04X %02X %04X:%04X\n", addr, val, CS, pc);
        if (addr == 0x78)
                mousex = 0;
        else
                mousey = 0;
}

static uint8_t amstrad_mouse_read(uint16_t addr, void *p)
{
//        printf("Read mouse %04X %04X:%04X %02X\n", addr, CS, pc, (addr == 0x78) ? mousex : mousey);
        if (addr == 0x78)
                return mousex;
        return mousey;
}

typedef struct mouse_amstrad_t
{
        int oldb;
} mouse_amstrad_t;

static void mouse_amstrad_poll(int x, int y, int z, int b, void *p)
{
        mouse_amstrad_t *mouse = (mouse_amstrad_t *)p;
        
        mousex += x;
        mousey -= y;

        if ((b & 1) && !(mouse->oldb & 1))
                keyboard_send(0x7e);
        if ((b & 2) && !(mouse->oldb & 2))
                keyboard_send(0x7d);
        if (!(b & 1) && (mouse->oldb & 1))
                keyboard_send(0xfe);
        if (!(b & 2) && (mouse->oldb & 2))
                keyboard_send(0xfd);
        
        mouse->oldb = b;
}

static void *mouse_amstrad_init()
{
        mouse_amstrad_t *mouse = (mouse_amstrad_t *)malloc(sizeof(mouse_amstrad_t));
        memset(mouse, 0, sizeof(mouse_amstrad_t));
                
        return mouse;
}

static void mouse_amstrad_close(void *p)
{
        mouse_amstrad_t *mouse = (mouse_amstrad_t *)p;
        
        free(mouse);
}

mouse_t mouse_amstrad =
{
        "Amstrad mouse",
        mouse_amstrad_init,
        mouse_amstrad_close,
        mouse_amstrad_poll,
        MOUSE_TYPE_AMSTRAD
};

void amstrad_init()
{
        lpt2_remove_ams();
        
        io_sethandler(0x0078, 0x0001, amstrad_mouse_read, NULL, NULL, amstrad_mouse_write, NULL, NULL,  NULL);
        io_sethandler(0x007a, 0x0001, amstrad_mouse_read, NULL, NULL, amstrad_mouse_write, NULL, NULL,  NULL);
        io_sethandler(0x0066, 0x0001, NULL,               NULL, NULL, amstrad_write,       NULL, NULL,  NULL);
        io_sethandler(0x0378, 0x0003, amstrad_read,       NULL, NULL, amstrad_write,       NULL, NULL,  NULL);
        io_sethandler(0xdead, 0x0001, amstrad_read,       NULL, NULL, amstrad_write,       NULL, NULL,  NULL);
        if ((romset == ROM_PC200 || romset == ROM_PPC512) && gfxcard != GFX_BUILTIN)        
                io_sethandler(0x03de, 0x0001, amstrad_read,       NULL, NULL, amstrad_write,       NULL, NULL,  NULL);

	
}

static void *ams1512_init()
{
	amstrad_language = device_get_config_int("language");
	return &amstrad_language;
}

device_config_t ams1512_config[] = 
{
	{
                .name = "language",
                .description = "BIOS language",
                .type = CONFIG_SELECTION,
		.selection =
		{
			{
				.description = "English",
				.value = 7
			},
			{
				.description = "German",
				.value = 6
			},
			{
				.description = "French",
				.value = 5
			},
			{
				.description = "Spanish",
				.value = 4
			},
			{
				.description = "Danish",
				.value = 3
			},
			{
				.description = "Swedish",
				.value = 2
			},
			{
				.description = "Italian",
				.value = 1
			},
			{
				.description = "Diagnostic mode",
				.value = 0
			},
			{
				.description = ""
			}
		},
                .default_int = 7
	},
        {
                .type = -1
        }
};
	

device_config_t ams2086_config[] = 
{
	{
                .name = "language",
                .description = "BIOS language",
                .type = CONFIG_SELECTION,
		.selection =
		{
			{
				.description = "English",
				.value = 7
			},
			{
				.description = "Diagnostic mode",
				.value = 0
			},
			{
				.description = ""
			}
		},
                .default_int = 7
	},
        {
                .type = -1
        }
};
		


device_config_t ams3086_config[] = 
{
	{
                .name = "language",
                .description = "BIOS language",
                .type = CONFIG_SELECTION,
		.selection =
		{
			{
				.description = "English",
				.value = 7
			},
			{
				.description = "Diagnostic mode",
				.value = 3
			},
			{
				.description = ""
			}
		},
                .default_int = 7
	},
        {
                .type = -1
        }
};
	

	

device_t ams1512_device =
{
        "Amstrad PC1512 (BIOS)",
        0,
        ams1512_init,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        ams1512_config
};



device_t ams2086_device =
{
        "Amstrad PC2086 (BIOS)",
        0,
        ams1512_init,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        ams2086_config
};


device_t ams3086_device =
{
        "Amstrad PC3086 (BIOS)",
        0,
        ams1512_init,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        ams3086_config
};
