// author: Kwang-Hyun Park (akaii@kw.ac.kr)

#ifndef _HAMSTER_H_
#define _HAMSTER_H_

#include <windows.h>

class Device;
class Robot;

namespace DataType
{
	enum { INTEGER = 4, FLOAT = 5 };
};

namespace DeviceType
{
	enum { SENSOR, EFFECTOR, COMMAND, EVENT };
};

namespace Connection
{
	enum { STATE_NONE, STATE_CONNECTING, STATE_CONNECTED, STATE_CONNECTION_LOST, STATE_DISCONNECTED, STATE_DISPOSED, ERROR_NO_AVAILABLE_USB = -1, ERROR_ROBOT_NOT_CONNECTED = -2 };
};

class __declspec(dllimport) Executable
{
public:
	Executable(void);
	virtual ~Executable(void);

	virtual void onExecute(Robot* robot) = 0;
};

class __declspec(dllimport) DeviceDataChangedListener
{
public:
	DeviceDataChangedListener(void);
	virtual ~DeviceDataChangedListener(void);

	virtual void onDeviceDataChanged(const Device* device, const void* values) = 0;
};

class __declspec(dllimport) ConnectionListener
{
public:
	ConnectionListener(void);
	virtual ~ConnectionListener(void);

	virtual void onConnectionStateChanged(const Robot* robot, int state) = 0;
	virtual void onConnectionError(const Robot* robot, int errorCode) = 0;
};

template<class T>
class __declspec(dllimport) List
{
private:
	T** collection_;
	int used_;
	int capacity_;

public:
	List(int initialCapacity = 20);
	~List(void);

	int size(void) const;
	bool isEmpty(void) const;
	T* get(int index) const;
	T* operator [](int index) const;
	int indexOf(const T* element) const;
	void add(T* element);
	void remove(T* element);
	void clear(void);
	void addAll(const List<T>& source);
};

class __declspec(dllimport) NamedElement
{
private:
	char* name_;

protected:
	NamedElement(const char* name);
	virtual ~NamedElement(void);

public:
	const char* getName(void) const;
	void setName(const char* name);
};

class __declspec(dllimport) Device : public NamedElement
{
private:
	const int uid_;
	const int dataType_;
	bool fired_;
	bool event_;
protected:
	const int dataSize_;
	List<DeviceDataChangedListener>* listeners_;

protected:
	Device(int uid, const char* name, int dataType, int dataSize);
public:
	virtual ~Device(void);

public:
	int getId(void) const;
	int getProductId(void) const;
	virtual int getDeviceType(void) const = 0;
	int getDataType(void) const;
	int getDataSize(void) const;
	bool e(void) const;
	virtual int read(void) const;
	virtual int read(int index) const;
	virtual int read(int* data) const;
	virtual float readFloat(void) const;
	virtual float readFloat(int index) const;
	virtual int readFloat(float* data) const;
	virtual bool write(int data);
	virtual bool write(int index, int data);
	virtual int write(const int* data);
	virtual bool writeFloat(float data);
	virtual bool writeFloat(int index, float data);
	virtual int writeFloat(const float* data);
	void addDeviceDataChangedListener(DeviceDataChangedListener* listener);
	void removeDeviceDataChangedListener(DeviceDataChangedListener* listener);
	void clearDeviceDataChangedListener(void);
};

class __declspec(dllimport) Roboid : public NamedElement
{
private:
	int mask_;
	List<Roboid>* roboids_;
	List<Device>* devices_;

protected:
	Roboid(const char* name, int mask);
public:
	virtual ~Roboid(void);

public:
	virtual const char* getId(void) const = 0;
	Roboid* findRoboidByName(const char* name) const;
	Device* findDeviceByName(const char* name) const;
	Device* findDeviceById(int deviceId) const;
	void addDeviceDataChangedListener(DeviceDataChangedListener* listener) const;
	void removeDeviceDataChangedListener(DeviceDataChangedListener* listener) const;
	void clearDeviceDataChangedListener(void) const;
};

