#include "devicemodel.h"
#include "telldusgui.h"
#include <telldus-core.h>
#include <QQueue>
#include <QIcon>
#include <math.h>
#include <QThread>
#include <QDebug>

#ifdef _WINDOWS
inline double round(double x) {
	return floor(x + 0.5);
}
#endif

const int SUPPORTED_METHODS = TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_BELL | TELLSTICK_DIM | TELLSTICK_LEARN | TELLSTICK_EXECUTE | TELLSTICK_UP | TELLSTICK_DOWN | TELLSTICK_STOP;


DeviceModel::DeviceModel(QObject *parent)
		:QAbstractTableModel(parent)
{
	connect(this, SIGNAL(deviceChange(int,int,int)), this, SLOT(deviceChanged(int,int,int)), Qt::QueuedConnection);
	int numberOfDevices = tdGetNumberOfDevices();
	errorNo = 0;
	if (numberOfDevices < 0) { //Error
		errorNo = numberOfDevices;
	}
	for( int i = 0; i < numberOfDevices; ++i ) {
		int id = tdGetDeviceId(i);
		Device *device = new Device(id, SUPPORTED_METHODS, this);
		connect(device, SIGNAL(showMessage(QString,QString,QString)), this, SIGNAL(showMessage(QString,QString,QString)));
		devices.append(device);
		connect(device, SIGNAL(stateChanged(int)), this, SLOT(deviceStateChanged(int)), Qt::QueuedConnection);
		connect(device, SIGNAL(nameChanged(int,QString)), this, SLOT(nameChanged(int,QString)), Qt::QueuedConnection);
	}

	deviceChangeCallbackId = tdRegisterDeviceChangeEvent( reinterpret_cast<TDDeviceChangeEvent>(&DeviceModel::deviceChangeEvent), this);
}


DeviceModel::~DeviceModel()
{
	tdUnregisterCallback(deviceChangeCallbackId);
}

int DeviceModel::rowCount(const QModelIndex &) const {
	return devices.size();
}

int DeviceModel::columnCount(const QModelIndex &) const {
	return 3;
}

QVariant DeviceModel::data(const QModelIndex &index, int role) const {
	if (index.column() > 2) {
		return QVariant();
	}

	if (index.row() >= devices.size()) {
		return QVariant();
	}


	if (index.column() == 0) {
		if (role == Qt::DisplayRole) {
			Device *device = devices[index.row()];
			switch( device->lastSentCommand() ) {
				case TELLSTICK_TURNON:
					return tr("on");
				case TELLSTICK_TURNOFF:
					return tr("off");
				case TELLSTICK_UP:
					return tr("up");
				case TELLSTICK_DOWN:
					return tr("down");
				case TELLSTICK_STOP:
					return tr("stopped");
				case TELLSTICK_DIM:
					int value = (int)round(device->lastSentValue().toFloat()/255*100);
					return tr("%1%").arg(value);
			}
			return tr("unknown %1").arg(device->lastSentCommand());
		} else if (role == Qt::DecorationRole) {
			Device *device = devices[index.row()];
			int lastSentCommand = device->lastSentCommand();
			return QIcon( QString(":/images/state_%1.png").arg(lastSentCommand) );
		} else if (role == Qt::TextAlignmentRole) {
			return Qt::AlignCenter;
		}
	} else if (index.column() == 1) {
		if (role == Qt::DisplayRole) {
			return devices[index.row()]->name();
		}
// 	} else if (index.column() == 2) {
// 		if (role == Qt::DisplayRole) {
// 			return devices[index.row()]->methods();
// 		}
	}

	return QVariant();
}

QVariant DeviceModel::headerData ( int section, Qt::Orientation orientation, int role ) const {
	if (orientation != Qt::Horizontal) {
		return QVariant();
	}

	if (role != Qt::DisplayRole) {
		return QVariant();
	}

	switch (section) {
		case 0:
			return tr("State");
		case 1:
			return tr("Device name");
		case 2:
			return tr("Action");
	}

	return QVariant();
}

bool DeviceModel::haveError() const {
	return errorNo != 0;
}

QString DeviceModel::errorString() const {
	char *error = tdGetErrorString(errorNo);
	QString errorStr = QString::fromUtf8( error );
	tdReleaseString(error);
	return errorStr;
}


Device *DeviceModel::device( const QModelIndex &index ) {
	if (index.row() >= devices.size()) {
		return 0;
	}
	return devices[index.row()];
}

int DeviceModel::deviceId( const QModelIndex &index ) {
	if (index.row() >= devices.size()) {
		return 0;
	}
	return devices[index.row()]->id();
}

void DeviceModel::deviceStateChanged( int deviceId ) {
	int row = rowForId(deviceId);
	if (row >= 0) {
		triggerCellUpdate(row, 0);
	}
}

void DeviceModel::deviceChanged( int deviceId, int eventType, int changeType ) {
	if (eventType == TELLSTICK_DEVICE_ADDED) {
		int deviceCount = devices.size();
		beginInsertRows( QModelIndex(), deviceCount, deviceCount );
		Device *device = new Device(deviceId, SUPPORTED_METHODS);
		//We are not running in the DeviceModel-thread here. Move the device to the correct thread
		device->moveToThread(this->thread());
		device->setParent(this);
		connect(device, SIGNAL(showMessage(QString,QString,QString)), this, SIGNAL(showMessage(QString,QString,QString)));
		devices.append(device);
		connect(device, SIGNAL(stateChanged(int)), this, SLOT(deviceStateChanged(int)));
		connect(device, SIGNAL(nameChanged(int,QString)), this, SLOT(nameChanged(int,QString)));
		endInsertRows();

	} else if (eventType == TELLSTICK_DEVICE_REMOVED) {
		int row = rowForId(deviceId);
		if (row >= 0) {
			beginRemoveRows( QModelIndex(), row, row );
			Device *d = devices.takeAt(row);
			delete d;
			endRemoveRows();
		}
	}

}

void DeviceModel::nameChanged(int deviceId, const QString &) {
	int row = rowForId(deviceId);
	if (row >= 0) {
		triggerCellUpdate(row, 1);
	}
}

void DeviceModel::triggerCellUpdate(int row, int column) {
	QModelIndex index = this->index(row, column);
	emit dataChanged( index, index );
}

int DeviceModel::rowForId( int deviceId ) const {
	for (int i = 0; i < devices.size(); ++i) {
		if (devices.at(i)->id() == deviceId) {
			return i;
		}
	}
	return -1;
}


void WINAPI DeviceModel::deviceChangeEvent(int deviceId, int eventId, int changeType, int, void *context) {
	DeviceModel *model = reinterpret_cast<DeviceModel *>(context);
	if (!model) {
		return;
	}
	emit model->deviceChange(deviceId, eventId, changeType);
}

/*void DeviceModel::connectDeviceSignals( Device *device ) const {
	connect(device, SIGNAL(showMessage(const QString &, const QString &, const QString &)), this, SIGNAL(showMessage(const QString &, const QString &, const QString &)));
	connect(device, SIGNAL(eventTriggered(const QString &, const QString &)), this, SIGNAL(eventTriggered(const QString &, const QString &)));
	connect(device, SIGNAL(stateChanged(int, int)), this, SLOT(deviceStateChanged(int,int)));
	connect(device, SIGNAL(deviceChanged(int,int,int)), this, SLOT(deviceChanged(int,int,int)));
}*/
