#pragma once

#include "data/command/InertiaCommand.h"

namespace inertia
{

/** FlashCommands */
enum FlashCommands : uint8_t {
	FlashStatus = 1, // both ProMoveFS & FAT
	FlashCheck = 2, // ProMoveFS
	FlashFormat = 3, // both ProMoveFS & FAT
	FlashGetFirstFile = 4, // ProMoveFS
	FlashGetNextFile = 5, // ProMoveFS
	FlashList = 6, // FAT
	FlashOpen = 7, // FAT
	FlashClose = 8, // FAT
	FlashRead = 9, // FAT
	FlashSeek = 10, // FAT
	FlashListEx = 11, // FAT
	FlashCreate = 12, //FAT
	FlashWrite = 13, // FAT
	FlashLast
};


/** FlashStateType */
enum FlashStateType {
	FlashStateOK = 0,
	FlashStateReadable = 1, // minor version is invalid
	FlashStateInvalid = 2, // File system invalid, need to reformat
	FlashUnformatted = 3, // complete memory needs to be formatted and prepared
	FlashUnsupported = 4 // Unknown memory type, no actions possible
};


/** FilesystemCode */
enum FilesystemCode {
	ProMoveFS = 1,
	FAT16 = 2,
	FAT32 = 3,
	FS_UNKNOWN
};


struct INERTIA_API FlashFileItem
{
	std::wstring filename;
	uint32_t filesize;
	uint32_t filePointer;
	uint32_t createTime;
	uint32_t accessTime;
	uint32_t modifyTime;
	bool hasExtendedInfo;

	FlashFileItem();
	FlashFileItem(std::wstring const& argString);
	FlashFileItem(const std::wstring& _filename, const uint32_t _filesize);
	FlashFileItem(const std::wstring& _filename, const uint32_t _filesize, const uint32_t _filePointer);
	FlashFileItem(const std::wstring& _filename, const uint32_t _filesize, const uint32_t _createTime, const uint32_t _accessTime, const uint32_t _modifyTime);

