#include "Constants.h"
#include "Io.h"
#include "Keyboard.h"
#include "Kempston.h"
#include "DataBus.h"
#include "ULABus.h"
#include "Sound.h"

static Uint8 _portFE;

static Uint8 _ioPortAccessCaptured = 0;
static Uint8 _ioIsInAccessCaptureContext = 0;
static Uint16 _ioCapturedPortAccessAddress;

void io_start() {
	io_destroy();
}

void io_destroy() {
	_portFE = 0;
	_ioPortAccessCaptured = 0;
	_ioIsInAccessCaptureContext = 0;
}

inline void _io_try_capture_port_access(Uint16 address) {
	if (_ioIsInAccessCaptureContext) {
		_ioPortAccessCaptured = 1;
		_ioCapturedPortAccessAddress = address;
	}
}

Uint8 io_read8_16bitaddr(Uint16 port) {
	Uint8 value = 0;
	// joystick takes precedence over keyboard
	if (kempston_is_supported_address(port)) {
		value = kempston_read8(port);
		data_bus_write8(value);
		_io_try_capture_port_access(port);
		return value;
	}
	else if (keyboard_is_supported_address(port)) {
		value = keyboard_read8(port);
		data_bus_write8(value);
		// NOTE:
		//     lack of return intended
		//
	}
	else if (ula_bus_is_supported_address(port)) {
		value = ula_bus_read8(port);
		_io_try_capture_port_access(port);
		return value;
	}

	Uint8 ear;
	switch (port & 0xFF)
	{
	case ULA_PORT_FE:
		// bits 5 and 7 are always 1
		value |= 0xA0;

		// bit 6 := ear
		ear = (_portFE & 0x10) << 2;	// ear is written to bit 4 and is read from bit 6
		value &= 0xBF;
		value |= ear;

		_io_try_capture_port_access(port);

		return value;
	default:
		return 0xFF;
	}
}

// a worker specifically for writing to port FE
void _io_perform_port_FE_write(Uint8 value) {
	_portFE = value;
}

void io_write8_16bitaddr(Uint16 port, Uint8 value) {
	data_bus_write8(value);

	switch (port & 0x00FF) {
	case ULA_PORT_FE:
		_io_perform_port_FE_write(value);
	default:
		break;
	}

	_io_try_capture_port_access(port);
}

void io_ear_write1_uncontended(Uint8 value) {
	// used by tape and not intended to capture port access for contention
	if (value) {
		_io_perform_port_FE_write(_portFE | 0x10);	// set bit 4
	}
	else {
		_io_perform_port_FE_write(_portFE & 0xEF);	// clear bit 4
	}
}

void io_enter_access_capture_context() {
	_ioPortAccessCaptured = 0;
	_ioCapturedPortAccessAddress = 0;
	_ioIsInAccessCaptureContext = 1;
}

void io_leave_access_capture_context() {
	_ioIsInAccessCaptureContext = 0;
}

Uint8 io_peek_access_capture_context(Uint16* address) {
	if (!_ioIsInAccessCaptureContext) {
		return 0;
	}

	_ioIsInAccessCaptureContext = 0;

	*address = _ioCapturedPortAccessAddress;
	return _ioPortAccessCaptured;
}

Uint8 io_read8_border_colour() {
	return _portFE & 7;		// bits 0, 1, 2
}

void io_set8_border_colour_uncontended(Uint8 value) {
	_portFE &= 0b11111000;		// bits 0, 1, 2
	_portFE |= value & 7;
}

Uint8 io_is_speaker_active() {
	return (_portFE & 0x10) >> 4;	// bit 4
}
