// Package mcp2515 implements a driver for the MCP2515 CAN Controller.
//
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf
//
// Reference: https://github.com/coryjfowler/MCP_CAN_lib
package mcp2515 // import "tinygo.org/x/drivers/mcp2515"

import (
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"tinygo.org/x/drivers"
	"tinygo.org/x/drivers/internal/legacy"
	"tinygo.org/x/drivers/internal/pin"
)

var (
	ErrNothingIsReceived          = errors.New("readMsg: nothing is received")
	ErrRequestNewModeMaxTimeEx    = errors.New("requestNewMode max time expired")
	ErrLengthIsLongerThanCapacity = errors.New("length is longer than capacity")
	ErrTxTimeout                  = errors.New("Tx: Tx timeout")
	ErrInvalidDirection           = errors.New("invalid direction")
	ErrInvalidParameter           = errors.New("invalid parameter")
	ErrCannotExpandBuffer         = errors.New("cannot expand buffer (to avoid memory allocation)")
)

// Device wraps MCP2515 SPI CAN Module.
type Device struct {
	spi           SPI
	cs            pin.OutputFunc
	msg           *CANMsg
	extended      bool
	mcpMode       byte
	configurePins func()
}

type Configuration struct {
	Extended bool
}

// CANMsg stores CAN message fields.
type CANMsg struct {
	ID   uint32
	Dlc  uint8
	Data []byte
	Ext  bool
	Rtr  bool
}

const (
	bufferSize int = 64
)

// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
func New(b drivers.SPI, csPin pin.Output) *Device {
	d := &Device{
		spi: SPI{
			bus: b,
			tx:  make([]byte, 0, bufferSize),
			rx:  make([]byte, 0, bufferSize),
		},
		cs:  csPin.Set,
		msg: &CANMsg{},
		configurePins: func() {
			legacy.ConfigurePinOut(csPin)
		},
	}

	return d
}

// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) {
	if d.configurePins == nil {
		panic(legacy.ErrConfigBeforeInstantiated)
	}
	d.extended = cfg.Extended
	d.configurePins()
}

const beginTimeoutValue int = 10

// Begin starts the CAN controller.
func (d *Device) Begin(speed byte, clock byte) error {
	timeOutCount := 0
	for {
		err := d.init(speed, clock)
		if err == nil {
			break
		}
		timeOutCount++
		if timeOutCount >= beginTimeoutValue {
			return err
		}
	}
	return nil
}

// Received returns true if CAN message is received.
func (d *Device) Received() bool {
	res, err := d.readStatus()
	if err != nil {
		panic(err)
	}
	// if RX STATUS INSTRUCTION  result is not 0x00 (= No RX message)
	// TODO: reconsider this logic
	return (res & mcpStatRxifMask) != 0x00
}

// Rx returns received CAN message.
func (d *Device) Rx() (*CANMsg, error) {
	err := d.readMsg()
	return d.msg, err
}

// Tx transmits CAN Message.
func (d *Device) Tx(canid uint32, dlc uint8, data []byte) error {
	// TODO: add ext, rtrBit, waitSent
	timeoutCount := 0

	var bufNum, res uint8
	var err error
	res = mcpAlltxbusy
	for res == mcpAlltxbusy && (timeoutCount < timeoutvalue) {
		if timeoutCount > 0 {
			time.Sleep(time.Microsecond * 10)
		}
		bufNum, res, err = d.getNextFreeTxBuf()
		if err != nil {
			return err
		}
		timeoutCount++
	}
	if timeoutCount == timeoutvalue {
		return ErrTxTimeout
	}
	ext := byte(0)
	if d.extended {
		ext = 1
	}
	err = d.writeCANMsg(bufNum, canid, ext, 0, dlc, data)
	if err != nil {
		return err
	}

	return nil
}

