// Copyright 2017 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// MPSSE is Multi-Protocol Synchronous Serial Engine
//
// MPSSE basics:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_135_MPSSE_Basics.pdf
//
// MPSSE and MCU emulation modes:
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf

package ftdi

import (
	"context"
	"errors"
	"fmt"
	"time"

	"periph.io/x/conn/v3/gpio"
	"periph.io/x/conn/v3/physic"
)

const (
	// TDI/TDO serial operation synchronised on clock edges.
	//
	// Long streams (default):
	// - [1, 65536] bytes (length is sent minus one, requires 8 bits multiple)
	//   <op>, <LengthLow-1>, <LengthHigh-1>, <byte0>, ..., <byteN>
	//
	// Short streams (dataBit is specified):
	// - [1, 8] bits
	//   <op>, <Length-1>, <byte>
	//
	// When both dataOut and dataIn are specified, one of dataOutFall or
	// dataInFall should be specified, at least for most sane protocols.
	//
	// Flags:
	dataOut     byte = 0x10 // Enable output, default on +VE (Rise)
	dataIn      byte = 0x20 // Enable input, default on +VE (Rise)
	dataOutFall byte = 0x01 // instead of Rise
	dataInFall  byte = 0x04 // instead of Rise
	dataLSBF    byte = 0x08 // instead of MSBF
	dataBit     byte = 0x02 // instead of Byte

	// Data line drives low when the data is 0 and tristates on data 1. This is
	// used with I²C.
	// <op>, <ADBus pins>, <ACBus pins>
	dataTristate byte = 0x9E

	// TSM operation (for JTAG).
	//
	// - Send bits 6 to 0 to the TMS pin using LSB or MSB.
	// - Bit 7 is passed to TDI/DO before the first clock of TMS and is held
	//   static for the duration of TMS clocking.
	//
	// <op>, <Length>, <byte>
	tmsOutLSBFRise byte = 0x4A
	tmsOutLSBFFall byte = 0x4B
	tmsIOLSBInRise byte = 0x6A
	tmsIOLSBInFall byte = 0x6B
	// Unclear: 0x6E and 0x6F

	// GPIO operation.
	//
	// - Operates on 8 GPIOs at a time, e.g. C0~C7 or D0~D7.
	// - Direction 1 means output, 0 means input.
	//
	// <op>, <value>, <direction>
	gpioSetD byte = 0x80
	gpioSetC byte = 0x82
	// <op>, returns <value>
	gpioReadD byte = 0x81
	gpioReadC byte = 0x83

	// Internal loopback.
	//
	// Connects TDI and TDO together.
	internalLoopbackEnable  byte = 0x84
	internalLoopbackDisable byte = 0x85

	// Clock.
	//
	// The TCK/SK has a 50% duty cycle.
	//
	// The inactive clock state can be set via the gpioSetD command and control
	// bit 0.
	//
	// By default, the base clock is 6MHz via a 5x divisor. On
	// FT232H/FT2232H/FT4232H, the 5x divisor can be disabled.
	clock30MHz byte = 0x8A
	clock6MHz  byte = 0x8B
	// Sets clock divisor.
	//
	// The effective value depends if clock30MHz was sent or not.
	//
	// - 0(1) 6MHz / 30MHz
	// - 1(2) 3MHz / 15MHz
	// - 2(3) 2MHz / 10MHz
	// - 3(4) 1.5MHz / 7.5MHz
	// - 4(5) 1.25MHz / 6MHz
	// - ...
	// - 0xFFFF(65536) 91.553Hz / 457.763Hz
	//
	// <op>, <valueL-1>, <valueH-1>
	clockSetDivisor byte = 0x86
	// Uses 3 phases data clocking: data is valid on both clock edges. Needed
	// for I²C.
	clock3Phase byte = 0x8C
	// Uses normal 2 phases data clocking.
	clock2Phase byte = 0x8D
	// Enables clock even while not doing any operation. Used with JTAG.
	// Enables the clock between [1, 8] pulses.
	// <op>, <length-1>
	clockOnShort byte = 0x8E
	// Enables the clock between [8, 524288] pulses in 8 multiples.
	// <op>, <lengthL-1>, <lengthH-1>
	clockOnLong byte = 0x8F
	// Enables clock until D5 is high or low. Used with JTAG.
	clockUntilHigh byte = 0x94
	clockUntilLow  byte = 0x95
	// <op>, <lengthL-1>, <lengthH-1> in 8 multiples.
	clockUntilHighLong byte = 0x9C
	clockUntilLowLong  byte = 0x9D
	// Enables adaptive clocking. Used with JTAG.
	//
	// This causes the controller to wait for D7 signal state as an ACK.
	clockAdaptive byte = 0x96
	// Disables adaptive clocking.
	clockNormal byte = 0x97

	// CPU mode.
	//
	// Access the device registers like a memory mapped device.
	//
	// <op>, <addrLow>
	cpuReadShort byte = 0x90
	// <op>, <addrHi>, <addrLow>
	cpuReadFar byte = 0x91
	// <op>, <addrLow>, <data>
	cpuWriteShort byte = 0x92
	// <op>, <addrHi>, <addrLow>, <data>
	cpuWriteFar byte = 0x91

	// Buffer operations.
	//
	// Flush the buffer back to the host.
	flush byte = 0x87
	// Wait until D5 (JTAG) or I/O1 (CPU) is high. Once it is detected as
	// high, the MPSSE engine moves on to process the next instruction.
	waitHigh byte = 0x88
	waitLow  byte = 0x89
)