class __declspec(dllimport) Robot : public NamedElement
{
private:
	List<Roboid>* roboids_;

protected:
	Robot(const char* name);
	virtual ~Robot(void);

public:
	virtual const char* getId(void) const = 0;
	virtual int getIndex(void) const = 0;
	virtual const char* getPortName(void) const = 0;
	virtual const char* getAddress(void) const = 0;
	Roboid* findRoboidByName(const char* name) const;
	Device* findDeviceByName(const char* name) const;
	Device* findDeviceById(int deviceId) const;
	bool e(int deviceId) const;
	int read(int deviceId) const;
	int read(int deviceId, int index) const;
	int read(int deviceId, int* data) const;
	float readFloat(int deviceId) const;
	float readFloat(int deviceId, int index) const;
	int readFloat(int deviceId, float* data) const;
	bool write(int deviceId, int data) const;
	bool write(int deviceId, int index, int data) const;
	int write(int deviceId, const int* data) const;
	bool writeFloat(int deviceId, float data) const;
	bool writeFloat(int deviceId, int index, float data) const;
	int writeFloat(int deviceId, const float* data) const;
	void addDeviceDataChangedListener(DeviceDataChangedListener* listener) const;
	void removeDeviceDataChangedListener(DeviceDataChangedListener* listener) const;
	void clearDeviceDataChangedListener(void) const;
};

class SerialConnector;
class __declspec(dllimport) AbstractRobot : public Robot
{
private:
	const int index_;
	HANDLE threadHandle_;
	Executable* executable_;
	DeviceDataChangedListener* deviceDataChangedListener_;
protected:
	SerialConnector* connector_;

protected:
	AbstractRobot(const char* name, int index);
	virtual ~AbstractRobot(void);

public:
	int getIndex(void) const;
	void setExecutable(Executable* executable);
	virtual void onExecute(Robot* robot);
	virtual void onDeviceDataChanged(const Device* device, const void* values);
};

class HamsterRoboid;
class Runnable;
class __declspec(dllimport) Hamster : public AbstractRobot
{
private:
	const int type_;
	HamsterRoboid* roboid_;
	Runnable* runnable_;
	ConnectionListener* connectionListener_;

public:
	Hamster(ConnectionListener* listener = NULL);
	virtual ~Hamster(void);

public:
	static const int SERIAL = 0;
	static const int BLE = 1;

	static const char* ID;
	static const int LEFT_WHEEL = 0x00400000;
	static const int RIGHT_WHEEL = 0x00400001;
	static const int BUZZER = 0x00400002;
	static const int OUTPUT_A = 0x00400003;
	static const int OUTPUT_B = 0x00400004;
	static const int TOPOLOGY = 0x00400005;
	static const int LEFT_LED = 0x00400006;
	static const int RIGHT_LED = 0x00400007;
	static const int NOTE = 0x00400008;
	static const int LINE_TRACER_MODE = 0x00400009;
	static const int LINE_TRACER_SPEED = 0x0040000a;
	static const int IO_MODE_A = 0x0040000b;
	static const int IO_MODE_B = 0x0040000c;
	static const int CONFIG_PROXIMITY = 0x0040000d;
	static const int CONFIG_GRAVITY = 0x0040000e;
	static const int CONFIG_BAND_WIDTH = 0x0040000f;
	
	static const int SIGNAL_STRENGTH = 0x00400010;
	static const int LEFT_PROXIMITY = 0x00400011;
	static const int RIGHT_PROXIMITY = 0x00400012;
	static const int LEFT_FLOOR = 0x00400013;
	static const int RIGHT_FLOOR = 0x00400014;
	static const int ACCELERATION = 0x00400015;
	static const int LIGHT = 0x00400016;
	static const int TEMPERATURE = 0x00400017;
	static const int INPUT_A = 0x00400018;
	static const int INPUT_B = 0x00400019;
	static const int LINE_TRACER_STATE = 0x0040001a;
	
	static const int TOPOLOGY_NONE = 0;
	static const int TOPOLOGY_DAISY_CHAIN = 1;
	static const int TOPOLOGY_STAR = 2;
	static const int TOPOLOGY_EXTENDED_STAR = 3;
	
	static const int LED_OFF = 0;
	static const int LED_BLUE = 1;
	static const int LED_GREEN = 2;
	static const int LED_CYAN = 3;
	static const int LED_RED = 4;
	static const int LED_MAGENTA = 5;
	static const int LED_YELLOW = 6;
	static const int LED_WHITE = 7;
	