	const std::wstring ToString() const;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif
};


struct INERTIA_API FlashCommand
	: InertiaCommand
{
protected:
	FlashCommand();
	FlashCommand(const uint8_t* packet, const unsigned max_size);
	FlashCommand(std::wstring const& argString);

public:
	InertiaCommandGroups GetGroup() const override;
	static std::wstring GetName(FlashCommands const command);

	static InertiaCommandPtr Parse(const uint8_t* in, unsigned const packetSize);
	static InertiaCommandPtr Parse(std::wstring const& argString);
};


struct INERTIA_API FlashStatusRequest
	: FlashCommand
{
	FlashStatusRequest();
	FlashStatusRequest(const uint8_t* packet, const unsigned max_size);
	FlashStatusRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashStatusResponse
	: FlashCommand
{
	FlashStateType state;
	uint8_t dataState;
	uint8_t version;
	FilesystemCode code;
	uint8_t hardwareRevision;
	uint8_t softwareMajorRevision, softwareMinorRevision, softwareHotFix;
	uint64_t memorySize;
	uint64_t freeMemorySize;
	uint32_t nFileSequences;
	uint8_t reserved1;

	FlashStatusResponse(const uint8_t* packet, const unsigned max_size);
	FlashStatusResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;
	static const std::wstring FlashStateToString(const FlashStateType state);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashCheckRequest
	: FlashCommand
{
	FlashCheckRequest();
	FlashCheckRequest(const uint8_t* packet, const unsigned max_size);
	FlashCheckRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashCheckResponse
	: FlashCommand
	, BoolValue
{
	FlashCheckResponse(const uint8_t* packet, const unsigned max_size);
	FlashCheckResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashFormatRequest
	: FlashCommand
{
	bool formatData;
	bool formatConfig;
	bool formatAsFAT16;

	FlashFormatRequest();
	FlashFormatRequest(const uint8_t* packet, const unsigned max_size);
	FlashFormatRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashFormatResponse
	: FlashCommand
	, BoolValue
{
	FlashFormatResponse(const uint8_t* packet, const unsigned max_size);
	FlashFormatResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashGetFirstFileRequest
	: FlashCommand
{
	uint8_t sequenceID;

	FlashGetFirstFileRequest();
	FlashGetFirstFileRequest(const uint8_t* packet, const unsigned max_size);
	FlashGetFirstFileRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashGetFirstFileResponse
	: FlashCommand
{
	bool result;
	uint32_t filePointer;

	FlashGetFirstFileResponse(const uint8_t* packet, const unsigned max_size);
	FlashGetFirstFileResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashGetNextFileRequest
	: FlashCommand
{
	uint32_t filePointer;
	int32_t fileCount;

	FlashGetNextFileRequest();
	FlashGetNextFileRequest(const uint8_t* packet, const unsigned max_size);
	FlashGetNextFileRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashGetNextFileResponse
	: FlashCommand
{
	bool result;
	uint8_t fileLength;
	uint8_t sequenceID;
	uint8_t displacement;
	std::vector<uint8_t> rawdata;

	FlashGetNextFileResponse(const uint8_t* packet, const unsigned max_size);
	FlashGetNextFileResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashListRequest
	: FlashCommand
{
	FlashListRequest();
	FlashListRequest(const uint8_t* packet, const unsigned max_size);
	FlashListRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashListResponse
	: FlashCommand
{
	std::vector<FlashFileItem> flashFiles;

	FlashListResponse(const uint8_t* packet, const unsigned max_size);
	FlashListResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashOpenRequest
	: FlashCommand
{
	std::wstring filename;

	FlashOpenRequest();
	FlashOpenRequest(const uint8_t* packet, const unsigned max_size);
	FlashOpenRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashOpenResponse
	: FlashCommand
	, BoolValue
{
	FlashOpenResponse(const uint8_t* packet, const unsigned max_size);
	FlashOpenResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashCloseRequest
	: FlashCommand
{
	FlashCloseRequest();
	FlashCloseRequest(const uint8_t* packet, const unsigned max_size);
	FlashCloseRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashCloseResponse
	: FlashCommand
	, BoolValue
{
	FlashCloseResponse(const uint8_t* packet, const unsigned max_size);
	FlashCloseResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashReadRequest
	: FlashCommand
{
	uint32_t readSize;

	FlashReadRequest();
	FlashReadRequest(const uint8_t* packet, const unsigned max_size);
	FlashReadRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashReadResponse
	: FlashCommand
{
	uint32_t filePointer;
	std::vector<uint8_t> rawdata;

	FlashReadResponse(const uint8_t* packet, const unsigned max_size);
	FlashReadResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashSeekRequest
	: FlashCommand
{
	uint32_t filePointer;

	FlashSeekRequest();
	FlashSeekRequest(const uint8_t* packet, const unsigned max_size);
	FlashSeekRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashSeekResponse
	: FlashCommand
	, BoolValue
{
	FlashSeekResponse(const uint8_t* packet, const unsigned max_size);
	FlashSeekResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashListExRequest
	: FlashCommand
{
	uint32_t fromTime;

	FlashListExRequest();
	FlashListExRequest(const uint8_t* packet, const unsigned max_size);
	FlashListExRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashListExResponse
	: FlashCommand
{
	uint8_t listExVersion;
	uint8_t chunkIndex;
	std::vector<FlashFileItem> flashFiles;

	FlashListExResponse(const uint8_t* packet, const unsigned max_size);
	FlashListExResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashCreateRequest
	: FlashCommand
{
	std::wstring filename;

	FlashCreateRequest();
	FlashCreateRequest(const uint8_t* packet, const unsigned max_size);
	FlashCreateRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashCreateResponse
	: FlashCommand
	, BoolValue
{
	FlashCreateResponse(const uint8_t* packet, const unsigned max_size);
	FlashCreateResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashWriteRequest
	: FlashCommand
{
	std::vector<uint8_t> data;

	FlashWriteRequest();
	FlashWriteRequest(const uint8_t* packet, const unsigned max_size);
	FlashWriteRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};


struct INERTIA_API FlashWriteResponse
	: FlashCommand
	, BoolValue
{
	FlashWriteResponse(const uint8_t* packet, const unsigned max_size);
	FlashWriteResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	uint8_t GetType() const override;
	bool IsResponse() const override;
};

}