// InitMPSSE sets the device into MPSSE mode.
//
// This requires a f232h, ft2232, ft2232h or a ft4232h.
//
// Use only one of Init or InitMPSSE.
func (h *handle) InitMPSSE() error {
	// http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
	// Pre-state:
	// - Write EEPROM i.IsFifo = true so the device DBus is started in tristate.

	// Try to verify the MPSSE controller without initializing it first. This is
	// the 'happy path', which enables reusing the device is its current state
	// without affecting current GPIO state.
	if h.mpsseVerify() != nil {
		// Do a full reset. Just trying to set the MPSSE controller will
		// likely not work. That's a layering violation (since the retry with reset
		// is done in driver.go) but we've survived worse things...
		//
		// TODO(maruel): This is not helping in practice, this need to be fine
		// tuned.
		if err := h.Reset(); err != nil {
			return err
		}
		if err := h.Init(); err != nil {
			return err
		}
		if err := h.SetBitMode(0, bitModeMpsse); err != nil {
			return err
		}
		if err := h.mpsseVerify(); err != nil {
			return err
		}
	}

	// Initialize MPSSE to a known state.
	// Reset the clock since it is impossible to read back the current clock rate.
	// Reset all the GPIOs are inputs since it is impossible to read back the
	// state of each GPIO (if they are input or output).
	cmd := []byte{
		clock30MHz, clockNormal, clock2Phase, internalLoopbackDisable,
		gpioSetC, 0x00, 0x00,
		gpioSetD, 0x00, 0x00,
	}
	if _, err := h.Write(cmd); err != nil {
		return err
	}
	// Success!!
	return nil
}

// mpsseVerify sends an invalid MPSSE command and verifies the returned value
// is incorrect.
//
// In practice this takes around 2ms.
func (h *handle) mpsseVerify() error {
	var b [16]byte
	for _, v := range []byte{0xAA, 0xAB} {
		// Write a bad command and ensure it returned correctly.
		// Unlike what the application note proposes, include a flush op right
		// after. Without the flush, the device will only flush after the delay
		// specified to SetLatencyTimer. The flush removes this unneeded wait,
		// which enables increasing the delay specified to SetLatencyTimer.
		b[0] = v
		b[1] = flush
		if _, err := h.Write(b[:2]); err != nil {
			return fmt.Errorf("d2xx: mpsseVerify: %v", err)
		}
		// Sometimes, especially right after a reset, the device spews a few bytes.
		// Discard them. This significantly increases the odds of a successful
		// initialization.
		p, e := h.h.GetQueueStatus()
		if e != 0 {
			return toErr("Read/GetQueueStatus", e)
		}
		for p > 2 {
			l := int(p) - 2
			if l > len(b) {
				l = len(b)
			}
			// Discard the overflow bytes.
			ctx, cancel := context200ms()
			defer cancel()
			if _, err := h.ReadAll(ctx, b[:l]); err != nil {
				return fmt.Errorf("d2xx: mpsseVerify: %v", err)
			}
			p -= uint32(l)
		}
		// Custom implementation, as we want to flush any stray byte.
		ctx, cancel := context200ms()
		defer cancel()
		if _, err := h.ReadAll(ctx, b[:2]); err != nil {
			return fmt.Errorf("d2xx: mpsseVerify: %v", err)
		}
		// 0xFA means invalid command, 0xAA is the command echoed back.
		if b[0] != 0xFA || b[1] != v {
			return fmt.Errorf("d2xx: mpsseVerify: failed test for byte %#x: %#x", v, b)
		}
	}
	return nil
}

//

// MPSSERegRead reads the memory mapped registers from the device.
func (h *handle) MPSSERegRead(addr uint16) (byte, error) {
	// Unlike most other operations, the uint16 byte order is <hi>, <lo>.
	b := [...]byte{cpuReadFar, byte(addr >> 8), byte(addr), flush}
	if _, err := h.Write(b[:]); err != nil {
		return 0, err
	}
	ctx, cancel := context200ms()
	defer cancel()
	_, err := h.ReadAll(ctx, b[:1])
	return b[0], err
}

// MPSSEClock sets the clock at the closest value and returns it.
func (h *handle) MPSSEClock(f physic.Frequency) (physic.Frequency, error) {
	// TODO(maruel): Memory clock and skip if the same value.
	clk := clock30MHz
	base := 30 * physic.MegaHertz
	div := base / f
	if div >= 65536 {
		clk = clock6MHz
		base /= 5
		div = base / f
		if div >= 65536 {
			return 0, errors.New("d2xx: clock frequency is too low")
		}
	}
	b := [...]byte{clk, clockSetDivisor, byte(div - 1), byte((div - 1) >> 8)}
	_, err := h.Write(b[:])
	return base / div, err
}