	static const int LINE_TRACER_MODE_OFF = 0;
	static const int LINE_TRACER_MODE_BLACK_LEFT_SENSOR = 1;
	static const int LINE_TRACER_MODE_BLACK_RIGHT_SENSOR = 2;
	static const int LINE_TRACER_MODE_BLACK_BOTH_SENSORS = 3;
	static const int LINE_TRACER_MODE_BLACK_TURN_LEFT = 4;
	static const int LINE_TRACER_MODE_BLACK_TURN_RIGHT = 5;
	static const int LINE_TRACER_MODE_BLACK_MOVE_FORWARD = 6;
	static const int LINE_TRACER_MODE_BLACK_UTURN = 7;
	static const int LINE_TRACER_MODE_WHITE_LEFT_SENSOR = 8;
	static const int LINE_TRACER_MODE_WHITE_RIGHT_SENSOR = 9;
	static const int LINE_TRACER_MODE_WHITE_BOTH_SENSORS = 10;
	static const int LINE_TRACER_MODE_WHITE_TURN_LEFT = 11;
	static const int LINE_TRACER_MODE_WHITE_TURN_RIGHT = 12;
	static const int LINE_TRACER_MODE_WHITE_MOVE_FORWARD = 13;
	static const int LINE_TRACER_MODE_WHITE_UTURN = 14;
	
	static const int IO_MODE_ADC = 0;
	static const int IO_MODE_DI = 1;
	static const int IO_MODE_SERVO = 8;
	static const int IO_MODE_PWM = 9;
	static const int IO_MODE_DO = 10;