func (d *Device) init(speed, clock byte) error {
	err := d.Reset()
	if err != nil {
		return err
	}

	if err := d.setCANCTRLMode(modeConfig); err != nil {
		return fmt.Errorf("setCANCTRLMode %s: ", err)
	}
	time.Sleep(time.Millisecond * 10)

	// set baudrate
	if err := d.configRate(speed, clock); err != nil {
		return fmt.Errorf("configRate %s: ", err)
	}
	time.Sleep(time.Millisecond * 10)

	if err := d.initCANBuffers(); err != nil {
		return fmt.Errorf("initCANBuffers: %s ", err)
	}
	if err := d.setRegister(mcpCANINTE, mcpRX0IF|mcpRX1IF); err != nil {
		return fmt.Errorf("setRegister: %s ", err)
	}
	if err := d.modifyRegister(mcpRXB0CTRL, mcpRxbRxMask|mcpRxbBuktMask, mcpRxbRxStdExt|mcpRxbBuktMask); err != nil {
		return fmt.Errorf("modifyRegister: %s ", err)
	}
	if err := d.modifyRegister(mcpRXB1CTRL, mcpRxbRxMask, mcpRxbRxStdExt); err != nil {
		return fmt.Errorf("modifyRegister: %s ", err)
	}

	if err := d.setMode(modeNormal); err != nil {
		return fmt.Errorf("setMode %s: ", err)
	}
	time.Sleep(time.Millisecond * 10)

	return nil
}

// Reset resets mcp2515.
func (d *Device) Reset() error {
	d.cs.Low()
	_, err := d.spi.readWrite(mcpReset)
	d.cs.High()
	// time.Sleep(time.Microsecond * 4)
	if err != nil {
		return err
	}

	time.Sleep(time.Millisecond * 10)

	return nil
}

func (d *Device) setCANCTRLMode(newMode byte) error {
	// If the chip is asleep and we want to change mode then a manual wake needs to be done
	// This is done by setting the wake up interrupt flag
	// This undocumented trick was found at https://github.com/mkleemann/can/blob/master/can_sleep_mcp2515.c
	m, err := d.getMode()
	if err != nil {
		return err
	}
	if m == modeSleep && newMode != modeSleep {
		r, err := d.readRegister(mcpCANINTE)
		if err != nil {
			return err
		}
		wakeIntEnabled := (r & mcpWAKIF) == 0x00
		if !wakeIntEnabled {
			d.modifyRegister(mcpCANINTE, mcpWAKIF, mcpWAKIF)
		}
		// Set wake flag (this does the actual waking up)
		d.modifyRegister(mcpCANINTF, mcpWAKIF, mcpWAKIF)

		// Wait for the chip to exit SLEEP and enter LISTENONLY mode.

		// If the chip is not connected to a CAN bus (or the bus has no other powered nodes) it will sometimes trigger the wake interrupt as soon
		// as it's put to sleep, but it will stay in SLEEP mode instead of automatically switching to LISTENONLY mode.
		// In this situation the mode needs to be manually set to LISTENONLY.

		if err := d.requestNewMode(modeListenOnly); err != nil {
			return err
		}

		// Turn wake interrupt back off if it was originally off
		if !wakeIntEnabled {
			d.modifyRegister(mcpCANINTE, mcpWAKIF, 0)
		}
	}

	// Clear wake flag
	d.modifyRegister(mcpCANINTF, mcpWAKIF, 0)

	return d.requestNewMode(newMode)
}

func (d *Device) setMode(opMode byte) error {
	if opMode != modeSleep {
		d.mcpMode = opMode
	}

	err := d.setCANCTRLMode(opMode)
	if err != nil {
		return err
	}

	return nil
}

func (d *Device) getMode() (byte, error) {
	r, err := d.readRegister(mcpCANSTAT)
	if err != nil {
		return 0, err
	}
	return r & modeMask, nil
}