// mpsseTxOp returns the right MPSSE command byte for the stream.
func mpsseTxOp(w, r bool, ew, er gpio.Edge, lsbf bool) byte {
	op := byte(0)
	if lsbf {
		op |= dataLSBF
	}
	if w {
		op |= dataOut
		if ew == gpio.FallingEdge {
			op |= dataOutFall
		}
	}
	if r {
		op |= dataIn
		if er == gpio.FallingEdge {
			op |= dataInFall
		}
	}
	return op
}

// MPSSETx runs a transaction on the clock on pins D0, D1 and D2.
//
// It can only do it on a multiple of 8 bits.
func (h *handle) MPSSETx(w, r []byte, ew, er gpio.Edge, lsbf bool) error {
	l := len(w)
	if len(w) != 0 {
		// TODO(maruel): This is easy to fix by daisy chaining operations.
		if len(w) > 65536 {
			return errors.New("d2xx: write buffer too long; max 65536")
		}
	}
	if len(r) != 0 {
		if len(r) > 65536 {
			return errors.New("d2xx: read buffer too long; max 65536")
		}
		if l != 0 && len(r) != l {
			return errors.New("d2xx: mismatched buffer lengths")
		}
		l = len(r)
	}
	// The FT232H has 1Kb Tx and Rx buffers. So partial writes should be done.
	// TODO(maruel): Test.

	// Flush can be useful if rbits != 0.
	op := mpsseTxOp(len(w) != 0, len(r) != 0, ew, er, lsbf)
	cmd := []byte{op, byte(l - 1), byte((l - 1) >> 8)}
	cmd = append(cmd, w...)
	if len(r) != 0 {
		cmd = append(cmd, flush)
	}
	if _, err := h.Write(cmd); err != nil {
		return err
	}
	if len(r) != 0 {
		ctx, cancel := context200ms()
		defer cancel()
		_, err := h.ReadAll(ctx, r)
		return err
	}
	return nil
}

// MPSSETxShort runs a transaction on the clock pins D0, D1 and D2 for a byte
// or less: between 1 and 8 bits.
func (h *handle) MPSSETxShort(w byte, wbits, rbits int, ew, er gpio.Edge, lsbf bool) (byte, error) {
	op := byte(dataBit)
	if lsbf {
		op |= dataLSBF
	}
	l := wbits
	if wbits != 0 {
		if wbits > 8 {
			return 0, errors.New("d2xx: write buffer too long; max 8")
		}
		op |= dataOut
		if ew == gpio.FallingEdge {
			op |= dataOutFall
		}
	}
	if rbits != 0 {
		if rbits > 8 {
			return 0, errors.New("d2xx: read buffer too long; max 8")
		}
		op |= dataIn
		if er == gpio.FallingEdge {
			op |= dataInFall
		}
		if l != 0 && rbits != l {
			return 0, errors.New("d2xx: mismatched buffer lengths")
		}
		l = rbits
	}
	b := [3]byte{op, byte(l - 1)}
	cmd := b[:2]
	if wbits != 0 {
		cmd = append(cmd, w)
	}
	if rbits != 0 {
		cmd = append(cmd, flush)
	}
	if _, err := h.Write(cmd); err != nil {
		return 0, err
	}
	if rbits != 0 {
		ctx, cancel := context200ms()
		defer cancel()
		_, err := h.ReadAll(ctx, b[:1])
		return b[0], err
	}
	return 0, nil
}

// MPSSECBus operates on 8 GPIOs at a time C0~C7.
//
// Direction 1 means output, 0 means input.
func (h *handle) MPSSECBus(mask, value byte) error {
	b := [...]byte{gpioSetC, value, mask}
	_, err := h.Write(b[:])
	return err
}

// MPSSEDBus operates on 8 GPIOs at a time D0~D7.
//
// Direction 1 means output, 0 means input.
func (h *handle) MPSSEDBus(mask, value byte) error {
	b := [...]byte{gpioSetD, value, mask}
	_, err := h.Write(b[:])
	return err
}

// MPSSECBusRead reads all the CBus pins C0~C7.
func (h *handle) MPSSECBusRead() (byte, error) {
	b := [...]byte{gpioReadC, flush}
	if _, err := h.Write(b[:]); err != nil {
		return 0, err
	}
	ctx, cancel := context200ms()
	defer cancel()
	if _, err := h.ReadAll(ctx, b[:1]); err != nil {
		return 0, err
	}
	return b[0], nil
}

// MPSSEDBusRead reads all the DBus pins D0~D7.
func (h *handle) MPSSEDBusRead() (byte, error) {
	b := [...]byte{gpioReadD, flush}
	if _, err := h.Write(b[:]); err != nil {
		return 0, err
	}
	ctx, cancel := context200ms()
	defer cancel()
	if _, err := h.ReadAll(ctx, b[:1]); err != nil {
		return 0, err
	}
	return b[0], nil
}

func context200ms() (context.Context, func()) {
	return context.WithTimeout(context.Background(), 200*time.Millisecond)
}
