#include "threads_view.h"

#include "ccm_piintrospector.h"

#include <QPainter>
#include <QSortFilterProxyModel>
#include <QStyle>
#include <QTreeView>


enum ColumnThreads {
	ctClassname,
	ctName,
	ctID,
	ctDelay,
	ctState,
	ctLoad,
	ctRunCost,
	ctRunCount,
	ctColumnCount,
};


ThreadsModel::ThreadsModel() {
	PICodeInfo::EnumInfo * ei = PICODEINFO::enums().value("PIIntrospectionThreads::ThreadState");
	if (ei) {
		for (const auto & e: ei->members) {
#if PIP_VERSION >= PIP_MAKE_VERSION(2, 39, 0)
			state_names[e.value] = PI2QString(e.name.mid(1).toString());
#else
			state_names[e.value] = PI2QString(e.name.mid(1));
#endif
		}
	}
	state_colors[PIIntrospectionThreads::sStopped]  = QColor(Qt::red).lighter(150);
	state_colors[PIIntrospectionThreads::sStarting] = QColor(Qt::blue).lighter(150);
	state_colors[PIIntrospectionThreads::sRunning]  = QColor(Qt::green).lighter(120);
	state_colors[PIIntrospectionThreads::sWaiting]  = QColor(Qt::yellow).lighter(110);
}


void ThreadsModel::update(const PIVector<PIIntrospectionThreads::ThreadInfo> & t) {
	int pts = threads.size_s();
	threads = t;
	if (t.size_s() > pts) {
		beginInsertRows(QModelIndex(), pts, t.size_s() - 1);
		endInsertRows();
	}
	if (t.size_s() < pts) {
		beginRemoveRows(QModelIndex(), t.size_s(), pts - 1);
		endRemoveRows();
	}
	dataChanged(index(0, 0), index(threads.size_s() - 1, columnCount()));
	// emit headerDataChanged(Qt::Horizontal, ccCount, columnCount());
}


void ThreadsModel::setStat(const PIVector<PISystemMonitor::ThreadStats> & s) {
	stat = s;
	dataChanged(index(0, ctLoad), index(threads.size_s() - 1, ctLoad));
}


void ThreadsModel::clear() {
	beginRemoveRows(QModelIndex(), 0, threads.size_s() - 1);
	threads.clear();
	endRemoveRows();
}


int ThreadsModel::rowCount(const QModelIndex & parent) const {
	return threads.size_s();
}


int ThreadsModel::columnCount(const QModelIndex & parent) const {
	return ctColumnCount;
}


QModelIndex ThreadsModel::index(int row, int column, const QModelIndex & parent) const {
	if (row >= threads.size_s() || row >= threads.size_s()) return QModelIndex();
	return createIndex(row, column, quintptr(0));
}


bool ThreadsModel::hasChildren(const QModelIndex & parent) const {
	if (!parent.isValid()) return true;
	return false;
}


QVariant ThreadsModel::headerData(int section, Qt::Orientation orientation, int role) const {
	if (orientation != Qt::Horizontal || role != Qt::DisplayRole) return QVariant();
	switch (section) {
	case ctClassname: return tr("Classname");
	case ctName: return tr("Name");
	case ctID: return tr("pID (pri)");
	case ctDelay: return tr("Delay, ms");
	case ctState: return tr("State");
	case ctLoad: return tr("CPU, %");
	case ctRunCost: return tr("Run cost, avg");
	case ctRunCount: return tr("Run counts");
	default: break;
	}
	return QVariant();
}


QVariant ThreadsModel::data(const QModelIndex & index, int role) const {
	if (role != Qt::DisplayRole && role != Qt::DecorationRole && role != Qt::UserRole) return QVariant();
	const PIIntrospectionThreads::ThreadInfo & ti(threads[index.row()]);
	if (role == Qt::DisplayRole) {
		switch (index.column()) {
		case ctClassname: return PI2QString(ti.classname);
		case ctName: return PI2QString(ti.name);
		case ctID: return QString("%1 (%2)").arg(ti.id).arg(ti.priority);
		case ctDelay: return QString::number(ti.delay);
		case ctState: return state_names.value(ti.state);
		case ctLoad: {
			for (const auto & s: stat) {
				if (s.id == llong(ti.id)) {
					return QString::number(s.cpu_load_kernel + s.cpu_load_user, 'f', 2) + " %";
				}
			}
			return "-";
		}
		case ctRunCost: {
			double v        = ti.run_us;
			QByteArray suff = " us";
			if (v > 1000.) {
				v /= 1000.;
				suff = " ms";
			}
			if (v > 1000.) {
				v /= 1000.;
				suff = " s";
			}
			return QString::number(v, 'f', 1) + suff;
		}
		case ctRunCount: return QString::number(ti.run_count);
		}
	}
	if (index.column() == ctState) {
		if (role == Qt::DecorationRole) {
			return state_colors.value(ti.state);
		}
		if (role == Qt::UserRole) {
			return int(ti.state);
		}
	}
	return QVariant();
}


Qt::ItemFlags ThreadsModel::flags(const QModelIndex & index) const {
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}


/*
void ContainersDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
    QStyledItemDelegate::paint(painter, option, index);
    if (model->index(index.row(), ccCount).data(Qt::UserRole+1).toInt() == 0) {
        QColor col;
        if (option.state == QStyle::State_Enabled ||
            option.state == QStyle::State_HasFocus)
            col = option.palette.color(QPalette::Active, QPalette::WindowText);
        else
            col = option.palette.color(QPalette::Disabled, QPalette::WindowText);
        col.setAlphaF(0.5);
        int y = option.rect.center().y() + (option.fontMetrics.height() / 2.5) - option.fontMetrics.strikeOutPos();
        painter->setPen(QPen(col, lineThickness()));
        painter->drawLine(QPoint(option.rect.left(), y), QPoint(option.rect.right(), y));
    }
}
*/


ThreadsView::ThreadsView(QWidget * parent): QWidget(parent) {
	setupUi(this);
	model                         = new ThreadsModel();
	QSortFilterProxyModel * proxy = new QSortFilterProxyModel();
	proxy->setSourceModel(model);
	proxy->setSortRole(Qt::UserRole);
	proxy->setDynamicSortFilter(false);
	treeThreads->setModel(model);
	connect(checkHideStopped, SIGNAL(toggled(bool)), this, SLOT(updateHidden()));
	// treeContainers->setItemDelegate(new ContainersDelegate(model));
}


ThreadsView::~ThreadsView() {}


void ThreadsView::setStat(const PIVector<PISystemMonitor::ThreadStats> & stat) {
	model->setStat(stat);
}


void ThreadsView::changeEvent(QEvent * e) {
	QWidget::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange: retranslateUi(this); break;
	default: break;
	}
}


void ThreadsView::updateHidden() {
	bool hide_stopped = checkHideStopped->isChecked();
	QModelIndex root  = treeThreads->rootIndex();
	for (int i = 0; i < model->rowCount(); ++i) {
		if (!hide_stopped)
			treeThreads->setRowHidden(i, root, false);
		else {
			if (model->index(i, ctState).data(Qt::UserRole).toInt() == PIIntrospectionThreads::sStopped)
				treeThreads->setRowHidden(i, root, true);
			else
				treeThreads->setRowHidden(i, root, false);
		}
	}
}


void ThreadsView::showThreads(const PIVector<PIIntrospectionThreads::ThreadInfo> & threads) {
	model->update(threads);
	updateHidden();
}


void ThreadsView::clear() {
	model->clear();
}