	static const int NOTE_OFF = 0;
	static const int NOTE_A_0 = 1;
	static const int NOTE_A_SHARP_0 = 2;
	static const int NOTE_B_FLAT_0 = 2;
	static const int NOTE_B_0 = 3;
	static const int NOTE_C_1 = 4;
	static const int NOTE_C_SHARP_1 = 5;
	static const int NOTE_D_FLAT_1 = 5;
	static const int NOTE_D_1 = 6;
	static const int NOTE_D_SHARP_1 = 7;
	static const int NOTE_E_FLAT_1 = 7;
	static const int NOTE_E_1 = 8;
	static const int NOTE_F_1 = 9;
	static const int NOTE_F_SHARP_1 = 10;
	static const int NOTE_G_FLAT_1 = 10;
	static const int NOTE_G_1 = 11;
	static const int NOTE_G_SHARP_1 = 12;
	static const int NOTE_A_FLAT_1 = 12;
	static const int NOTE_A_1 = 13;
	static const int NOTE_A_SHARP_1 = 14;
	static const int NOTE_B_FLAT_1 = 14;
	static const int NOTE_B_1 = 15;
	static const int NOTE_C_2 = 16;
	static const int NOTE_C_SHARP_2 = 17;
	static const int NOTE_D_FLAT_2 = 17;
	static const int NOTE_D_2 = 18;
	static const int NOTE_D_SHARP_2 = 19;
	static const int NOTE_E_FLAT_2 = 19;
	static const int NOTE_E_2 = 20;
	static const int NOTE_F_2 = 21;
	static const int NOTE_F_SHARP_2 = 22;
	static const int NOTE_G_FLAT_2 = 22;
	static const int NOTE_G_2 = 23;
	static const int NOTE_G_SHARP_2 = 24;
	static const int NOTE_A_FLAT_2 = 24;
	static const int NOTE_A_2 = 25;
	static const int NOTE_A_SHARP_2 = 26;
	static const int NOTE_B_FLAT_2 = 26;
	static const int NOTE_B_2 = 27;
	static const int NOTE_C_3 = 28;
	static const int NOTE_C_SHARP_3 = 29;
	static const int NOTE_D_FLAT_3 = 29;
	static const int NOTE_D_3 = 30;
	static const int NOTE_D_SHARP_3 = 31;
	static const int NOTE_E_FLAT_3 = 31;
	static const int NOTE_E_3 = 32;
	static const int NOTE_F_3 = 33;
	static const int NOTE_F_SHARP_3 = 34;
	static const int NOTE_G_FLAT_3 = 34;
	static const int NOTE_G_3 = 35;
	static const int NOTE_G_SHARP_3 = 36;
	static const int NOTE_A_FLAT_3 = 36;
	static const int NOTE_A_3 = 37;
	static const int NOTE_A_SHARP_3 = 38;
	static const int NOTE_B_FLAT_3 = 38;
	static const int NOTE_B_3 = 39;
	static const int NOTE_C_4 = 40;
	static const int NOTE_C_SHARP_4 = 41;
	static const int NOTE_D_FLAT_4 = 41;
	static const int NOTE_D_4 = 42;
	static const int NOTE_D_SHARP_4 = 43;
	static const int NOTE_E_FLAT_4 = 43;
	static const int NOTE_E_4 = 44;
	static const int NOTE_F_4 = 45;
	static const int NOTE_F_SHARP_4 = 46;
	static const int NOTE_G_FLAT_4 = 46;
	static const int NOTE_G_4 = 47;
	static const int NOTE_G_SHARP_4 = 48;
	static const int NOTE_A_FLAT_4 = 48;
	static const int NOTE_A_4 = 49;
	static const int NOTE_A_SHARP_4 = 50;
	static const int NOTE_B_FLAT_4 = 50;
	static const int NOTE_B_4 = 51;
	static const int NOTE_C_5 = 52;
	static const int NOTE_C_SHARP_5 = 53;
	static const int NOTE_D_FLAT_5 = 53;
	static const int NOTE_D_5 = 54;
	static const int NOTE_D_SHARP_5 = 55;
	static const int NOTE_E_FLAT_5 = 55;
	static const int NOTE_E_5 = 56;
	static const int NOTE_F_5 = 57;
	static const int NOTE_F_SHARP_5 = 58;
	static const int NOTE_G_FLAT_5 = 58;
	static const int NOTE_G_5 = 59;
	static const int NOTE_G_SHARP_5 = 60;
	static const int NOTE_A_FLAT_5 = 60;
	static const int NOTE_A_5 = 61;
	static const int NOTE_A_SHARP_5 = 62;
	static const int NOTE_B_FLAT_5 = 62;
	static const int NOTE_B_5 = 63;
	static const int NOTE_C_6 = 64;
	static const int NOTE_C_SHARP_6 = 65;
	static const int NOTE_D_FLAT_6 = 65;
	static const int NOTE_D_6 = 66;
	static const int NOTE_D_SHARP_6 = 67;
	static const int NOTE_E_FLAT_6 = 67;
	static const int NOTE_E_6 = 68;
	static const int NOTE_F_6 = 69;
	static const int NOTE_F_SHARP_6 = 70;
	static const int NOTE_G_FLAT_6 = 70;
	static const int NOTE_G_6 = 71;
	static const int NOTE_G_SHARP_6 = 72;
	static const int NOTE_A_FLAT_6 = 72;
	static const int NOTE_A_6 = 73;
	static const int NOTE_A_SHARP_6 = 74;
	static const int NOTE_B_FLAT_6 = 74;
	static const int NOTE_B_6 = 75;
	static const int NOTE_C_7 = 76;
	static const int NOTE_C_SHARP_7 = 77;
	static const int NOTE_D_FLAT_7 = 77;
	static const int NOTE_D_7 = 78;
	static const int NOTE_D_SHARP_7 = 79;
	static const int NOTE_E_FLAT_7 = 79;
	static const int NOTE_E_7 = 80;
	static const int NOTE_F_7 = 81;
	static const int NOTE_F_SHARP_7 = 82;
	static const int NOTE_G_FLAT_7 = 82;
	static const int NOTE_G_7 = 83;
	static const int NOTE_G_SHARP_7 = 84;
	static const int NOTE_A_FLAT_7 = 84;
	static const int NOTE_A_7 = 85;
	static const int NOTE_A_SHARP_7 = 86;
	static const int NOTE_B_FLAT_7 = 86;
	static const int NOTE_B_7 = 87;
	static const int NOTE_C_8 = 88;

public:
	const char* getId(void) const;
	const char* getPortName(void) const;
	const char* getAddress(void) const;
	static void wait(int milliseconds);
};

#endif