func (d *Device) configRate(speed, clock byte) error {
	var cfg1, cfg2, cfg3 byte
	set := true
	switch clock {
	case Clock16MHz:
		switch speed {
		case CAN5kBps:
			cfg1 = mcp16mHz5kBpsCfg1
			cfg2 = mcp16mHz5kBpsCfg2
			cfg3 = mcp16mHz5kBpsCfg3
		case CAN10kBps:
			cfg1 = mcp16mHz10kBpsCfg1
			cfg2 = mcp16mHz10kBpsCfg2
			cfg3 = mcp16mHz10kBpsCfg3
		case CAN20kBps:
			cfg1 = mcp16mHz20kBpsCfg1
			cfg2 = mcp16mHz20kBpsCfg2
			cfg3 = mcp16mHz20kBpsCfg3
		case CAN25kBps:
			cfg1 = mcp16mHz25kBpsCfg1
			cfg2 = mcp16mHz25kBpsCfg2
			cfg3 = mcp16mHz25kBpsCfg3
		case CAN31k25Bps:
			cfg1 = mcp16mHz31k25BpsCfg1
			cfg2 = mcp16mHz31k25BpsCfg2
			cfg3 = mcp16mHz31k25BpsCfg3
		case CAN33kBps:
			cfg1 = mcp16mHz33kBpsCfg1
			cfg2 = mcp16mHz33kBpsCfg2
			cfg3 = mcp16mHz33kBpsCfg3
		case CAN40kBps:
			cfg1 = mcp16mHz40kBpsCfg1
			cfg2 = mcp16mHz40kBpsCfg2
			cfg3 = mcp16mHz40kBpsCfg3
		case CAN47kBps:
			cfg1 = mcp16mHz47kBpsCfg1
			cfg2 = mcp16mHz47kBpsCfg2
			cfg3 = mcp16mHz47kBpsCfg3
		case CAN50kBps:
			cfg1 = mcp16mHz50kBpsCfg1
			cfg2 = mcp16mHz50kBpsCfg2
			cfg3 = mcp16mHz50kBpsCfg3
		case CAN80kBps:
			cfg1 = mcp16mHz80kBpsCfg1
			cfg2 = mcp16mHz80kBpsCfg2
			cfg3 = mcp16mHz80kBpsCfg3
		case CAN83k3Bps:
			cfg1 = mcp16mHz83k3BpsCfg1
			cfg2 = mcp16mHz83k3BpsCfg2
			cfg3 = mcp16mHz83k3BpsCfg3
		case CAN95kBps:
			cfg1 = mcp16mHz95kBpsCfg1
			cfg2 = mcp16mHz95kBpsCfg2
			cfg3 = mcp16mHz95kBpsCfg3
		case CAN100kBps:
			cfg1 = mcp16mHz100kBpsCfg1
			cfg2 = mcp16mHz100kBpsCfg2
			cfg3 = mcp16mHz100kBpsCfg3
		case CAN125kBps:
			cfg1 = mcp16mHz125kBpsCfg1
			cfg2 = mcp16mHz125kBpsCfg2
			cfg3 = mcp16mHz125kBpsCfg3
		case CAN200kBps:
			cfg1 = mcp16mHz200kBpsCfg1
			cfg2 = mcp16mHz200kBpsCfg2
			cfg3 = mcp16mHz200kBpsCfg3
		case CAN250kBps:
			cfg1 = mcp16mHz250kBpsCfg1
			cfg2 = mcp16mHz250kBpsCfg2
			cfg3 = mcp16mHz250kBpsCfg3
		case CAN500kBps:
			cfg1 = mcp16mHz500kBpsCfg1
			cfg2 = mcp16mHz500kBpsCfg2
			cfg3 = mcp16mHz500kBpsCfg3
		case CAN666kBps:
			cfg1 = mcp16mHz666kBpsCfg1
			cfg2 = mcp16mHz666kBpsCfg2
			cfg3 = mcp16mHz666kBpsCfg3
		case CAN1000kBps:
			cfg1 = mcp16mHz1000kBpsCfg1
			cfg2 = mcp16mHz1000kBpsCfg2
			cfg3 = mcp16mHz1000kBpsCfg3
		default:
			set = false
		}
	case Clock8MHz:
		switch speed {
		case CAN5kBps:
			cfg1 = mcp8mHz5kBpsCfg1
			cfg2 = mcp8mHz5kBpsCfg2
			cfg3 = mcp8mHz5kBpsCfg3
		case CAN10kBps:
			cfg1 = mcp8mHz10kBpsCfg1
			cfg2 = mcp8mHz10kBpsCfg2
			cfg3 = mcp8mHz10kBpsCfg3
		case CAN20kBps:
			cfg1 = mcp8mHz20kBpsCfg1
			cfg2 = mcp8mHz20kBpsCfg2
			cfg3 = mcp8mHz20kBpsCfg3
		case CAN31k25Bps:
			cfg1 = mcp8mHz31k25BpsCfg1
			cfg2 = mcp8mHz31k25BpsCfg2
			cfg3 = mcp8mHz31k25BpsCfg3
		case CAN40kBps:
			cfg1 = mcp8mHz40kBpsCfg1
			cfg2 = mcp8mHz40kBpsCfg2
			cfg3 = mcp8mHz40kBpsCfg3
		case CAN50kBps:
			cfg1 = mcp8mHz50kBpsCfg1
			cfg2 = mcp8mHz50kBpsCfg2
			cfg3 = mcp8mHz50kBpsCfg3
		case CAN80kBps:
			cfg1 = mcp8mHz80kBpsCfg1
			cfg2 = mcp8mHz80kBpsCfg2
			cfg3 = mcp8mHz80kBpsCfg3
		case CAN100kBps:
			cfg1 = mcp8mHz100kBpsCfg1
			cfg2 = mcp8mHz100kBpsCfg2
			cfg3 = mcp8mHz100kBpsCfg3
		case CAN125kBps:
			cfg1 = mcp8mHz125kBpsCfg1
			cfg2 = mcp8mHz125kBpsCfg2
			cfg3 = mcp8mHz125kBpsCfg3
		case CAN200kBps:
			cfg1 = mcp8mHz200kBpsCfg1
			cfg2 = mcp8mHz200kBpsCfg2
			cfg3 = mcp8mHz200kBpsCfg3
		case CAN250kBps:
			cfg1 = mcp8mHz250kBpsCfg1
			cfg2 = mcp8mHz250kBpsCfg2
			cfg3 = mcp8mHz250kBpsCfg3
		case CAN500kBps:
			cfg1 = mcp8mHz500kBpsCfg1
			cfg2 = mcp8mHz500kBpsCfg2
			cfg3 = mcp8mHz500kBpsCfg3
		case CAN1000kBps:
			cfg1 = mcp8mHz1000kBpsCfg1
			cfg2 = mcp8mHz1000kBpsCfg2
			cfg3 = mcp8mHz1000kBpsCfg3
		default:
			set = false
		}
	default:
		set = false
	}
	if !set {
		return ErrInvalidParameter
	}
	if err := d.setRegister(mcpCNF1, cfg1); err != nil {
		return err
	}
	if err := d.setRegister(mcpCNF2, cfg2); err != nil {
		return err
	}
	if err := d.setRegister(mcpCNF3, cfg3); err != nil {
		return err
	}

	return nil
}

