#pragma once
#include <QObject>
#include <vector>
#include <QRandomGenerator>
#include <QSettings>
#include <QJsonObject>
#include <memory>

class Actor;

typedef enum {
	ITEM_VALUE_BOOL = 0,
	ITEM_VALUE_UINT,
	ITEM_VALUE_NO_VALUE,
	ITEM_VALUE_ENUM,
} item_value_type_t;

typedef enum {
	ITEM_UPDATE_USER = 0,
	ITEM_UPDATE_ACTOR,
	ITEM_UPDATE_REMOTE,
	ITEM_UPDATE_LOADED,
	ITEM_UPDATE_BACKEND,
	ITEM_UPDATE_INVALID
} item_update_type_t;

struct ItemFieldChanges;
struct ItemUpdateRequest;

class ItemData
{
protected:
	QString name_;
	uint8_t value_;
	uint32_t itemId_;
	bool loaded_;
	bool hidden_;
	item_value_type_t type_;
	QString groupName_;
	std::vector<QString> valueNames_;
	bool override_ = false;

public:
	ItemData(uint32_t itemIdIn = QRandomGenerator::global()->generate(),
			 QString name = "Item",
			 uint8_t value = 0,
			 bool loaded = false,
			 bool hidden = false,
			 item_value_type_t type = ITEM_VALUE_BOOL,
			 QString groupName = "");

	inline bool operator==(const ItemData& in) const
	{
		return itemId_ == in.itemId_;
	}
	inline bool operator!=(const ItemData& in) const
	{
		return itemId_ != in.itemId_;
	}

	uint32_t id() const;

	bool hasChanged(const ItemData& other) const;
	bool hasChanged(const ItemData& other, const ItemFieldChanges& changes) const;
	void setName(QString name);
	uint8_t getValue() const;
	void setValueData(uint8_t value);
	bool getLoaded() const;
	void setLoaded(bool loaded);
	bool isHidden() const;
	void setHidden(bool hidden);
	item_value_type_t getValueType();
	QString getGroupName() const;
	void setGroupName(QString groupName);
	std::vector<QString> getValueNames() const;
	void setValueNames(std::vector<QString> valueNames);
	int valueNameToIndex(const QString& name) const;
	QString indexToValueName(int index) const;
	void storeWithChanges(QJsonObject& json, const ItemFieldChanges& changes);
	ItemFieldChanges loadWithChanges(const QJsonObject& json, const bool preserve = false);
	virtual QString getName() const;
	bool getOverride() const;
	void setOverride(bool overrideVal);
	virtual void store(QJsonObject& json);
	virtual void load(const QJsonObject& json, const bool preserve = false);
};

class Item: public QObject, public ItemData
{
	Q_OBJECT
private:
	std::vector< std::shared_ptr<Actor> > actors_;

signals:
	void updated(ItemUpdateRequest update);

public slots:
	virtual void requestUpdate(ItemUpdateRequest update);

public:

	Item(uint32_t itemIdIn = QRandomGenerator::global()->generate(), QString name = "Item", uint8_t value = 0,
	     QObject *parent = nullptr);
	Item(const ItemData& itemData,  QObject *parent = nullptr);

	virtual ~Item();

	Item& operator=(const ItemData& other);
	std::vector< std::shared_ptr<Actor> >& getActors();
	bool hasActors();
	void addActor(std::shared_ptr<Actor> actor);
	bool removeActor(std::shared_ptr<Actor> actor);
	void removeAllActors();
	void setActorsActive(bool in);
	void setOverride(const bool in);
	bool getOverride();
	ItemUpdateRequest createValueUpdateRequest(item_update_type_t type,
	                                           bool withActors = false);

	virtual void store(QJsonObject& json);
	virtual void load(const QJsonObject& json, const bool preserve = false);

	static std::shared_ptr<Item> loadItem(const QJsonObject& json);

protected:
	virtual void enactValue(uint8_t value);

};


struct ItemFieldChanges
{
	bool name :1;
	bool value :1;
	bool hidden :1;
	bool type :1;
	bool groupName :1;
	bool actors :1;
	bool valueNames :1;
	bool override :1;
	ItemFieldChanges(bool defaultVal = false)
	{
		name = defaultVal;
		value = defaultVal;
		hidden = defaultVal;
		type = defaultVal;
		groupName = defaultVal;
		actors = false;
		valueNames = defaultVal;
		override = defaultVal;
	}
	inline bool isNone() const
	{
		return !name && !value && !hidden && !type && !groupName && !actors && !valueNames && !override;
	}
};

struct ItemUpdateRequest
{
	item_update_type_t type = ITEM_UPDATE_INVALID;
	ItemData payload;
	ItemFieldChanges changes;
	std::vector<std::shared_ptr<Actor> > newActors;
};

struct ItemAddRequest
{
	item_update_type_t type = ITEM_UPDATE_INVALID;
	std::shared_ptr<Item> payload;
	ItemFieldChanges changes;
	inline ItemUpdateRequest updateRequest() const
	{
		ItemUpdateRequest update;
		update.payload = *payload;
		update.type = type;
		update.changes = changes;
		if(changes.actors)
			update.newActors = payload->getActors();
		return update;
	}
};

