#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <iostream>

#include "items/item.h"
#include "service.h"
#include "tcpclient.h"

TcpClient::TcpClient(QObject* parent):
	Service(parent),
	socket(new QTcpSocket(this))
{
	connect(socket, &QTcpSocket::readyRead, this, &TcpClient::socketReadyRead);
}

void TcpClient::sendJson(const QJsonObject& json)
{
	QByteArray jsonData = QJsonDocument(json).toJson();
	socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData);
}

bool TcpClient::launch(const QHostAddress &address, quint16 port)
{
	socket->connectToHost(address, port);
	if(!socket->waitForConnected(4000))
	{
		qWarning()<<"Unable to connect to "<<address<<':'<<port<<' '<<socket->error();
		return false;
	}
	return true;
}

void TcpClient::processIncomeingJson(const QByteArray& jsonbytes)
{
	QJsonDocument doc = QJsonDocument::fromJson(jsonbytes);
	QJsonObject json = doc.object();
	QString type = json["MessageType"].toString();
	if(type == "ItemUpdate")
	{
		QJsonArray data = json["Data"].toArray();
		bool FullList = json["FullList"].toBool(false);
		std::vector<std::shared_ptr<Item>> items;
		std::vector<ItemFieldChanges> fieldChanges;
		for(QJsonValueRef itemjson : data)
		{
			QJsonObject jsonobject = itemjson.toObject();
			std::shared_ptr<Item> item = Item::loadItem(jsonobject);
			if(item)
			{
				item->setLoaded(FullList);
				fieldChanges.push_back(item->loadWithChanges(jsonobject));
				items.push_back(item);
			}
		}
		if(FullList && !items.empty())
		{
			qDebug()<<"Client replaceing items";
			requestReplaceItems(items);
		}
		else if(!items.empty())
		{
			std::vector<ItemAddRequest> itemAddRequests;
			qDebug()<<"Client updateing items";
			for(size_t i = 0; i < items.size(); i++)
			{
				ItemAddRequest request;
				request.type = ITEM_UPDATE_REMOTE;
				request.payload = items[i];
				request.changes = fieldChanges[i];
				itemAddRequests.push_back(request);
				qDebug()<<"Payload"<<request.payload->id();
			}
			gotItems(itemAddRequests);
		}
	}
	else
	{
		Service::processIncomeingJson(jsonbytes);
	}
}

void TcpClient::processComand(const QByteArray& command)
{
	if(command.startsWith("MSG JSON LEN "))
	{
		state = STATE_RECV_JSON;
		recievebytes = command.mid(13).toLongLong();
	}
}

void TcpClient::socketReadyRead()
{
	buffer += socket->readAll();
	bool remianing = true;
	while(remianing)
	{
		remianing = false;
		while(state == STATE_IDLE && buffer.contains('\n'))
		{
			size_t newlineIndex = buffer.indexOf('\n');
			QByteArray command = buffer.left(newlineIndex);
			buffer.remove(0, newlineIndex+1);
			processComand(command);
			remianing = true;
		}
		if(state == STATE_RECV_JSON)
		{
			if(recievebytes <= buffer.size())
			{
				QByteArray json = buffer.left(recievebytes);
				buffer.remove(0, recievebytes);
				recievebytes = 0;
				state = STATE_IDLE;
				processIncomeingJson(json);
				remianing = true;
			}
		}
	}
}

void TcpClient::itemUpdated(ItemUpdateRequest update)
{
	if(update.type == ITEM_UPDATE_USER)
	{
		QJsonArray items;
		QJsonObject itemjson;
		update.payload.storeWithChanges(itemjson, update.changes);
		items.append(itemjson);
		QJsonObject json = createMessage("ItemUpdate", items);
		json["FullList"] = false;
		sendJson(json);
	}
}

void TcpClient::sensorEvent(Sensor sensor, sensor_update_type_t type)
{
	// Only forward user-initiated sensor updates to the server
	// to prevent feedback loops with backend/remote updates
	if(type == SENSOR_UPDATE_USER)
	{
		Service::sensorEvent(sensor, type);
	}
}

TcpClient::~TcpClient()
{
	delete socket;
}
