package mshal

func (h *HAL) patchEepromDetectSize() (int, error) {
	eepromFound := 0
	for i := byte(0x50); i <= 0x57; i++ {
		ok, err := h.I2CTransfer(i, []byte{0}, nil)
		if err != nil {
			return 0, err
		}
		if !ok {
			break
		}
		eepromFound++
	}

	if eepromFound == 0 {
		return 0, nil
	}

	if eepromFound > 1 {
		return eepromFound * 256, nil
	}

	if h.deviceType == 2109 {
		/* If we find only one EEPROM we assume it is 16-bit addressable  since <256 byte EEPROMs
		   make no sense for this application. To actually test it you need to write to the chip :( */
		return 4096, nil
	}

	return 256, nil
}

func (h *HAL) patchEEPROMUnlock(unlock bool) error {
	if h.deviceType == 2107 {
		return h.GPIOWrite(4, !unlock)
	} else if h.deviceType == 2109 {
		return h.GPIOWrite(5, !unlock)
	}

	return nil
}

type halPatchEEPROMMemoryRegion struct {
	hal *HAL
}

func (h halPatchEEPROMMemoryRegion) GetName() MemoryRegionNameType {
	return MemoryRegionEEPROM
}

func (h halPatchEEPROMMemoryRegion) GetLength() int {
	return h.hal.eepromSize
}

func (h halPatchEEPROMMemoryRegion) GetParent() (MemoryRegion, int) {
	return nil, 0
}

func (h halPatchEEPROMMemoryRegion) read(addr int, buf []byte) (int, error) {
	var ok bool
	var err error

	if h.GetLength() <= 2048 {
		ok, err = h.hal.I2CTransfer(0x50+byte(addr>>8), []byte{byte(addr)}, buf)
	} else {
		ok, err = h.hal.I2CTransfer(0x50, []byte{byte(addr >> 8), byte(addr)}, buf)
	}

	if err != nil {
		return 0, err
	}
	if !ok {
		return 0, ErrorNoAck
	}

	return len(buf), nil
}

func (h halPatchEEPROMMemoryRegion) write(addr int, buf []byte) (int, error) {
	if err := h.hal.patchEEPROMUnlock(true); err != nil {
		return 0, err
	}
	defer h.hal.patchEEPROMUnlock(false)

	/* We can write 16-byte pages */
	endOfPage := (addr + 16) / 16 * 16
	bytesRemaining := endOfPage - addr

	if len(buf) > bytesRemaining {
		buf = buf[:bytesRemaining]
	}

	var workBuf [16 + 2]byte
	copy(workBuf[2:], buf)
	workBuf[0] = byte(addr >> 8)
	workBuf[1] = byte(addr)
	wrBuf := workBuf[:len(buf)+2]

	var ok bool
	var err error

	if h.GetLength() <= 2048 {
		ok, err = h.hal.I2CTransfer(0x50+byte(addr>>8), wrBuf[1:], nil)
	} else {
		ok, err = h.hal.I2CTransfer(0x50, wrBuf, nil)
	}

	if err != nil {
		return 0, err
	}
	if !ok {
		return 0, ErrorNoAck
	}

	/* The EEPROM is working now, poll it to know when ACK received */
	for {
		_, err := h.read(0, []byte{0})
		if err == nil {
			break
		}
		if err != ErrorNoAck {
			return 0, err
		}
	}

	return len(buf), nil
}

func (h halPatchEEPROMMemoryRegion) GetAlignment() int {
	return 1
}

func (h halPatchEEPROMMemoryRegion) Access(write bool, addr int, buf []byte) (int, error) {
	if len(buf) == 0 {
		return 0, nil
	}

	if addr > h.GetLength() {
		return 0, nil
	}
	if addr+len(buf) > h.GetLength() {
		buf = buf[:h.GetLength()-addr]
	}

	if write {
		return h.write(addr, buf)
	}

	return h.read(addr, buf)
}

func (h *HAL) patchMakeEEPROMRegion() MemoryRegion {
	if !h.patchInstalled {
		return nil
	}

	return regionWrapCompleteIO(halPatchEEPROMMemoryRegion{hal: h})
}
