package i6502

const (
	aciaData = iota
	aciaStatus
	aciaCommand
	aciaControl
)

/*
ACIA 6551 Serial IO

This Asynchronous Communications Interface Adapater can be
directly attached to the 6502's address and data busses.

It provides serial IO.

The supplied Rx and Tx channels can be used to read and wirte
data to the ACIA 6551.
*/
type Acia6551 struct {
	rx byte
	tx byte

	commandData byte
	controlData byte

	rxFull  bool
	txEmpty bool

	rxIrqEnabled bool
	txIrqEnabled bool

	overrun bool

	output chan []byte
}

func NewAcia6551(output chan []byte) (*Acia6551, error) {
	acia := &Acia6551{output: output}
	acia.Reset()

	return acia, nil
}

func (a *Acia6551) Size() uint16 {
	// We have a only 4 addresses, Data, Status, Command and Control
	return 0x04
}

// Emulates a hardware reset
func (a *Acia6551) Reset() {
	a.rx = 0
	a.rxFull = false

	a.tx = 0
	a.txEmpty = true

	a.rxIrqEnabled = false
	a.txIrqEnabled = false

	a.overrun = false

	a.setControl(0)
	a.setCommand(0)
}

func (a *Acia6551) setControl(data byte) {
	a.controlData = data
}

func (a *Acia6551) setCommand(data byte) {
	a.commandData = data

	a.rxIrqEnabled = (data & 0x02) != 0
	a.txIrqEnabled = ((data & 0x04) != 0) && ((data & 0x08) != 1)
}

func (a *Acia6551) statusRegister() byte {
	status := byte(0)

	if a.rxFull {
		status |= 0x08
	}

	if a.txEmpty {
		status |= 0x10
	}

	if a.overrun {
		status |= 0x04
	}

	return status
}

// Implements io.Reader, for external programs to read TX'ed data from
// the serial output.
func (a *Acia6551) Read(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}

	if a.txEmpty {
		return 0, nil
	} else {
		a.txEmpty = true
		copy(p, []byte{a.tx})
		// TODO: Handle txInterrupt
		return 1, nil
	}
}

// Implements io.Writer, for external programs to write to the
// ACIA's RX
func (a *Acia6551) Write(p []byte) (n int, err error) {
	for _, b := range p {
		a.rxWrite(b)
	}

	return len(p), nil
}

// Used by the AddressBus to read data from the ACIA 6551
func (a *Acia6551) ReadByte(address uint16) byte {
	switch address {
	case aciaData:
		return a.rxRead()
	case aciaStatus:
		return a.statusRegister()
	case aciaCommand:
		return a.commandData
	case aciaControl:
		return a.controlData
	}

	return 0x00
}

// Used by the AddressBus to write data to the ACIA 6551
func (a *Acia6551) WriteByte(address uint16, data byte) {
	switch address {
	case aciaData:
		a.txWrite(data)
	case aciaStatus:
		a.Reset()
	case aciaCommand:
		a.setCommand(data)
	case aciaControl:
		a.setControl(data)
	}
}

func (a *Acia6551) rxRead() byte {
	a.overrun = false
	a.rxFull = false
	return a.rx
}

func (a *Acia6551) rxWrite(data byte) {
	// Oh no, overrun. Set the appropriate status
	if a.rxFull {
		a.overrun = true
	}

	a.rx = data
	a.rxFull = true

	// TODO: Interrupts
}

func (a *Acia6551) txWrite(data byte) {
	a.output <- []byte{data}
	a.tx = data
	// a.txEmpty = false
}