func (d *Device) initCANBuffers() error {
	a1 := byte(mcpTXB0CTRL)
	a2 := byte(mcpTXB1CTRL)
	a3 := byte(mcpTXB2CTRL)
	for i := 0; i < 14; i++ {
		if err := d.setRegister(a1, 0); err != nil {
			return err
		}
		if err := d.setRegister(a2, 0); err != nil {
			return err
		}
		if err := d.setRegister(a3, 0); err != nil {
			return err
		}
		a1++
		a2++
		a3++
	}

	if err := d.setRegister(mcpRXB0CTRL, 0); err != nil {
		return err
	}
	if err := d.setRegister(mcpRXB1CTRL, 0); err != nil {
		return err
	}

	return nil
}

func (d *Device) readMsg() error {
	status, err := d.readRxTxStatus()
	if err != nil {
		return err
	}
	if (status & mcpRX0IF) == 0x01 {
		err := d.readRxBuffer(mcpReadRx0)
		if err != nil {
			return err
		}
	} else if (status & mcpRX1IF) == 0x02 {
		err := d.readRxBuffer(mcpReadRx1)
		if err != nil {
			return err
		}
	} else {
		return ErrNothingIsReceived
	}

	return nil
}

func (d *Device) readRxBuffer(loadAddr uint8) error {
	msg := d.msg
	d.cs.Low()
	defer d.cs.High()
	_, err := d.spi.readWrite(loadAddr)
	if err != nil {
		return err
	}
	err = d.spi.read(4)
	if err != nil {
		return err
	}
	buf := d.spi.rx
	msg.ID = uint32((uint32(buf[0]) << 3) + (uint32(buf[1]) >> 5))
	msg.Ext = false
	if (buf[1] & mcpTxbExideM) == mcpTxbExideM {
		// extended id
		msg.ID = uint32(uint32(msg.ID<<2) + uint32(buf[1]&0x03))
		msg.ID = uint32(uint32(msg.ID<<8) + uint32(buf[2]))
		msg.ID = uint32(uint32(msg.ID<<8) + uint32(buf[3]))
		msg.Ext = true
	}
	err = d.spi.read(1)
	if err != nil {
		return err
	}
	msgSize := d.spi.rx[0]
	msg.Dlc = uint8(msgSize & mcpDlcMask)
	msg.Rtr = false
	if (msgSize & mcpRtrMask) == 0x40 {
		msg.Rtr = true
	}
	readLen := uint8(canMaxCharInMessage)
	if msg.Dlc < canMaxCharInMessage {
		readLen = msg.Dlc
	}
	err = d.spi.read(int(readLen))
	if err != nil {
		return err
	}
	msg.Data = d.spi.rx

	return err
}

