import Foundation
import Phidget22_C

/**
Phidget Data Adapter Introduction  The Data Adapter class is used to interface third party devices and microcontrollers with Phidgets.
*/
public class DataAdapterBase : Phidget {

	public init() {
		var h: PhidgetHandle?
		PhidgetDataAdapter_create(&h)
		super.init(h!)
		initializeEvents()
	}

	internal override init(_ handle: PhidgetHandle) {
		super.init(handle)
	}

	deinit {
		if (retained) {
			Phidget_release(&chandle)
		} else {
			uninitializeEvents()
			PhidgetDataAdapter_delete(&chandle)
		}
	}

	/**
	The voltage used to communicate with and power the external device.

	- returns:
	The voltage value

	- throws:
	An error of type `PhidgetError`
	*/
	public func getDataAdapterVoltage() throws -> DataAdapterVoltage {
		let result: PhidgetReturnCode
		var dataAdapterVoltage: Phidget_DataAdapterVoltage = DATAADAPTER_VOLTAGE_EXTERN
		result = PhidgetDataAdapter_getDataAdapterVoltage(chandle, &dataAdapterVoltage)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return DataAdapterVoltage(rawValue: dataAdapterVoltage.rawValue)!
	}

	/**
	The voltage used to communicate with and power the external device.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- dataAdapterVoltage: The voltage value
	*/
	public func setDataAdapterVoltage(_ dataAdapterVoltage: DataAdapterVoltage) throws {
		let result: PhidgetReturnCode
		result = PhidgetDataAdapter_setDataAdapterVoltage(chandle, Phidget_DataAdapterVoltage(dataAdapterVoltage.rawValue))
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

	/**
	Configures the number of data bits used for communication. Refer to the documentation for the device you are communicating with.

	- returns:
	The number of data bits

	- throws:
	An error of type `PhidgetError`
	*/
	public func getDataBits() throws -> UInt32 {
		let result: PhidgetReturnCode
		var dataBits: UInt32 = 0
		result = PhidgetDataAdapter_getDataBits(chandle, &dataBits)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return dataBits
	}

	/**
	Configures the number of data bits used for communication. Refer to the documentation for the device you are communicating with.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- dataBits: The number of data bits
	*/
	public func setDataBits(_ dataBits: UInt32) throws {
		let result: PhidgetReturnCode
		result = PhidgetDataAdapter_setDataBits(chandle, dataBits)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

	/**
	The minimum number of data bits

	- returns:
	The number of data bits

	- throws:
	An error of type `PhidgetError`
	*/
	public func getMinDataBits() throws -> UInt32 {
		let result: PhidgetReturnCode
		var minDataBits: UInt32 = 0
		result = PhidgetDataAdapter_getMinDataBits(chandle, &minDataBits)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return minDataBits
	}

	/**
	The maximum number of data bits

	- returns:
	The number of data bits

	- throws:
	An error of type `PhidgetError`
	*/
	public func getMaxDataBits() throws -> UInt32 {
		let result: PhidgetReturnCode
		var maxDataBits: UInt32 = 0
		result = PhidgetDataAdapter_getMaxDataBits(chandle, &maxDataBits)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return maxDataBits
	}

	/**
	Configures endianness of each byte.

	- returns:
	The endianness of the data bytes.

	- throws:
	An error of type `PhidgetError`
	*/
	public func getEndianness() throws -> DataAdapterEndianness {
		let result: PhidgetReturnCode
		var endianness: PhidgetDataAdapter_Endianness = ENDIANNESS_MSB_FIRST
		result = PhidgetDataAdapter_getEndianness(chandle, &endianness)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return DataAdapterEndianness(rawValue: endianness.rawValue)!
	}

	/**
	Configures endianness of each byte.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- endianness: The endianness of the data bytes.
	*/
	public func setEndianness(_ endianness: DataAdapterEndianness) throws {
		let result: PhidgetReturnCode
		result = PhidgetDataAdapter_setEndianness(chandle, PhidgetDataAdapter_Endianness(endianness.rawValue))
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

	/**
	The rate at which data is transmitted over the communication lines in bits per second.

	- returns:
	The communication frequency to use for future data transfers.

	- throws:
	An error of type `PhidgetError`
	*/
	public func getFrequency() throws -> DataAdapterFrequency {
		let result: PhidgetReturnCode
		var frequency: PhidgetDataAdapter_Frequency = FREQUENCY_10kHz
		result = PhidgetDataAdapter_getFrequency(chandle, &frequency)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return DataAdapterFrequency(rawValue: frequency.rawValue)!
	}

	/**
	The rate at which data is transmitted over the communication lines in bits per second.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- frequency: The communication frequency to use for future data transfers.
	*/
	public func setFrequency(_ frequency: DataAdapterFrequency) throws {
		let result: PhidgetReturnCode
		result = PhidgetDataAdapter_setFrequency(chandle, PhidgetDataAdapter_Frequency(frequency.rawValue))
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

	/**
	Initiates a set of write and read transactions to happen in quick succession.

	- returns:
	The received data. This will be made up of all 'R' bytes as specified in the **I2CPacketString**, in order from first to last.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- address: The address of the I2C device
		- I2CPacketString: Specify the bytes of your I2C packet using 's' for start, 'R' for read, 'T' for write, and 'p' for stop. Only one stop condition per call is supported.  
  
For example, if you wanted to write two bytes, generate a repeat start, write one more byte, then read three bytes, the string would be "sTTsTsRRRp".  
  
You can also use numbers to indicate the number of bytes, e.g. "sT2sTsR3p"
		- data: The entire set of data to send, in order from first to last. The length of this data must match the total number of bytes specified to be sent in the **I2CPacketString**.
	*/
	public func i2cComplexTransaction(address: Int, I2CPacketString: String, data: [UInt8]) throws -> [UInt8] {
		let result: PhidgetReturnCode
		let dataLen: Int = data.count
		let recvData: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: 127)
		var recvDataLen: Int = 127
		result = PhidgetDataAdapter_i2cComplexTransaction(chandle, Int32(address), I2CPacketString, data, dataLen, recvData, &recvDataLen)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return Array(UnsafeBufferPointer(start: recvData, count: Int(recvDataLen)))
	}

	/**
	Write a number of bytes and immediately read a number of bytes over I2C.

	- returns:
	The received data.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- address: The address of the I2C device
		- data: The data to send.
	*/
	public func i2cSendReceive(address: Int, data: [UInt8], receiveLength: UInt32) throws -> [UInt8] {
		let result: PhidgetReturnCode
		let dataLen: Int = data.count
		let recvData: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(receiveLength))
		let receiveLength: Int = Int(receiveLength)
		result = PhidgetDataAdapter_i2cSendReceive(chandle, Int32(address), data, dataLen, recvData, Int(receiveLength))
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return Array(UnsafeBufferPointer(start: recvData, count: Int(receiveLength)))
	}

	/**
	The maximum length of a packet that can be received in bytes.

	- returns:
	The maximum length of a received packet.

	- throws:
	An error of type `PhidgetError`
	*/
	public func getMaxReceivePacketLength() throws -> UInt32 {
		let result: PhidgetReturnCode
		var maxReceivePacketLength: UInt32 = 0
		result = PhidgetDataAdapter_getMaxReceivePacketLength(chandle, &maxReceivePacketLength)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return maxReceivePacketLength
	}

	/**
	Transmits a packet of data using the selected protocol information on the corresponding communication terminals to any connected device(s).

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- data: The data to send.
	*/
	public func sendPacket(data: [UInt8]) throws {
		let result: PhidgetReturnCode
		let dataLen: Int = data.count
		result = PhidgetDataAdapter_sendPacket(chandle, data, dataLen)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

	/**
	Transmits a packet of data using the selected protocol information on the corresponding communication terminals to any connected device(s).

	- parameters:
		- data: The data to send.
		- completion: Asynchronous completion callback
	*/
	public func sendPacket(data: [UInt8], completion: @escaping (ErrorCode) -> ()) {
		let callback = AsyncCallback(completion)
		let callbackCtx = Unmanaged.passRetained(callback)
		let dataLen: Int = data.count
		PhidgetDataAdapter_sendPacket_async(chandle, data, dataLen, AsyncCallback.nativeAsyncCallback, UnsafeMutableRawPointer(callbackCtx.toOpaque()))
	}

	/**
	The maximum length of a packet that can be sent in bytes.

	- returns:
	The maximum length of a sent packet.

	- throws:
	An error of type `PhidgetError`
	*/
	public func getMaxSendPacketLength() throws -> UInt32 {
		let result: PhidgetReturnCode
		var maxSendPacketLength: UInt32 = 0
		result = PhidgetDataAdapter_getMaxSendPacketLength(chandle, &maxSendPacketLength)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return maxSendPacketLength
	}

	/**
	Sends a packet and waits for a corresponding response from the external device, until the timeout elapses.

	- returns:
	The received data.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- data: The data to send.
	*/
	public func sendPacketWaitResponse(data: [UInt8]) throws -> [UInt8] {
		let result: PhidgetReturnCode
		let dataLen: Int = data.count
		let recvData: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: 1024)
		var recvDataLen: Int = 1024
		result = PhidgetDataAdapter_sendPacketWaitResponse(chandle, data, dataLen, recvData, &recvDataLen)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return Array(UnsafeBufferPointer(start: recvData, count: Int(recvDataLen)))
	}

	/**
	Configures functionality of the SPI chip select pin

	- returns:
	The SPI chip select polarity.

	- throws:
	An error of type `PhidgetError`
	*/
	public func getSPIChipSelect() throws -> DataAdapterSPIChipSelect {
		let result: PhidgetReturnCode
		var sPIChipSelect: PhidgetDataAdapter_SPIChipSelect = SPI_CHIP_SELECT_ACTIVE_LOW
		result = PhidgetDataAdapter_getSPIChipSelect(chandle, &sPIChipSelect)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return DataAdapterSPIChipSelect(rawValue: sPIChipSelect.rawValue)!
	}

	/**
	Configures functionality of the SPI chip select pin

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- SPIChipSelect: The SPI chip select polarity.
	*/
	public func setSPIChipSelect(_ SPIChipSelect: DataAdapterSPIChipSelect) throws {
		let result: PhidgetReturnCode
		result = PhidgetDataAdapter_setSPIChipSelect(chandle, PhidgetDataAdapter_SPIChipSelect(SPIChipSelect.rawValue))
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

	/**
	Configures SCLK polarity and phase.

	- returns:
	The SPI mode.

	- throws:
	An error of type `PhidgetError`
	*/
	public func getSPIMode() throws -> DataAdapterSPIMode {
		let result: PhidgetReturnCode
		var sPIMode: PhidgetDataAdapter_SPIMode = SPI_MODE_0
		result = PhidgetDataAdapter_getSPIMode(chandle, &sPIMode)
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
		return DataAdapterSPIMode(rawValue: sPIMode.rawValue)!
	}

	/**
	Configures SCLK polarity and phase.

	- throws:
	An error of type `PhidgetError`

	- parameters:
		- SPIMode: The SPI mode.
	*/
	public func setSPIMode(_ SPIMode: DataAdapterSPIMode) throws {
		let result: PhidgetReturnCode
		result = PhidgetDataAdapter_setSPIMode(chandle, PhidgetDataAdapter_SPIMode(SPIMode.rawValue))
		if result != EPHIDGET_OK {
			throw (PhidgetError(code: result))
		}
	}

}
