#pragma once

#include "uart.hpp"

// TODO master mutex 1 by 1 command

// Master messages
// 1. Add points, n<=16+data
// 2. Delete points
// 3. Stop move
// 4. Start move
// 5. Set param, n<=16+data
// 6. Goto xy
// 7. Move dxdy
// 8. Goto nextpoint
// 9. Clear Npoint
// A. Set pos

// Slave message
// Ack last message crc. Position. Npoint. Moving. StoredPoints. MemTotalPoints. 1+4+2+1+2+2 = 12

class Comm : public UartBlock<37>
{
public:
	typedef int16_t PosType;
protected:
	enum class Command : uint8_t
	{
		kAddPoints = 0,
		kDeletePoints,
		kStopMove,
		kStartMove,
		kSetParam,
		kGotoXY,
		kMovedXdY,
		kGotoNextPoint,
		kSetNPoint,
		kSetPos,
	};
	PosType readPosType(const uint8_t* buffer, unsigned index=0)
	{
		PosType pos;
		const uint8_t* posBuffer = buffer+index*sizeof(PosType);
		for (unsigned i=0; i<sizeof(PosType); i++)
			((uint8_t*)&pos)[i] = posBuffer[i];
		return pos;
	}
	void writePosType(uint8_t* buffer, PosType pos, unsigned index=0)
	{
		uint8_t* posBuffer = buffer+index*sizeof(PosType);
		for (unsigned i=0; i<sizeof(PosType); i++)
			posBuffer[i] = ((uint8_t*)&pos)[i];
	}
	virtual void addPoints(const uint8_t* xys, unsigned points) = 0;
	virtual void deletePoints() = 0;
	virtual void stopMove() = 0;
	virtual void startMove() = 0;
	virtual void setParam() = 0;
	virtual void gotoXY(PosType x, PosType y) = 0;
	virtual void movedXdY(PosType dx, PosType dy) = 0;
	virtual void gotoNextPoint() = 0;
	virtual void setNPoint(uint16_t nPoint) = 0;
	virtual void setPos(PosType x, PosType y) = 0;
};

#pragma pack(push, 1)
struct SlaveInfo
{
	uint8_t sequence;
	uint16_t status;
	uint16_t spaceSize;
	uint16_t freeSpace;
	int16_t  posX;
	int16_t  posY;
	uint16_t nPoint;
};
#pragma pack(pop)

class CommSlave : public Comm
{
public:
	static const uint16_t maskProtocol      = 0xF000;
	static const uint16_t maskButton        = 0x0010;
	static const uint16_t maskMoving        = 0x0008;
	static const uint16_t maskWaitingNeedle = 0x0004;
	static const uint16_t maskRunning       = 0x0002;
	static const uint16_t maskAlive         = 0x0001;

	static uint8_t getProtocol(uint16_t status)
	{
		return (status&maskProtocol)>>12;
	}

	CommSlave()
	{
		sequence = 255;
	}
	virtual void onMsg(uint8_t* buffer)
	{
		sequence = buffer[0];
		switch((Command)(buffer[1]&0xf))
		{
			case Command::kAddPoints:
				addPoints(buffer+2, (buffer[1]>>4)+1);
				break;
			case Command::kDeletePoints:
				deletePoints();
				break;
			case Command::kStopMove:
				stopMove();
				break;
			case Command::kStartMove:
				startMove();
				break;
			case Command::kSetParam:
				setParam();
				break;
			case Command::kGotoXY:
				gotoXY(readPosType(buffer+2,0), readPosType(buffer+2,1));
				break;
			case Command::kMovedXdY:
				movedXdY(readPosType(buffer+2,0), readPosType(buffer+2,1));
				break;
			case Command::kGotoNextPoint:
				gotoNextPoint();
				break;
			case Command::kSetNPoint:
				setNPoint((buffer[2+0]<<8) | buffer[2+1]);
				break;
			case Command::kSetPos:
				setPos(readPosType(buffer+2,0), readPosType(buffer+2,1));
				break;
		}
	}
	uint8_t sequence;
};