func (d *Device) getNextFreeTxBuf() (uint8, uint8, error) {
	status, err := d.readStatus()
	if err != nil {
		return 0, mcpAlltxbusy, err
	}
	status &= mcpStatTxPendingMask

	bufNum := uint8(0x00)

	if status == mcpStatTxPendingMask {
		return 0, mcpAlltxbusy, nil
	}

	for i := 0; i < int(mcpNTxbuffers-nReservedTx(0)); i++ {
		if (status & txStatusPendingFlag(uint8(i))) == 0 {
			bufNum = txCtrlReg(uint8(i)) + 1
			d.modifyRegister(mcpCANINTF, txIfFlag(uint8(i)), 0)
			return bufNum, mcp2515Ok, nil
		}
	}

	return 0, mcpAlltxbusy, nil
}

func (d *Device) writeCANMsg(bufNum uint8, canid uint32, ext, rtrBit, dlc uint8, data []byte) error {
	d.cs.Low()
	defer d.cs.High()
	_, err := d.spi.readWrite(txSidhToLoad(bufNum))
	if err != nil {
		return err
	}
	err = d.spi.clearBuffer(tx)
	if err != nil {
		return err
	}
	err = d.spi.setTxBufData(canid, ext, rtrBit, dlc, data)
	if err != nil {
		return err
	}
	err = d.spi.write()
	if err != nil {
		return err
	}
	// Since cs.Low and cs.High are executed in d.startTransmission,
	// it is necessary to set cs.High once to separate the instruction of mcp2515.
	d.cs.High()

	err = d.startTransmission(bufNum)
	if err != nil {
		return err
	}

	return nil
}

