#pragma once

#include "bus_hardware_id.hpp"
#include "bus_hardware_options.hpp"
#include "bus_hw_options_defaults.hpp"
#include "device_id.hpp"
#include "log_level.hpp"
#include "nlc_callback.hpp"
#include "od_index.hpp"
#include "od_library.hpp"
#include "od_types.hpp"
#include "result.hpp"
#include "result_base.hpp"
#include "profinet_dcp.hpp"

namespace nlc {
/**
 * @brief Interface class used for entry point to the nanolib.
 *
 *
 * Brief workflow:
 * -# Scan is recommended: NanoLibAccessor.listAvailableBusHardware()
 * -# Communication settings: BusHardwareOptions(...)
 * -# NanoLibAccessor.openBusHardwareWithProtocol(...)
 * -# NanoLibAccessor.openBusHardwareWithProtocol(...)
 * -# NanoLibAccessor.addDevice(...)
 * -# NanoLibAccessor.connectDevice(...)
 * -# NanoLibAccessor.disconnectDevice(...)
 * -# NanoLibAccessor.removeDevice(...)
 * -# NanoLibAccessor.closeBusHardware(...)
 *
 */
class NanoLibAccessor {
public:
	NanoLibAccessor() {
	}
	virtual ~NanoLibAccessor() {
	}

	/**
	 * @brief Lists available bus hardware
	 *
	 * @return ResultBusHwIds
	 */
	virtual ResultBusHwIds listAvailableBusHardware() = 0;

	/**
	 * @brief Establishes connection with a bus hardware
	 *
	 * @param busHwId Bus hardware id
	 * @param busHwOpt Bus configuration
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid openBusHardwareWithProtocol(BusHardwareId const &busHwId,
												   BusHardwareOptions const &busHwOpt)
		= 0;

	/**
	 * @brief Disconnects from bus hardware
	 *
	 * @param busHwId Bus hardware id
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid closeBusHardware(BusHardwareId const &busHwId) = 0;

	/**
	 * @brief Sets bus protocol specific state
	 *
	 * @param busHwId Bus hardware id
	 * @param state Bus specific string value
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid setBusState(BusHardwareId const &busHwId, std::string const &state) = 0;

	/**
	 * @brief Add a bus device described by deviceId to nanolib internal device list and return a
	 * DeviceHandle for it
	 *
	 * @param deviceId Device indentificator
	 *
	 * @return ResultDeviceHandle
	 */
	virtual ResultDeviceHandle addDevice(DeviceId const &deviceId) = 0;