func (s *SPI) setTxBufData(canid uint32, ext, rtrBit, dlc uint8, data []byte) error {
	var id [4]byte
	if ext == 1 {
		canid = canid & extidBottom29Mask
		extended_id := canid
		high_11 := extended_id & extidTop11WriteMask
		low_18 := extended_id & extidBottom18Mask
		high_11 <<= 3
		extended_id_shifted := high_11 | low_18
		canid = extended_id_shifted | extidFlagMask
	} else {
		canid = canid & stdidBottom11Mask
		canid <<= 16 + 5
	}
	binary.BigEndian.PutUint32(id[:], canid)
	for _, b := range id {
		err := s.setTxData(b)
		if err != nil {
			return err
		}
	}
	if rtrBit == 1 {
		dlc |= mcpRtrMask
	} else {
		dlc |= (0)
	}
	err := s.setTxData(dlc)
	if err != nil {
		return err
	}
	for _, d := range data {
		err := s.setTxData(d)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *Device) startTransmission(bufNum uint8) error {
	d.cs.Low()
	_, err := d.spi.readWrite(txSidhToRTS(bufNum))
	d.cs.High()
	if err != nil {
		return err
	}

	return nil
}

func nReservedTx(number uint8) uint8 {
	if number < mcpNTxbuffers {
		return number
	}
	return mcpNTxbuffers - 1
}

func txStatusPendingFlag(i uint8) uint8 {
	ret := uint8(0)
	switch i {
	case 0:
		ret = mcpStatTx0Pending
	case 1:
		ret = mcpStatTx1Pending
	case 2:
		ret = mcpStatTx2Pending
	}
	return ret
}

func txCtrlReg(status uint8) uint8 {
	ret := uint8(0)
	switch status {
	case 0:
		ret = mcpTXB0CTRL
	case 1:
		ret = mcpTXB1CTRL
	case 2:
		ret = mcpTXB2CTRL
	}
	return ret
}

func txIfFlag(i uint8) uint8 {
	ret := uint8(0)
	switch i {
	case 0:
		ret = mcpTX0IF
	case 1:
		ret = mcpTX1IF
	case 2:
		ret = mcpTX2IF
	}
	return ret
}

func txSidhToSidh(i uint8) uint8 {
	ret := uint8(0)
	switch i {
	case mcpTX0IF:
		ret = mcpTXB0SIDH
	case mcpTX1IF:
		ret = mcpTXB1SIDH
	case mcpTX2IF:
		ret = mcpTXB2SIDH
	}
	return ret
}

func txSidhToRTS(i uint8) uint8 {
	ret := uint8(0)
	switch i {
	case mcpTXB0SIDH:
		ret = mcpRtsTx0
	case mcpTXB1SIDH:
		ret = mcpRtsTx1
	case mcpTXB2SIDH:
		ret = mcpRtsTx2
	}
	return ret
}

func txSidhToLoad(i uint8) uint8 {
	ret := uint8(0)
	switch i {
	case mcpTXB0SIDH:
		ret = mcpLoadTx0
	case mcpTXB1SIDH:
		ret = mcpLoadTx1
	case mcpTXB2SIDH:
		ret = mcpLoadTx2
	}
	return ret
}

func (d *Device) setRegister(addr, value byte) error {
	d.cs.Low()
	defer d.cs.High()
	_, err := d.spi.readWrite(mcpWrite)
	if err != nil {
		return err
	}
	_, err = d.spi.readWrite(addr)
	if err != nil {
		return err
	}
	_, err = d.spi.readWrite(value)
	if err != nil {
		return err
	}
	// time.Sleep(time.Microsecond * 4)

	return nil
}

func (d *Device) readRegister(addr byte) (byte, error) {
	d.cs.Low()
	defer d.cs.High()
	_, err := d.spi.readWrite(mcpRead)
	if err != nil {
		return 0, err
	}
	_, err = d.spi.readWrite(addr)
	if err != nil {
		return 0, err
	}
	err = d.spi.read(1)
	if err != nil {
		return 0, err
	}
	// time.Sleep(time.Microsecond * 4)
	return d.spi.rx[0], nil
}

func (d *Device) modifyRegister(addr, mask, data byte) error {
	d.cs.Low()
	defer d.cs.High()
	_, err := d.spi.readWrite(mcpBitMod)
	if err != nil {
		return err
	}
	_, err = d.spi.readWrite(addr)
	if err != nil {
		return err
	}
	_, err = d.spi.readWrite(mask)
	if err != nil {
		return err
	}
	_, err = d.spi.readWrite(data)
	if err != nil {
		return err
	}
	// time.Sleep(time.Microsecond * 4)

	return nil
}

func (d *Device) requestNewMode(newMode byte) error {
	s := time.Now()
	for {
		err := d.modifyRegister(mcpCANCTRL, modeMask, newMode)
		if err != nil {
			return err
		}
		r, err := d.readRegister(mcpCANSTAT)
		if err != nil {
			return err
		}
		if r&modeMask == newMode {
			return nil
		} else if e := time.Now(); e.Sub(s) > 200*time.Millisecond {
			return ErrRequestNewModeMaxTimeEx
		}
	}
}

func (d *Device) readStatus() (byte, error) {
	d.cs.Low()
	defer d.cs.High()
	_, err := d.spi.readWrite(mcpReadStatus)
	if err != nil {
		return 0, err
	}
	err = d.spi.read(1)
	if err != nil {
		return 0, err
	}

	return d.spi.rx[0], nil
}

func (d *Device) readRxTxStatus() (byte, error) {
	status, err := d.readStatus()
	if err != nil {
		return 0, err
	}
	ret := status & (mcpStatTxifMask | mcpStatRxifMask)
	if (status & mcpStatTx0if) == 0x08 {
		ret |= mcpTX0IF
	}
	if (status & mcpStatTx1if) == 0x20 {
		ret |= mcpTX1IF
	}
	if (status & mcpStatTx2if) == 0x80 {
		ret |= mcpTX2IF
	}
	ret |= ret & mcpStatRxifMask

	return ret, nil
}

type SPI struct {
	bus drivers.SPI
	tx  []byte
	rx  []byte
}

const (
	tx = iota
	rx
)

func (s *SPI) readWrite(w byte) (byte, error) {
	return s.bus.Transfer(w)
}

func (s *SPI) read(readLength int) error {
	err := s.clearBuffer(rx)
	if err != nil {
		return err
	}
	err = s.setBufferLength(readLength, rx)
	if err != nil {
		return err
	}
	return s.bus.Tx(nil, s.rx)
}

func (s *SPI) write() error {
	return s.bus.Tx(s.tx, nil)
}

func (s *SPI) clearBuffer(dir int) error { return s.setBufferLength(0, dir) }

func (s *SPI) setBufferLength(length int, dir int) error {
	if dir == tx {
		if length > cap(s.tx) {
			return ErrLengthIsLongerThanCapacity
		}
		s.tx = s.tx[:length]
	} else if dir == rx {
		if length > cap(s.rx) {
			return ErrLengthIsLongerThanCapacity
		}
		s.rx = s.rx[:length]
	} else {
		return ErrInvalidDirection
	}
	return nil
}

func (s *SPI) setTxData(data byte) error {
	if len(s.tx) >= bufferSize {
		return ErrCannotExpandBuffer
	}
	s.tx = append(s.tx, data)

	return nil
}

func (d *Device) dumpMode() error {
	m, err := d.getMode()
	if err != nil {
		return err
	}
	fmt.Printf("Mode: %02X\r\n", m)

	return nil
}

func (d *Device) dumpRegister(addr byte) error {
	r, err := d.readRegister(addr)
	if err != nil {
		return err
	}
	fmt.Printf("Register: %02X = %02X\r\n", addr, r)

	return nil
}