	/**
	 * @brief Removes a bus device from nanolib internal list
	 *
	 * @param deviceHandle Handle of the bus device
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid removeDevice(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Device Id of specific device from nanolib internal list
	 *
	 * @param deviceHandle Handle of the bus device
	 *
	 * @return ResultDeviceIds
	 */
	virtual ResultDeviceId getDeviceId(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets all Device Ids from the nanolib internal list
	 *
	 * @return ResultDeviceIds
	 */
	virtual ResultDeviceIds getDeviceIds() = 0;

	/**
	 * @brief Establishes connection with DeviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultVoid Description (Result.hasError())
	 */
	virtual ResultVoid connectDevice(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Disconnects from DeviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid disconnectDevice(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Vendor Id of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultInt
	 */
	virtual ResultInt getDeviceVendorId(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Product Code of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultInt
	 */
	virtual ResultInt getDeviceProductCode(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Name of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultString
	 */
	virtual ResultString getDeviceName(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Hardware version of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultString
	 */
	virtual ResultString getDeviceHardwareVersion(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Firmware build id of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultString
	 */
	virtual ResultString getDeviceFirmwareBuildId(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Bootloader build id of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultString
	 */
	virtual ResultString getDeviceBootloaderBuildId(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Serial number of the device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultString
	 */
	virtual ResultString getDeviceSerialNumber(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Unique ID (96 bit/12 bytes) of the device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultArrayByte
	 */
	virtual ResultArrayByte getDeviceUid(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets Bootloader version of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultInt
	 */
	virtual ResultInt getDeviceBootloaderVersion(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets connection state of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultConnectionState
	 */
	virtual ResultConnectionState getConnectionState(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Checks connection state of bus device with deviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultConnectionState
	 */
	virtual ResultConnectionState checkConnectionState(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Gets device protocol specific state
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultString Bus specific string value
	 */
	virtual ResultString getDeviceState(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Sets device protocol specific state
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param state Bus specific string value
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid setDeviceState(DeviceHandle const deviceHandle, std::string const &state) = 0;

	/**
	 * @brief Scans for devices in a network
	 *
	 * @param BusHardwareId Available bus
	 * @param NlcScanBusCallback Callback object used to trace progress
	 *
	 * @return ResultDeviceIds Array of DeviceId
	 */
	virtual ResultDeviceIds scanDevices(BusHardwareId const &busHwId, NlcScanBusCallback *callback)
		= 0;

	/**
	 * @brief Gets the ProtocolSpecificAccessor object
	 *
	 * @param BusHardwareId Available bus
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid getProtocolSpecificAccessor(BusHardwareId const &busHwId) = 0;

	/**
	 * @brief Checks if connection with bus hardware is open
	 *
	 * @param BusHardwareId Available bus
	 *
	 * @return Boolean
	 */
	virtual bool isBusHardwareOpen(BusHardwareId const &busHardwareId) const = 0;

	/**
	 * @brief Reads a numeric value from controllers object dictionary
	 *
	 * @param DeviceHandle The device to read from
	 * @param OdIndex The index/sub-index to read from
	 *
	 * @return ResultInt The uninterpreted number value (can be signed, unsigned, fix16.16)
	 */
	virtual ResultInt readNumber(DeviceHandle const deviceHandle, OdIndex const odIndex) = 0;

	/**
	 * @brief Reads a string from controllers object dictionary
	 *
	 * @param DeviceHandle The device to read from
	 * @param OdIndex The index/sub-index to read from
	 *
	 * @return ResultString The uninterpreted string value (can be std::string or null)
	 */
	virtual ResultString readString(DeviceHandle const deviceHandle, OdIndex const odIndex) = 0;

	/**
	 * @brief Reads arbitrary bytes (domain object data) from controllers object dictionary
	 *
	 * @param DeviceHandle The device to read from
	 * @param odIndex The index/sub-index to read from
	 *
	 * @return ResultArrayByte The result array of bytes
	 */
	virtual ResultArrayByte readBytes(DeviceHandle const deviceHandle, OdIndex const odIndex) = 0;

	/**
	 * @brief Writes given number to the controllers object dictionary
	 *
	 * @param DeviceHandle The device to write to
	 * @param value The uninterpreted value (can be signed, unsigned, fix16.16)
	 * @param odIndex The index/sub-index to write to
	 * @param size Bits
	 *
	 * @result ResultVoid
	 */
	virtual ResultVoid writeNumber(DeviceHandle const deviceHandle, int64_t value,
								   OdIndex const odIndex, unsigned int bitLength)
		= 0;

	/**
	 * @brief Writes arbitrary bytes (domain object data) to the controllers object dictionary
	 *
	 * @param DeviceHandle The device to write to
	 * @param data Byte vector, array of bytes
	 * @param odIndex The index/sub-index to write to
	 *
	 * @result ResultVoid
	 */
	virtual ResultVoid writeBytes(DeviceHandle const deviceHandle, std::vector<uint8_t> const &data,
								  OdIndex const odIndex)
		= 0;

	/**
	 * @brief Reads numeric array from object dictionary
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param index Unsigned integer
	 *
	 * @return ResultArrayInt
	 */
	virtual ResultArrayInt readNumberArray(DeviceHandle const deviceHandle, uint16_t const index)
		= 0;

	/**
	 * @brief Updates firmware on controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param absoluteFilePath Path to file containing firmware data (uint8_t)
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid firmwareUploadFromFile(DeviceHandle const deviceHandle,
											  std::string const &absoluteFilePath,
											  NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Updates firmware on controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param fwData Array containing firmware data
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid firmwareUpload(DeviceHandle const deviceHandle,
									  std::vector<uint8_t> const &fwData,
									  NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Updates bootloader on controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param bootloaderAbsoluteFilePath Path to file containing bootloader data (uint8_t)
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid bootloaderUploadFromFile(DeviceHandle const deviceHandle,
												std::string const &bootloaderAbsoluteFilePath,
												NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Updates bootloader on controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param btData Bootloader data in array of uint8_t
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid bootloaderUpload(DeviceHandle const deviceHandle,
										std::vector<uint8_t> const &btData,
										NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Updates bootloader and firmware on the controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param bootloaderAbsoluteFilePath Path to file containing bootloader data (uint8_t)
	 * @param absoluteFilePath Path to file containing firmware data (uint8_t)
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid bootloaderFirmwareUploadFromFile(
		DeviceHandle const deviceHandle, std::string const &bootloaderAbsoluteFilePath,
		std::string const &absoluteFilePath, NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Updates bootloader and firmware on the controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param btData Bootloader data in array of uint8_t
	 * @param fwData Firmware data in array of uint8_t
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid
	bootloaderFirmwareUpload(DeviceHandle const deviceHandle, std::vector<uint8_t> const &btData,
							 std::vector<uint8_t> const &fwData, NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Uploads NanoJ program to controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param absoluteFilePath Path to file containing NanoJ data (uint8_t)
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid nanojUploadFromFile(DeviceHandle const deviceHandle,
										   std::string const &absoluteFilePath,
										   NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Uploads NanoJ program to controller
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param vmmData NanoJ data in array of uint8_t
	 * @param NlcDataTransferCallback Callback object used to trace progress
	 *
	 * @return ResultVoid
	 */
	virtual ResultVoid nanojUpload(DeviceHandle const deviceHandle,
								   std::vector<uint8_t> const &vmmData,
								   NlcDataTransferCallback *callback)
		= 0;

	/**
	 * @brief Returns reference to the ObjectDictionary library
	 *
	 * @return OdLibrary&
	 */
	virtual OdLibrary &objectDictionaryLibrary() = 0;

	/**
	 * @brief Assigns ObjectDictionary to DeviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 * @param objectDictionary
	 *
	 * @return ResultObjectDictionary
	 */
	virtual ResultObjectDictionary assignObjectDictionary(DeviceHandle const deviceHandle,
														  ObjectDictionary const &objectDictionary)
		= 0;

	/**
	 * @brief Gets ObjectDictionary from DeviceHandle
	 *
	 * @param deviceHandle Handle of a device on the bus
	 *
	 * @return ResultObjectDictionary
	 */
	virtual ResultObjectDictionary getAssignedObjectDictionary(DeviceHandle const deviceHandle) = 0;

	/**
	 * @brief Sets limitations for the console output of the library.
	 *
	 * @param LogLevel Unsigned integer
	 *
	 */
	virtual void setLoggingLevel(LogLevel level) = 0;

	/**
	 * @brief Returns reference to Profinet DCP interface
	 */
	virtual ProfinetDCP &getProfinetDCP() = 0;
};
} // namespace nlc