#include "../core/objects.h"
#include "../core/files.h"
#include "../core/core.h"
#include "../core/processors.h"
#include "../core/script.h"
#include "../core/lang.h"

#include "models.h"
#include "moc/moc_models.cc"
#include "widgets.h"
#include "property_editor.h"
#include "message_dialog.h"

static QIcon nodeIcon(NodeType type, bool is_disabled = false)
{
	QString iconName;
	switch (type) {
	case NODE_FUNCTIONS:
		iconName = ":/images/star.png";
		break;
	case NODE_LICENSES:
		iconName = ":/images/key.png";
		break;
	case NODE_SCRIPT:
		iconName = ":/images/script.png";
		break;
	case NODE_OPTIONS:
		iconName = ":/images/gear.png";
		break;
	case NODE_FILES:
	case NODE_ASSEMBLIES:
		iconName = ":/images/box.png";
		break;
	case NODE_ARCHITECTURE:
		iconName = ":/images/processor.png";
		break;
	case NODE_LOAD_COMMANDS:
		iconName = ":/images/lightning.png";
		break;
	case NODE_SEGMENTS:
		iconName = ":/images/code.png";
		break;
	case NODE_EXPORTS:
		iconName = ":/images/export.png";
		break;
	case NODE_IMPORTS:
		iconName = ":/images/import.png";
		break;
	case NODE_RESOURCES:
		iconName = ":/images/database.png";
		break;
	case NODE_CALC:
		iconName = ":/images/calc.png";
		break;
	case NODE_DUMP:
		iconName = ":/images/dump.png";
		break;
	case NODE_LICENSE:
		iconName = ":/images/item_key.png";
		break;
	case NODE_FILE:
		iconName = ":/images/item_file.png";
		break;
	case NODE_PROPERTY:
		iconName = ":/images/item_property.png";
		break;
	case NODE_LOAD_COMMAND:
		iconName = ":/images/item_load_command.png";
		break;
	case NODE_SEGMENT:
	case NODE_SECTION:
		iconName = ":/images/item_code.png";
		break;
	case NODE_IMPORT_FUNCTION:
		iconName = ":/images/item_import.png";
		break;
	case NODE_EXPORT:
		iconName = ":/images/item_export.png";
		break;
	case NODE_RESOURCE:
		iconName = ":/images/item_resource.png";
		break;
	case NODE_WATERMARK:
	case NODE_FUNCTION:
		iconName = ":/images/item.png";
		break;
	case NODE_SCRIPT_BOOKMARK:
		iconName = ":/images/item_code.png";
		break;
	case NODE_FOLDER:
	case NODE_RESOURCE_FOLDER:
	case NODE_MAP_FOLDER:
	case NODE_IMPORT_FOLDER:
	case NODE_EXPORT_FOLDER:
	case NODE_IMPORT:
	case NODE_FILE_FOLDER:
		if (is_disabled) {
			QIcon res;
			QPixmap firstImage(":/images/folder_close.png");
			QPixmap secondImage(":/images/bullet_delete.png");
			{
				QPainter painter(&firstImage);
				painter.drawPixmap(firstImage.width() - secondImage.width(), firstImage.height() - secondImage.height(), secondImage);
			}
			res.addPixmap(firstImage);

			firstImage = QPixmap(":/images/folder.png");
			{
				QPainter painter(&firstImage);
				painter.drawPixmap(firstImage.width() - secondImage.width(), firstImage.height() - secondImage.height(), secondImage);
			}
			res.addPixmap(firstImage, QIcon::Normal, QIcon::On);

			return res;
		} else {
			QIcon res = QIcon(":/images/folder_close.png");
			res.addPixmap(QPixmap(":/images/folder.png"), QIcon::Normal, QIcon::On);
			return res;
		}
		break;
	}

	if (is_disabled) {
		QPixmap firstImage(iconName);
		QPixmap secondImage(":/images/bullet_delete.png");
		QPainter painter(&firstImage);
		painter.drawPixmap(firstImage.width() - secondImage.width(), firstImage.height() - secondImage.height(), secondImage);

		return QIcon(firstImage);
	} else {
		return QIcon(iconName);
	}
}

template<typename F>
static QIcon functionIcon(F *func)
{
	if (!func || !func->need_compile())
		return nodeIcon(NODE_FUNCTION, true);

	QString iconName;
	switch (func->compilation_type()) {
	case ctMutation:
		iconName = ":/images/item_m.png";
		break;
	case ctUltra:
		iconName = ":/images/item_u.png";
		break;
	default:
		iconName = ":/images/item_v.png";
		break;
	}

	QString bullet_name;
	if (func->type() == otUnknown)
		bullet_name = ":/images/bullet_warning.png";
	else if (func->compilation_type() != ctMutation && (func->compilation_options() & coLockToKey))
		bullet_name = ":/images/bullet_key.png";

	if (!bullet_name.isEmpty()) {
		QPixmap firstImage(iconName);
		QPixmap secondImage(bullet_name);
		QPainter painter(&firstImage);
		painter.drawPixmap(firstImage.width() - secondImage.width(), firstImage.height() - secondImage.height(), secondImage);

		QIcon res;
		res.addPixmap(firstImage);
		return res;
	}
	return QIcon(iconName);
}

/**
 * ProjectNode
 */

ProjectNode::ProjectNode(ProjectNode *parent, NodeType type, void *data)
	: parent_(NULL), data_(data), type_(type), properties_(NULL)
{
	if (parent)
		parent->addChild(this);
}

void ProjectNode::clear()
{
	for (int i = 0; i < children_.size(); i++) {
		ProjectNode *node = children_[i];
		node->parent_ = NULL;
		delete node;
	}
	children_.clear();

	if (properties_) {
		delete properties_;
		properties_ = NULL;
	}
}

ProjectNode::~ProjectNode()
{
	clear();
	if (parent_)
		parent_->removeChild(this);
}

QString functionProtection(const FunctionBundle *func)
{
	return func ? QString::fromUtf8(func->display_protection().c_str()) : QString::fromUtf8(language[lsNone].c_str());
}

bool ProjectNode::contains(const QRegExp &filter) const
{
	for (int i = 0; i < 2; i++) {
		if (text(i).contains(filter))
			return true;
	}

	return false;
}

QString ProjectNode::text(int column) const
{
	if (column == 0)
		return text_;

	switch (type_) {
	case NODE_SCRIPT:
	case NODE_SCRIPT_BOOKMARK:
		{
			Script *object = static_cast<Script *>(data_);
			if (column == 1) {
				return QString::fromUtf8(object->text().c_str());
			}
		}
		break;
	case NODE_FUNCTION:
		{
			FunctionBundle *object = static_cast<FunctionBundle *>(data_);
			if (column == 1) {
				return QString::fromUtf8(object->display_address().c_str());
			} else if (column == 2) {
				return QString::fromUtf8(object->display_protection().c_str());
			}
		}
		break;
	case NODE_MAP_FUNCTION:
		{
			MapFunctionBundle *object = static_cast<MapFunctionBundle *>(data_);
			if (column == 1) {
				return QString::fromUtf8(object->display_address().c_str());
			} else if (column == 2) {
				IFile *file = object->owner()->owner();
				return functionProtection(file->function_list()->GetFunctionByName(object->name()));
			}
		}
		break;
	case NODE_LOAD_COMMAND:
		{
			ILoadCommand *object = static_cast<ILoadCommand *>(data_);
			if (column == 1) {
				return QString::fromUtf8(DisplayValue(object->address_size(), object->address()).c_str());
			} else if (column == 2) {
				return QString::fromUtf8(DisplayValue(osDWord, object->size()).c_str());
			}
		}
		break;
	case NODE_SEGMENT:
	case NODE_SECTION:
		{
			ISection *object = static_cast<ISection *>(data_);
			if (column == 1) {
				return QString::fromUtf8(DisplayValue(object->address_size(), object->address()).c_str());
			} else if (column == 2) {
				return QString::fromUtf8(DisplayValue(osDWord, object->size()).c_str());
			} else if (column == 3) {
				return QString::fromUtf8(DisplayValue(osDWord, object->physical_offset()).c_str());
			} else if (column == 4) {
				return QString::fromUtf8(DisplayValue(osDWord, object->physical_size()).c_str());
			} else if (column == 5) {
				return QString::fromUtf8(DisplayValue(osDWord, object->flags()).c_str());
			}
		}
		break;
	case NODE_RESOURCE:
		{
			IResource *object = static_cast<IResource *>(data_);
			if (column == 1) {
				return QString::fromUtf8(DisplayValue(object->address_size(), object->address()).c_str());
			} else 	if (column == 2) {
				return QString::fromUtf8(DisplayValue(osDWord, object->size()).c_str());
			}
		}
		break;
	case NODE_IMPORT_FUNCTION:
		{
			IImportFunction *object = static_cast<IImportFunction *>(data_);
			if (column == 1) {
				return QString::fromUtf8(DisplayValue(object->address_size(), object->address()).c_str());
			}
		}
		break;
	case NODE_EXPORT:
		{
			IExport *object = static_cast<IExport *>(data_);
			if (column == 1) {
				return QString::fromUtf8(DisplayValue(object->address_size(), object->address()).c_str());
			}
		}
		break;
	case NODE_PROPERTY:
		{
			Property *object = static_cast<Property *>(data_);
			if (column == 1) {
				return object->valueText();
			}
		}
		break;
#ifdef ULTIMATE
	case NODE_LICENSE:
		{
			License *object = static_cast<License *>(data_);
			if (column == 1) {
				return QString::fromUtf8(object->customer_email().c_str());
			} else if (column == 2) {
				LicenseDate date = object->date();
				return QDate(date.Year, date.Month, date.Day).toString(Qt::SystemLocaleShortDate);
			}
		}
		break;
	case NODE_FILE:
		{
			InternalFile *object = static_cast<InternalFile *>(data_);
			if (column == 1) {
				return QString::fromUtf8(object->file_name().c_str());
			}
		}
		break;
#endif
	}
	return QString();
}

QString ProjectNode::path() const
{
	QString res;
	for (ProjectNode *p = parent(); p && p->type() != NODE_ROOT; p = p->parent()) {
		if (!res.isEmpty())
			res = " > " + res;
		res = p->text() + res;
	}
	return res;
}

void ProjectNode::addChild(ProjectNode *child)
{
	insertChild(childCount(), child);
}

void ProjectNode::insertChild(int index, ProjectNode *child)
{ 
	if (child->parent())
		return;

	child->parent_ = this;
	children_.insert(index, child);
}

void ProjectNode::removeChild(ProjectNode *child)
{
	if (children_.removeOne(child))
		child->parent_ = NULL;
}

void ProjectNode::setPropertyManager(PropertyManager *propertyManager)
{
	if (properties_) {
		delete properties_;
		properties_ = NULL;
	}

	if (!propertyManager)
		return;

	properties_ = new ProjectNode(NULL, type_, data_); 
	properties_->setText(text_);
	properties_->setIcon(nodeIcon(type_));

	QList<Property *> propertyList;
	QMap<Property *, ProjectNode *> propertyMap;
	propertyMap[propertyManager->root()] = properties_;
	propertyList.append(propertyManager->root()->children());
	for (int i = 0; i < propertyList.size(); i++) {
		Property *prop = propertyList[i];
		propertyList.append(prop->children());

		ProjectNode *node = new ProjectNode(propertyMap[prop->parent()], NODE_PROPERTY, prop); 
		node->setText(prop->name());
		node->setIcon(nodeIcon(node->type()));
		propertyMap[prop] = node;
	}
}

void ProjectNode::localize()
{
	switch (type_) {
	case NODE_FUNCTIONS:
		setText(QString::fromUtf8(language[lsFunctionsForProtection].c_str()));
		break;
	case NODE_LICENSES:
		setText(QString::fromUtf8(language[lsLicenses].c_str()));
		break;
	case NODE_FILES:
		setText(QString::fromUtf8(language[lsFiles].c_str()));
		break;
	case NODE_ASSEMBLIES:
		setText(QString::fromUtf8(language[lsAssemblies].c_str()));
		break;
	case NODE_SCRIPT:
		setText(QString::fromUtf8(language[lsScript].c_str()));
		break;
	case NODE_OPTIONS:
		setText(QString::fromUtf8(language[lsOptions].c_str()));
		break;
	case NODE_PROPERTY:
		setText(static_cast<Property*>(data_)->name());
		break;
	case NODE_LOAD_COMMANDS:
		setText(QString::fromUtf8(language[lsDirectories].c_str()));
		break;
	case NODE_SEGMENTS:
		setText(QString::fromUtf8(language[lsSegments].c_str()));
		break;
	case NODE_IMPORTS:
		setText(QString::fromUtf8(language[lsImports].c_str()));
		break;
	case NODE_EXPORTS:
		setText(QString::fromUtf8(language[lsExports].c_str()));
		break;
	case NODE_RESOURCES:
		setText(QString::fromUtf8(language[lsResources].c_str()));
		break;
	case NODE_CALC:
		setText(QString::fromUtf8(language[lsCalculator].c_str()));
		break;
	case NODE_DUMP:
		setText(QString::fromUtf8(language[lsDump].c_str()));
		break;
	case NODE_ARCHITECTURE:
		for (int i = 0; i < childCount(); i++) {
			child(i)->localize();
		}
	}

	if (properties_) {
		QList<ProjectNode *> list;
		list.append(properties_);
		for (int i = 0; i < list.size(); i++) {
			ProjectNode *node = list[i];
			node->localize();

			list.append(node->children());
		}
	}
}

/**
 * BaseModel
 */

BaseModel::BaseModel(QObject *parent)
	: QAbstractItemModel(parent), root_(NULL), core_(NULL)
{
	root_ = new ProjectNode(NULL, NODE_ROOT);
}

BaseModel::~BaseModel()
{
	delete root_;
}

ProjectNode *BaseModel::indexToNode(const QModelIndex &index) const 
{
	if (index.isValid())
		return static_cast<ProjectNode *>(index.internalPointer());
	return root_;
}

QModelIndex BaseModel::parent(const QModelIndex &index) const
{
	if (!index.isValid())
		return QModelIndex();

	ProjectNode *childNode = indexToNode(index);
	ProjectNode *parentNode = childNode->parent();
	if (parentNode == root_)
		return QModelIndex();

	return createIndex(parentNode->parent()->children().indexOf(parentNode), 0, parentNode);
}

int BaseModel::columnCount(const QModelIndex & /* parent */) const
{
	return 1;
}

int BaseModel::rowCount(const QModelIndex &parent) const
{
	ProjectNode *parentNode = indexToNode(parent);
	return parentNode->childCount();
}

QVariant BaseModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	ProjectNode *node = indexToNode(index);
	switch (role) {
	case Qt::DisplayRole:
		return node->text();
	case Qt::DecorationRole:
		return node->icon();
	case Qt::FontRole:
		{
			switch (node->type()) {
			case NODE_MAP_FUNCTION:
			case NODE_WATERMARK:
			case NODE_MESSAGE:
			case NODE_WARNING:
			case NODE_ERROR:
			case NODE_TEMPLATE:
				break;
			default:
				if (node->parent()->type() == NODE_ROOT) {
					QFont font;
					font.setBold(true);
					return font;
				}
			}
		}
		break;
	case Qt::BackgroundColorRole:
		if (node->type() == NODE_WARNING)
			return QColor(0xff, 0xf8, 0xe3);
		else if (node->type() == NODE_ERROR)
			return QColor(0xff, 0xf6, 0xf4);
		break;
	case Qt::TextColorRole:
		if (node->type() == NODE_ERROR)
			return QColor(0xff, 0x3c, 0x08);
		break;
	case Qt::Vmp::StaticTextRole:
		{
			int size = 0;
			switch (node->type())
			{
			case NODE_FUNCTIONS:
				size = (int)reinterpret_cast<FunctionBundleList*>(node->data())->count();
				break;
			case NODE_MAP_FOLDER:
				if (node->parent()->type() == NODE_ROOT) {
					QList<ProjectNode *> nodeList = node->children();
					for (int i = 0; i < nodeList.size(); i++) {
						ProjectNode *child = nodeList[i];
						if (child->type() == NODE_MAP_FOLDER)
							nodeList.append(child->children());
						else
							size++;
					}
					break;
				}
			case NODE_FOLDER:
			case NODE_IMPORT_FOLDER:
			case NODE_EXPORT_FOLDER:
			case NODE_FILE_FOLDER:
				{
					QListIterator<ProjectNode *> i(node->children());
					while (i.hasNext()) {
						NodeType childType = i.next()->type();
						if (childType != NODE_FOLDER && childType != NODE_MAP_FOLDER && childType != NODE_FILE_FOLDER)
							size++;
					}
				}
				break;
			case NODE_RESOURCE_FOLDER:
			case NODE_IMPORT:
			case NODE_IMPORTS:
			case NODE_EXPORTS:
			case NODE_SEGMENTS:
			case NODE_RESOURCES:
			case NODE_LICENSES:
			case NODE_LOAD_COMMANDS:
			case NODE_SEGMENT:
				size = node->childCount();
				break;
#ifdef ULTIMATE
			case NODE_FILES:
			case NODE_ASSEMBLIES:
				size = (int)reinterpret_cast<FileManager*>(node->data())->count();
				break;
#endif
			case NODE_WATERMARK:
				size = (int)reinterpret_cast<Watermark*>(node->data())->use_count();
				break;
			}
			if (size > 0) 
				return QString("(%1)").arg(size);
		}
		break;
	case Qt::Vmp::StaticColorRole: 
		return QColor(0x9b, 0xa2, 0xaa);
	}
	return QVariant();
}

QModelIndex BaseModel::nodeToIndex(ProjectNode *node) const
{
	if (!node || node == root_)
		return QModelIndex();

	return createIndex(node->parent()->children().indexOf(node), 0, node);
}

QModelIndex BaseModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	ProjectNode *parentNode = indexToNode(parent);
	ProjectNode *childNode = parentNode->children().value(row);
	if (!childNode)
		return QModelIndex();

	return createIndex(row, column, childNode);
}

void BaseModel::setCore(Core *core)
{
	clear();
	core_ = core;
}

void BaseModel::clear()
{
	root_->clear();
	objectToNode_.clear();
}

void BaseModel::setObjectNode(void *object, ProjectNode *node)
{
	objectToNode_[object] = node;
}

QModelIndex BaseModel::objectToIndex(void *object) const
{
	return nodeToIndex(objectToNode(object));
}

ProjectNode *BaseModel::objectToNode(void *object) const
{
	return objectToNode_[object];
}

bool BaseModel::removeNode(void *object)
{
	ProjectNode *node = objectToNode(object);
	if (!node)
		return false;

	removeObject(object);

	ProjectNode *parent = node->parent();
	int i = parent->children().indexOf(node);
	beginRemoveRows(nodeToIndex(parent), i, i);
	emit nodeRemoved(node);
	delete node;
	endRemoveRows();
	return true;
}

void BaseModel::updateNode(ProjectNode *node)
{
	QModelIndex index = nodeToIndex(node);
	dataChanged(index, index);
	emit nodeUpdated(node);
}

void BaseModel::removeObject(void *object)
{
	objectToNode_.remove(object);
	emit objectRemoved(object);
}

void BaseModel::createFunctionsTree(ProjectNode *root)
{
	size_t i, count;
	int k, j;
	QList<QString> classList;
	QList<QString> delimList;
	QList<ProjectNode *> folderList;
	QMap<ProjectNode *, QString> delimMap;
	QMap<QString, ProjectNode *> classMap;
	NodeType folder_type;
	QString delim;
	ushort u;
	ProjectNode *folder, *node, *child;
	MapFunctionBundleList *map_function_list = NULL;
	IImport *import = NULL;
	IExportList *export_list = NULL;

	switch (root->type()) {
#ifdef LITE
	case NODE_FUNCTIONS:
#else
	case NODE_ROOT:
#endif
		if (!core()->input_file())
			return;
		map_function_list = core()->input_file()->map_function_list();
		count = map_function_list->count();
		folder_type = NODE_MAP_FOLDER;
		break;
	case NODE_IMPORT:
		import = static_cast<IImport*>(root->data());
		count = import->count();
		folder_type = NODE_IMPORT_FOLDER;
		break;
	case NODE_EXPORTS:
		export_list = static_cast<IExportList*>(root->data());
		count = export_list->count();
		folder_type = NODE_EXPORT_FOLDER;
		break;
	default:
		return;
	}

	for (i = 0; i < count; i++) {
		MapFunctionBundle *func = NULL;
		IImportFunction *import_func = NULL;
		IExport *export_func = NULL;
		QString funcName;

		switch (root->type()) {
#ifdef LITE
		case NODE_FUNCTIONS:
#else
		case NODE_ROOT:
#endif
			func = map_function_list->item(i);
			if (!func->is_code())
				continue;
			funcName = QString::fromUtf8(func->display_name(false).c_str());
			break;
		case NODE_IMPORT:
			import_func = import->item(i);
			funcName = QString::fromUtf8(import_func->display_name(false).c_str());
			break;
		case NODE_EXPORTS:
			export_func = export_list->item(i);
			funcName = QString::fromUtf8(export_func->display_name(false).c_str());
			break;
		}

		size_t c = 0;
		int p = 0;
		classList.clear();
		delimList.clear();
		delim.clear();
		for (k = 0; k < funcName.size(); k++) {
			u = funcName[k].unicode();
			switch (u) {
			case '<':
				c++;
				break;
			case '>':
				c--;
				break;
			case ':':
				if (c == 0 && k > 0 && funcName[k - 1].unicode() == ':') {
					if (funcName[p] == '`')
						p++;
					classList.push_back(funcName.mid(p, k - p - 1));
					delimList.push_back(delim);
					delim = "::";
					p = k + 1;
				}
				break;
			case '.':
				if (c == 0 && k > 0 && funcName[k - 1].unicode() != ':') {
					classList.push_back(funcName.mid(p, k - p));
					delimList.push_back(delim);
					delim = QChar(u);
					p = k + 1;
				}
				break;
			case '/':
				if (c == 0 && k > 0) {
					classList.push_back(funcName.mid(p, k - p));
					delimList.push_back(delim);
					delim = QChar(u);
					p = k + 1;
				}
				break;
			case ' ':
				if (c == 0)
					p = k + 1;
				break;
			case '(':
			case '"':
				k = funcName.size();
				break;
			}
		}

		folder = root;
		QString fullClassName;
		for (k = 0; k < classList.size(); k++) {
			QString className = classList[k];
			delim = delimList[k];
			fullClassName = fullClassName + delim + className;

			ProjectNode *classFolder;
			auto it = classMap.upperBound(fullClassName);
			if (it != classMap.begin() && (it - 1).key() == fullClassName)
				classFolder = (it - 1).value();
			else {
				classFolder = new ProjectNode(NULL, folder_type);
				if (it == classMap.end()) {
					for (j = 0; j < folder->childCount(); j++) {
						if (folder->child(j)->type() != folder_type)
							break;
					}
				}
				else
					j = folder->children().indexOf(*it);
				folder->insertChild(j, classFolder);
				classFolder->setText(className);
				classFolder->setIcon(nodeIcon(classFolder->type()));

				delimMap[classFolder] = delim;
				classMap[fullClassName] = classFolder;
			}
			folder = classFolder;
		}

		switch (root->type()) {
#ifdef LITE
		case NODE_FUNCTIONS:
#else
		case NODE_ROOT:
#endif
			{
				node = new ProjectNode(folder, NODE_MAP_FUNCTION, func);
				node->setText(QString::fromUtf8(func->display_name().c_str()));
				node->setIcon(functionIcon(core()->input_file()->function_list()->GetFunctionByName(func->name())));
				setObjectNode(func, node);
			}
			break;
		case NODE_IMPORT:
			{
				node = new ProjectNode(folder, NODE_IMPORT_FUNCTION, import_func);
				node->setText(QString::fromUtf8(import_func->display_name().c_str()));
				node->setIcon(nodeIcon(node->type()));
				setObjectNode(import_func, node);
			}
			break;
		case NODE_EXPORTS:
			{
				node = new ProjectNode(folder, NODE_EXPORT, export_func);
				node->setText(QString::fromUtf8(export_func->display_name().c_str()));
				node->setIcon(nodeIcon(node->type()));
			}
			break;
		}
	}

	// optimize
	folderList.push_back(root);
	for (k = 0; k < folderList.size(); k++) {
		folder = folderList[k];
		if (folder->childCount() == 1 && folder->child(0)->type() == folder_type) {
			child = folder->child(0);
			while (child->childCount()) {
				node = child->child(0);
				child->removeChild(node);
				folder->addChild(node);
			}
			folder->setText(folder->text() + delimMap[child] + child->text());

			delete child;
			j = folderList.indexOf(child);
			if (j != -1)
				folderList[j] = folder;
		}
	}
}

void BaseModel::localize()
{
	if (isEmpty())
		return;

	QList<ProjectNode*> nodes = root()->children();
	if (nodes.isEmpty())
		nodes.append(root());

	for (int i = 0; i < nodes.count(); i++) {
		ProjectNode *node = nodes[i];
		node->localize();
		updateNode(node);
	}
}

/**
 * ProjectModel
 */

ProjectModel::ProjectModel(QObject *parent)
	: BaseModel(parent), nodeFunctions_(NULL), 
#ifdef ULTIMATE
	nodeLicenses_(NULL), nodeFiles_(NULL),
#endif
#ifndef LITE
	nodeScript_(NULL),
#endif
	nodeOptions_(NULL)
{

}

ProjectModel::~ProjectModel()
{

}

Qt::ItemFlags ProjectModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags res = QAbstractItemModel::flags(index);
	if (res & Qt::ItemIsSelectable) {
		ProjectNode *node = indexToNode(index);
		if (node) {
			if (node->type() == NODE_FOLDER || node->type() == NODE_FUNCTION || node->type() == NODE_FILE_FOLDER || node->type() == NODE_FILE)
				res |= Qt::ItemIsDragEnabled;
			if (node->type() == NODE_FUNCTIONS || node->type() == NODE_FOLDER || node->type() == NODE_FILES || node->type() == NODE_FILE_FOLDER || node->type() == NODE_ASSEMBLIES)
				res |= Qt::ItemIsDropEnabled;
			if ((node->type() == NODE_FOLDER && !static_cast<Folder *>(node->data())->read_only()) || node->type() == NODE_LICENSE || node->type() == NODE_FILE_FOLDER || node->type() == NODE_FILE)
				res |= Qt::ItemIsEditable;
		}
	}
	return res;
}

QStringList ProjectModel::mimeTypes() const
{
    return QStringList() << QLatin1String("application/x-projectmodeldatalist");
}

QMimeData *ProjectModel::mimeData(const QModelIndexList &indexes) const
{
    if (indexes.count() <= 0)
        return 0;
    QStringList types = mimeTypes();
    if (types.isEmpty())
        return 0;
    QMimeData *data = new QMimeData();
    QString format = types.at(0);
    QByteArray encoded;
    QDataStream stream(&encoded, QIODevice::WriteOnly);
    for (QModelIndexList::ConstIterator it = indexes.begin(); it != indexes.end(); ++it) {
		ProjectNode *node = indexToNode(*it);
		if (!node)
			continue;

		stream << node->type();
		switch (node->type()) {
		case NODE_FUNCTION:
			{
				FunctionBundle *func = reinterpret_cast<FunctionBundle *>(node->data());
				stream << QString::fromUtf8(func->id().c_str());
			}
			break;
		case NODE_FOLDER:
			{
				Folder *folder = reinterpret_cast<Folder *>(node->data());
				stream << QString::fromUtf8(folder->id().c_str());
			}
			break;
#ifdef ULTIMATE
		case NODE_FILE_FOLDER:
			{
				FileFolder *folder = reinterpret_cast<FileFolder *>(node->data());
				stream << QString::fromUtf8(folder->id().c_str());
			}
			break;
		case NODE_FILE:
			{
				InternalFile *file = reinterpret_cast<InternalFile *>(node->data());
				stream << (int)file->id();
			}
			break;
#endif
		}
	}
    data->setData(format, encoded);
    return data;
}

bool ProjectModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent)
{
    if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction))
        return false;
    QStringList types = mimeTypes();
    if (types.isEmpty())
        return false;
    QString format = types.at(0);
    if (!data->hasFormat(format))
        return false;
	ProjectNode *dst = indexToNode(parent);
	if (!dst)
		return false;

	if (dst->type()== NODE_FILES || dst->type() == NODE_ASSEMBLIES || dst->type()== NODE_FILE_FOLDER || dst->type()== NODE_FILE) {
#ifdef ULTIMATE
		FileFolder *dst_folder;
		switch (dst->type()) {
		case NODE_FILE:
			dst_folder = reinterpret_cast<InternalFile *>(dst->data())->folder();
			break;
		case NODE_FILE_FOLDER:
			dst_folder = reinterpret_cast<FileFolder *>(dst->data());
			break;
		case NODE_FILES:
		case NODE_ASSEMBLIES:
			dst_folder = NULL;
			break;
		default:
			return false;
		}
		QByteArray encoded = data->data(format);
		QDataStream stream(&encoded, QIODevice::ReadOnly);
		while (!stream.atEnd()) {
			int t;
			stream >> t;
			switch (t) {
			case NODE_FILE:
				{
					int id;
					stream >> id;
					InternalFile *file = core()->file_manager()->item((size_t)id);
					file->set_folder(dst_folder);
				}
				break;
			case NODE_FILE_FOLDER:
				{
					QString str_id;
					stream >> str_id;
					FileFolder *folder = core()->file_manager()->folder_list()->GetFolderById(str_id.toUtf8().constData());
					if (folder)
						folder->set_owner(dst_folder ? dst_folder : core()->file_manager()->folder_list());
				}
				break;
			}
		}
#endif
	} else {
		Folder *dst_folder;
		switch (dst->type()) {
		case NODE_FUNCTION:
			dst_folder = reinterpret_cast<IFunction *>(dst->data())->folder();
			break;
		case NODE_FOLDER:
			dst_folder = reinterpret_cast<Folder *>(dst->data());
			break;
		case NODE_FUNCTIONS:
			dst_folder = NULL;
			break;
		default:
			return false;
		}
		QByteArray encoded = data->data(format);
		QDataStream stream(&encoded, QIODevice::ReadOnly);
		while (!stream.atEnd()) {
			int t;
			stream >> t;
			switch (t) {
			case NODE_FUNCTION:
				{
					QString str_id;
					stream >> str_id;
					FunctionBundle *func = core()->input_file()->function_list()->GetFunctionById(str_id.toUtf8().constData());
					if (func)
						func->set_folder(dst_folder);
				}
				break;
			case NODE_FOLDER:
				{
					QString str_id;
					stream >> str_id;
					Folder *folder = core()->input_file()->folder_list()->GetFolderById(str_id.toUtf8().constData());
					if (folder)
						folder->set_owner(dst_folder ? dst_folder : core()->input_file()->folder_list());
				}
				break;
			}
		}
	}
	return true;
}

QVariant ProjectModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		if (section == 0)
			return QString::fromUtf8(language[lsProject].c_str());
	}
	return QVariant();
}

void ProjectModel::addFolder(Folder *folder)
{
	ProjectNode *node = internalAddFolder(folder);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::updateFolder(Folder *folder)
{
	ProjectNode *node = internalUpdateFolder(folder);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::removeFolder(Folder *folder)
{
	if (removeNode(folder))
		emit modified();
}

ProjectNode *ProjectModel::internalAddFolder(Folder *folder)
{
#ifdef LITE
	return NULL;
#else
	ProjectNode *nodeFolder = objectToNode(folder->owner());
	if (!nodeFolder)
		nodeFolder = nodeFunctions_;
	ProjectNode *node = new ProjectNode(NULL, NODE_FOLDER, (void *)folder);
	node->setText(QString::fromUtf8(folder->name().c_str()));
	node->setIcon(nodeIcon(node->type()));
	QList<ProjectNode *> folderList = nodeFolder->children();
	int index = folderList.size();
	for (int i = 0; i < folderList.size(); i++) {
		if (folderList[i]->type() != NODE_FOLDER) {
			index = i;
			break;
		}
	}
	beginInsertRows(nodeToIndex(nodeFolder), index, index);
	nodeFolder->insertChild(index, node);
	endInsertRows();

	setObjectNode(folder, node);
	return node;
#endif
}

ProjectNode *ProjectModel::internalUpdateFolder(Folder *folder)
{
#ifdef LITE
	return NULL;
#else
	ProjectNode *node = objectToNode(folder);
	if (!node)
		return NULL;

	node->setText(QString::fromUtf8(folder->name().c_str()));

	ProjectNode *folder_node = folder->owner() ? objectToNode(folder->owner()) : nodeFunctions_;
	ProjectNode *parent_node = node->parent();
	if (folder_node && parent_node && parent_node != folder_node) {
		int index_from = parent_node->children().indexOf(node);
		int index_to = folder_node->childCount();
		for (int i = 0; i < folder_node->childCount(); i++) {
			if (folder_node->child(i)->type() != NODE_FOLDER) {
				index_to = i;
				break;
			}
		}
		beginMoveRows(nodeToIndex(parent_node), index_from, index_from, nodeToIndex(folder_node), index_to);
		parent_node->removeChild(node);
		folder_node->insertChild(index_to, node);
		endMoveRows();
	}

	return node;
#endif
}

void ProjectModel::addFunction(IFunction *func)
{
	ProjectNode *node = internalAddFunction(func);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::updateFunction(IFunction *func)
{
	ProjectNode *node = internalUpdateFunction(func);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::removeFunction(IFunction *func)
{
#ifdef LITE
	FunctionBundle *bundle = core()->input_file()->function_list()->GetFunctionByFunc(func);
	if (!bundle)
		return;

	removeObject(func);

	FunctionArch *func_arch = bundle->GetArchByFunction(func);
	if (func_arch)
		delete func_arch;
	if (bundle->count() == 0)
		delete bundle;
	ProjectNode *node = internalUpdateFunction(func);
	if (node)
		updateNode(node);
#else
	ProjectNode *node = objectToNode(func);
	if (!node)
		return;
	removeObject(func);

	FunctionBundle *bundle = reinterpret_cast<FunctionBundle *>(node->data());
	FunctionArch *func_arch = bundle->GetArchByFunction(func);
	if (func_arch)
		delete func_arch;
	if (bundle->count() == 0) {
		removeNode(bundle);
		delete bundle;
	}
#endif
	emit modified();
}

ProjectNode *ProjectModel::internalAddFunction(IFunction *func)
{
#ifndef LITE
	FunctionBundle *bundle = 
#endif
		core()->input_file()->function_list()->Add(func->owner()->owner(), func);
#ifndef LITE
	ProjectNode *nodeFolder = objectToNode(func->folder());
	if (!nodeFolder)
		nodeFolder = nodeFunctions_;

	ProjectNode *node = objectToNode(bundle);
	if (!node) {
		beginInsertRows(nodeToIndex(nodeFolder), nodeFolder->childCount(), nodeFolder->childCount());
		node = new ProjectNode(nodeFolder, NODE_FUNCTION, bundle);
		endInsertRows();
		setObjectNode(bundle, node);
	}
	setObjectNode(func, node);

#endif
	return internalUpdateFunction(func);
}

ProjectNode *ProjectModel::internalUpdateFunction(IFunction *func)
{
#ifdef LITE
	IArchitecture *arch = func->owner()->owner();
	MapFunctionBundle *map_func = core()->input_file()->map_function_list()->GetFunctionByAddress(arch, func->address());
	if (!map_func)
		return NULL;
	
	ProjectNode *node = objectToNode(map_func);
	if (!node)
		return NULL;

	node->setIcon(functionIcon(core()->input_file()->function_list()->GetFunctionByAddress(arch, func->address())));
#else
	ProjectNode *node = objectToNode(func);
	if (!node)
		return NULL;

	FunctionBundle *bundle = reinterpret_cast<FunctionBundle *>(node->data());
	std::string func_name = bundle->display_name();
	node->setText(func_name.empty() ? QString::fromUtf8(bundle->display_address().c_str()) : QString::fromUtf8(func_name.c_str()));
	node->setIcon(functionIcon(bundle));

	ProjectNode *folder_node = bundle->folder() ? objectToNode(bundle->folder()) : nodeFunctions_;
	ProjectNode *parent_node = node->parent();
	if (folder_node && parent_node && parent_node != folder_node) {
		int i = parent_node->children().indexOf(node);
		beginMoveRows(nodeToIndex(parent_node), i, i, nodeToIndex(folder_node), folder_node->childCount());
		parent_node->removeChild(node);
		folder_node->addChild(node);
		endMoveRows();
	}
#endif

	return node;
}

void ProjectModel::updateScript()
{
#ifndef LITE
	ProjectNode *node = nodeScript_;
	if (!node)
		return;

	node->setIcon(nodeIcon(node->type(), !core()->script()->need_compile()));
#endif
}

#ifdef ULTIMATE
void ProjectModel::addLicense(License *license)
{
	ProjectNode *node = internalAddLicense(license);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::updateLicense(License *license)
{
	ProjectNode *node = internalUpdateLicense(license);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::removeLicense(License *license)
{
	if (removeNode(license))
		emit modified();
}

void ProjectModel::addFileFolder(FileFolder *folder)
{
	ProjectNode *node = internalAddFileFolder(folder);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::updateFileFolder(FileFolder *folder)
{
	ProjectNode *node = internalUpdateFileFolder(folder);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::removeFileFolder(FileFolder *folder)
{
	if (removeNode(folder))
		emit modified();
}

void ProjectModel::addFile(InternalFile *file)
{
	ProjectNode *node = internalAddFile(file);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::updateFile(InternalFile *file)
{
	ProjectNode *node = internalUpdateFile(file);
	if (node) {
		updateNode(node);
		emit modified();
	}
}

void ProjectModel::removeFile(InternalFile *file)
{
	if (removeNode(file))
		emit modified();
}

ProjectNode *ProjectModel::internalAddLicense(License *license)
{
	beginInsertRows(nodeToIndex(nodeLicenses_), nodeLicenses_->childCount(), nodeLicenses_->childCount());
	ProjectNode *node = new ProjectNode(nodeLicenses_, NODE_LICENSE, license);
	endInsertRows();

	setObjectNode(license, node);
	internalUpdateLicense(license);
	return node;
}

ProjectNode *ProjectModel::internalUpdateLicense(License *license)
{
	ProjectNode *node = objectToNode(license);
	if (!node)
		return NULL;

	node->setText(QString::fromUtf8(license->customer_name().c_str()));
	node->setIcon(nodeIcon(node->type(), license->blocked()));
	return node;
}

ProjectNode *ProjectModel::internalAddFileFolder(FileFolder *folder)
{
	ProjectNode *nodeFolder = objectToNode(folder->owner());
	if (!nodeFolder)
		nodeFolder = nodeFiles_;
	ProjectNode *node = new ProjectNode(NULL, NODE_FILE_FOLDER, (void *)folder);
	node->setText(QString::fromUtf8(folder->name().c_str()));
	node->setIcon(nodeIcon(node->type()));
	QList<ProjectNode *> folderList = nodeFolder->children();
	int index = folderList.size();
	for (int i = 0; i < folderList.size(); i++) {
		if (folderList[i]->type() != NODE_FILE_FOLDER) {
			index = i;
			break;
		}
	}
	beginInsertRows(nodeToIndex(nodeFolder), index, index);
	nodeFolder->insertChild(index, node);
	endInsertRows();

	setObjectNode(folder, node);
	return node;
}

ProjectNode *ProjectModel::internalUpdateFileFolder(FileFolder *folder)
{
	ProjectNode *node = objectToNode(folder);
	if (!node)
		return NULL;

	node->setText(QString::fromUtf8(folder->name().c_str()));

	ProjectNode *folder_node = folder->owner() ? objectToNode(folder->owner()) : nodeFiles_;
	ProjectNode *parent_node = node->parent();
	if (folder_node && parent_node && parent_node != folder_node) {
		int index_from = parent_node->children().indexOf(node);
		int index_to = folder_node->childCount();
		for (int i = 0; i < folder_node->childCount(); i++) {
			if (folder_node->child(i)->type() != NODE_FILE_FOLDER) {
				index_to = i;
				break;
			}
		}
		beginMoveRows(nodeToIndex(parent_node), index_from, index_from, nodeToIndex(folder_node), index_to);
		parent_node->removeChild(node);
		folder_node->insertChild(index_to, node);
		endMoveRows();
	}

	return node;
}

ProjectNode *ProjectModel::internalAddFile(InternalFile *file)
{
	if (!nodeFiles_)
		return NULL;

	ProjectNode *nodeFolder = objectToNode(file->folder());
	if (!nodeFolder)
		nodeFolder = nodeFiles_;

	beginInsertRows(nodeToIndex(nodeFiles_), nodeFiles_->childCount(), nodeFiles_->childCount());
	ProjectNode *node = new ProjectNode(nodeFolder, NODE_FILE, file);
	endInsertRows();

	setObjectNode(file, node);
	internalUpdateFile(file);
	return node;
}

ProjectNode *ProjectModel::internalUpdateFile(InternalFile *file)
{
	ProjectNode *node = objectToNode(file);
	if (!node)
		return NULL;

	node->setText(QString::fromUtf8(file->name().c_str()));
	node->setIcon(nodeIcon(node->type()));
	ProjectNode *folder_node = file->folder() ? objectToNode(file->folder()) : nodeFiles_;
	ProjectNode *parent_node = node->parent();
	if (folder_node && parent_node && parent_node != folder_node) {
		int i = parent_node->children().indexOf(node);
		beginMoveRows(nodeToIndex(parent_node), i, i, nodeToIndex(folder_node), folder_node->childCount());
		parent_node->removeChild(node);
		folder_node->addChild(node);
		endMoveRows();
	}

	return node;
}

void ProjectModel::updateFiles()
{
	ProjectNode *node = nodeFiles_;
	if (!node)
		return;

	node->setIcon(nodeIcon(node->type(), !core()->file_manager()->need_compile()));
	updateNode(node);
}
#endif
#ifndef LITE
QRegularExpression FuncRegex("function\\s+[_a-zA-Z]+[_a-zA-Z0-9]*\\s*\\([^\\)]*\\)");
void ProjectModel::updateScriptBookmarks()
{
	ProjectNode *parent = nodeScript_;
	if (!parent)
		return;

	QString script = QString::fromUtf8(core()->script()->text().c_str());
	QRegularExpression multi_space("\\s+");
	int nChild = 0;
	auto it = FuncRegex.globalMatch(script);
	while(it.hasNext()) {
		QString newText = it.next().captured().replace(multi_space, " ");

		if (nChild >= parent->childCount()) {
			beginInsertRows(nodeToIndex(nodeScript_), nodeScript_->childCount(), nodeScript_->childCount());
			ProjectNode *node = new ProjectNode(nodeScript_, NODE_SCRIPT_BOOKMARK, core()->script());
			node->setText(newText);
			node->setIcon(nodeIcon(node->type()));
			endInsertRows();
		} else {
			ProjectNode *node = parent->child(nChild);
			if(node->text(0).compare(newText)) {
				node->setText(newText);
				updateNode(node);
			}
		}
		nChild++;
	}
	if(nChild != parent->childCount()) {
		beginRemoveRows(nodeToIndex(parent), nChild, parent->childCount() - 1);
		while (nChild != parent->childCount()) {
			ProjectNode *node = parent->child(nChild);
			emit nodeRemoved(node);
			delete node;
		}
		endRemoveRows();
	}
}
uptr_t ProjectModel::bookmarkNodeToPos(ProjectNode *node) const
{
	uptr_t ret = 0;
	int bookIdx = nodeToIndex(node).row();
	QString script = QString::fromUtf8(core()->script()->text().c_str());
	auto it = FuncRegex.globalMatch(script);
	int nChild = 0;
	while (it.hasNext()) {
		auto m = it.next();
		if (nChild == bookIdx)
			return m.capturedStart();
		nChild++;
	}
	return ret;
}
#endif

void ProjectModel::setCore(Core *core)
{
	BaseModel::setCore(core);

	if (!core) {
		beginResetModel();
		nodeFunctions_ = NULL;
#ifdef ULTIMATE
		nodeLicenses_ = NULL;
		nodeFiles_ = NULL;
#endif
#ifndef LITE
		nodeScript_ = NULL;
#endif
		nodeOptions_ = NULL;
		endResetModel();
		return;
	}

	size_t i, j;

	IFile *file = core->input_file();
	if (file) {
		nodeFunctions_ = new ProjectNode(root(), NODE_FUNCTIONS, file->function_list());
		nodeFunctions_->setText(QString::fromUtf8(language[lsFunctionsForProtection].c_str()));
		nodeFunctions_->setIcon(nodeIcon(nodeFunctions_->type()));

#ifdef LITE
		setObjectNode(file->map_function_list(), nodeFunctions_);
		createFunctionsTree(nodeFunctions_);
#else
		setObjectNode(file->folder_list(), nodeFunctions_);
		std::vector<Folder*> folderList = file->folder_list()->GetFolderList();
		for (i = 0; i < folderList.size(); i++) {
			internalAddFolder(folderList[i]);
		}
#endif

		for (i = 0; i < file->count(); i++) {
			IArchitecture *arch = file->item(i);
			if (!arch->visible())
				continue;

			for (j = 0; j < arch->function_list()->count(); j++) {
				internalAddFunction(arch->function_list()->item(j));
			}
		}
	}

#ifdef ULTIMATE
	LicensingManager *licensing_manager = core->licensing_manager();
	nodeLicenses_ = new ProjectNode(root(), NODE_LICENSES, licensing_manager);
	nodeLicenses_->setText(QString::fromUtf8(language[lsLicenses].c_str()));
	nodeLicenses_->setIcon(nodeIcon(nodeLicenses_->type()));
	for (i = 0; i < licensing_manager->count(); i++) {
		internalAddLicense(licensing_manager->item(i));
	}

	if (file && (file->disable_options() & cpVirtualFiles) == 0) {
		FileManager *file_manager = core->file_manager();
		if (file->format_name() == "PE" && file->count() > 1) {
			nodeFiles_ = new ProjectNode(root(), NODE_ASSEMBLIES, file_manager);
			nodeFiles_->setText(QString::fromUtf8(language[lsAssemblies].c_str()));
		}
		else {
			nodeFiles_ = new ProjectNode(root(), NODE_FILES, file_manager);
			nodeFiles_->setText(QString::fromUtf8(language[lsFiles].c_str()));
		}
		nodeFiles_->setIcon(nodeIcon(nodeFiles_->type(), !file_manager->need_compile()));
		setObjectNode(file_manager->folder_list(), nodeFiles_);

		std::vector<FileFolder*> folderList = file_manager->folder_list()->GetFolderList();
		for (i = 0; i < folderList.size(); i++) {
			internalAddFileFolder(folderList[i]);
		}
		for (i = 0; i < file_manager->count(); i++) {
			internalAddFile(file_manager->item(i));
		}
	}
#endif

	if (file) {
#ifndef LITE
		nodeScript_ = new ProjectNode(root(), NODE_SCRIPT, core->script());
		nodeScript_->setText(QString::fromUtf8(language[lsScript].c_str()));
		nodeScript_->setIcon(nodeIcon(nodeScript_->type(), !core->script()->need_compile()));
		updateScriptBookmarks();
#endif

		nodeOptions_ = new ProjectNode(root(), NODE_OPTIONS);
		nodeOptions_->setText(QString::fromUtf8(language[lsOptions].c_str()));
		nodeOptions_->setIcon(nodeIcon(nodeOptions_->type()));
	}
}

struct FunctionBundleListCompareHelper {
	Folder *folder;
	int field;
	FunctionBundleListCompareHelper(Folder *_folder, int _field) : folder(_folder), field(_field) {}
	bool operator () (const FunctionBundle *func1, const FunctionBundle *func2) const
	{
		if (func1->folder() == func2->folder() && func1->folder() == folder) {
			switch (field) {
			case 0:
				return QString::compare(QString::fromUtf8(func1->display_name().c_str()), QString::fromUtf8(func2->display_name().c_str()), Qt::CaseInsensitive) < 0;
			case 1:
				return func1->display_address() < func2->display_address();
			case 2:
				{
					CompilationType ct1 = func1->need_compile() ? func1->compilation_type() : ctNone;
					CompilationType ct2 = func2->need_compile() ? func2->compilation_type() : ctNone;
					return (ct1 == ct2) ? (func1->compilation_options() < func2->compilation_options()) : (ct1 < ct2);
				}
			}
		}
		return func1->folder() < func2->folder();
	}
};

#ifdef ULTIMATE
struct LicensingManagerCompareHelper {
	int field;
	LicensingManagerCompareHelper(int _field) : field(_field) {}
	bool operator () (const License *license1, const License *license2) const
	{
		switch (field) {
		case 0:
			return QString::compare(QString::fromUtf8(license1->customer_name().c_str()), QString::fromUtf8(license2->customer_name().c_str()), Qt::CaseInsensitive) < 0;
		case 1:
			return QString::compare(QString::fromUtf8(license1->customer_email().c_str()), QString::fromUtf8(license2->customer_email().c_str()), Qt::CaseInsensitive) < 0;
		case 2:
			return license1->date().value() < license2->date().value();
		}
		return false;
	}
};

struct FileManagerCompareHelper {
	FileFolder *folder;
	int field;
	FileManagerCompareHelper(FileFolder *_folder, int _field) : folder(_folder), field(_field) {}
	bool operator () (const InternalFile *file1, const InternalFile *file2) const
	{
		if (file1->folder() == file2->folder() && file1->folder() == folder) {
			switch (field) {
			case 0:
				return QString::compare(QString::fromUtf8(file1->name().c_str()), QString::fromUtf8(file2->name().c_str()), Qt::CaseInsensitive) < 0;
			case 1:
				return QString::compare(QString::fromUtf8(file1->file_name().c_str()), QString::fromUtf8(file2->file_name().c_str()), Qt::CaseInsensitive) < 0;
			}
		}
		return file1->folder() < file2->folder();
	}
};

struct FileFolderCompareHelper {
	bool operator () (const FileFolder *folder1, const FileFolder *folder2) const
	{
		return QString::compare(QString::fromUtf8(folder1->name().c_str()), QString::fromUtf8(folder2->name().c_str()), Qt::CaseInsensitive) < 0;
	}
};
#endif

struct FolderCompareHelper {
	bool operator () (const Folder *folder1, const Folder *folder2) const
	{
		return QString::compare(QString::fromUtf8(folder1->name().c_str()), QString::fromUtf8(folder2->name().c_str()), Qt::CaseInsensitive) < 0;
	}
};

void ProjectModel::sortNode(ProjectNode *node, int field)
{
	bool isModified = false;
	QModelIndex parent = nodeToIndex(node);
	QList<ProjectNode*> nodeList = node->children();

	switch (node->type()) {
	case NODE_FUNCTIONS:
	case NODE_FOLDER:
		{
			IFile *file = core()->input_file();
			Folder *folder = (node->type() == NODE_FUNCTIONS) ? core()->input_file()->folder_list() : reinterpret_cast<Folder *>(node->data());

			if (field == 0) {
				std::sort(folder->_begin(),folder->_end(), FolderCompareHelper());

				QList<int> posList;
				for (int i = 0; i < nodeList.size(); i++) {
					ProjectNode *child = nodeList[i];
					if (child->type() != NODE_FOLDER) 
						break;

					int j = (int)folder->IndexOf(reinterpret_cast<Folder *>(child->data()));
					if (j == -1)
						continue;

					int p = posList.size();
					for (int c = 0; c < posList.size(); c++) {
						if (posList[c] > j) {
							p = c;
							break;
						}
					}

					if (p < i) {
						beginMoveRows(parent, i, i, parent, p);
						node->removeChild(child);
						node->insertChild(p, child);
						endMoveRows();
						isModified = true;
					}
					posList.insert(p, j);
				}
			}

			if (node->type() == NODE_FUNCTIONS)
				folder = NULL;

			std::sort(file->function_list()->_begin(), file->function_list()->_end(), FunctionBundleListCompareHelper(folder, field));

			QList<FunctionBundle *> funcList;
			for (size_t i = 0; i < file->function_list()->count(); i++) {
				FunctionBundle *func = file->function_list()->item(i);
				if (func->folder() == folder)
					funcList.append(func);
			}

			int k = 0;
			QList<int> posList;
			for (int i = 0; i < nodeList.size(); i++) {
				ProjectNode *child = nodeList[i];
				if (child->type() == NODE_FOLDER) {
					k++;
					continue;
				}

				int j = funcList.indexOf(reinterpret_cast<FunctionBundle *>(child->data()));
				if (j == -1)
					continue;

				int p = posList.size();
				for (int c = 0; c < posList.size(); c++) {
					if (posList[c] > j) {
						p = c;
						break;
					}
				}

				if (k + p < i) {
					beginMoveRows(parent, i, i, parent, k + p);
					node->removeChild(child);
					node->insertChild(k + p, child);
					endMoveRows();
					isModified = true;
				}
				posList.insert(p, j);
			}
		}
		break;
#ifdef ULTIMATE
	case NODE_LICENSES:
		{
			std::sort(core()->licensing_manager()->_begin(), core()->licensing_manager()->_end(), LicensingManagerCompareHelper(field));

			QList<int> posList;
			for (int i = 0; i < node->childCount(); i++) {
				ProjectNode *child = node->child(i);

				int j = (int)core()->licensing_manager()->IndexOf(reinterpret_cast<License *>(child->data()));
				if (j == -1)
					continue;

				int p = posList.size();
				for (int c = 0; c < posList.size(); c++) {
					if (posList[c] > j) {
						p = c;
						break;
					}
				}

				if (p < i) {
					beginMoveRows(parent, i, i, parent, p);
					node->removeChild(child);
					node->insertChild(p, child);
					endMoveRows();
					isModified = true;
				}
				posList.insert(p, j);
			}
		}
		break;
	case NODE_FILES:
	case NODE_FILE_FOLDER:
	case NODE_ASSEMBLIES:
		{
			FileFolder *folder = (node->type() == NODE_FILES || node->type() == NODE_ASSEMBLIES) ? core()->file_manager()->folder_list() : reinterpret_cast<FileFolder *>(node->data());

			if (field == 0) {
				std::sort(folder->_begin(),folder->_end(), FileFolderCompareHelper());

				QList<int> posList;
				for (int i = 0; i < node->childCount(); i++) {
					ProjectNode *child = node->child(i);
					if (child->type() != NODE_FILE_FOLDER) 
						break;

					int j = (int)folder->IndexOf(reinterpret_cast<FileFolder *>(child->data()));
					if (j == -1)
						continue;

					int p = posList.size();
					for (int c = 0; c < posList.size(); c++) {
						if (posList[c] > j) {
							p = c;
							break;
						}
					}

					if (p < i) {
						beginMoveRows(parent, i, i, parent, p);
						node->removeChild(child);
						node->insertChild(p, child);
						endMoveRows();
						isModified = true;
					}
					posList.insert(p, j);
				}
			}

			if (node->type() == NODE_FILES || node->type() == NODE_ASSEMBLIES)
				folder = NULL;

			std::sort(core()->file_manager()->_begin(), core()->file_manager()->_end(), FileManagerCompareHelper(folder, field));

			QList<InternalFile *> fileList;
			for (size_t i = 0; i < core()->file_manager()->count(); i++) {
				InternalFile *file = core()->file_manager()->item(i);
				if (file->folder() == folder)
					fileList.append(file);
			}

			int k = 0;
			QList<int> posList;
			for (int i = 0; i < node->childCount(); i++) {
				ProjectNode *child = node->child(i);
				if (child->type() == NODE_FILE_FOLDER) {
					k++;
					continue;
				}

				int j = fileList.indexOf(reinterpret_cast<InternalFile *>(child->data()));
				if (j == -1)
					continue;

				int p = posList.size();
				for (int c = 0; c < posList.size(); c++) {
					if (posList[c] > j) {
						p = c;
						break;
					}
				}

				if (k + p < i) {
					beginMoveRows(parent, i, i, parent, k + p);
					node->removeChild(child);
					node->insertChild(k + p, child);
					endMoveRows();
					isModified = true;
				}
				posList.insert(p, j);
			}
		}
		break;

#endif
	}

	if (isModified)
		emit modified();
}

/**
 * FunctionsModel
 */

FunctionsModel::FunctionsModel(QObject *parent)
	: BaseModel(parent)
{

}

void FunctionsModel::setCore(Core *core)
{
	beginResetModel();
	BaseModel::setCore(core);

	if (core)
		createFunctionsTree(root());

	endResetModel();
}

void FunctionsModel::updateFunction(IFunction *func)
{
	IArchitecture *arch = func->owner()->owner();
	MapFunctionBundle *map_func = core()->input_file()->map_function_list()->GetFunctionByAddress(arch, func->address());
	if (!map_func)
		return;
	
	ProjectNode *node = objectToNode(map_func);
	if (!node)
		return;

	node->setIcon(functionIcon(core()->input_file()->function_list()->GetFunctionByAddress(arch, func->address())));
	updateNode(node);
}

void FunctionsModel::removeFunction(IFunction *func)
{
	updateFunction(func);
}

QVariant FunctionsModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		if (section == 0)
			return QString::fromUtf8(language[lsFunctions].c_str());
	}
	return QVariant();
}

/**
 * InfoModel
 */

InfoModel::InfoModel(QObject *parent)
	: BaseModel(parent)
{

}

void InfoModel::setCore(Core *core)
{
	BaseModel::setCore(core);

	if (!core || !core->input_file()) {
		beginResetModel();
		endResetModel();
		return;
	}

	size_t i, j, k;
	QList<ProjectNode*> nodes;
	for (i = 0; i < core->input_file()->count(); i++) {
		IArchitecture *arch = core->input_file()->item(i);
		if (!arch->visible())
			continue;

		ProjectNode *node = new ProjectNode(root(), NODE_ARCHITECTURE, arch);
		node->setText(arch->name().c_str());
		node->setIcon(nodeIcon(node->type()));
		nodes.append(node);
	}

	IArchitecture *default_arch = NULL;
	if (nodes.size() == 1) {
		default_arch = reinterpret_cast<IArchitecture*>(nodes[0]->data());
		delete nodes[0];
		nodes[0] = root();
	}

	for (int f = 0; f < nodes.size(); f++) {
		ProjectNode *nodeFile = nodes[f];
		IArchitecture *file = (nodeFile->type() == NODE_ROOT) ? default_arch : reinterpret_cast<IArchitecture*>(nodeFile->data());
		assert(file);
		if (file == NULL) continue;

		if (file->command_list()->count()) {
			ProjectNode *nodeCommands = new ProjectNode(nodeFile, NODE_LOAD_COMMANDS);
			nodeCommands->setText(QString::fromUtf8(language[lsDirectories].c_str()));
			nodeCommands->setIcon(nodeIcon(nodeCommands->type()));
			for (i = 0; i < file->command_list()->count(); i++) {
				ILoadCommand *command = file->command_list()->item(i);
				if (!command->visible())
					continue;

				ProjectNode *node = new ProjectNode(nodeCommands, NODE_LOAD_COMMAND, command);
				node->setText(QString::fromUtf8(command->name().c_str()));
				node->setIcon(nodeIcon(node->type()));
				setObjectNode(command, node);
			}
		}

		ProjectNode *nodeSegments = new ProjectNode(nodeFile, NODE_SEGMENTS);
		nodeSegments->setText(QString::fromUtf8(language[lsSegments].c_str()));
		nodeSegments->setIcon(nodeIcon(nodeSegments->type()));
		for (i = 0; i < file->segment_list()->count(); i++) {
			ISection *segment = file->segment_list()->item(i);

			ProjectNode *node = new ProjectNode(nodeSegments, NODE_SEGMENT, segment);
			node->setText(QString::fromUtf8(segment->name().c_str()));
			bool has_children = false;
			for (j = 0; j < file->section_list()->count(); j++) {
				if (file->section_list()->item(j)->parent() == segment) {
					has_children = true;
					break;
				}
			}

			node->setIcon(nodeIcon(has_children ? NODE_FOLDER : node->type(), segment->excluded_from_packing() || segment->excluded_from_memory_protection()));
			setObjectNode(segment, node);
		}

		for (i = 0; i < file->section_list()->count(); i++) {
			ISection *section = file->section_list()->item(i);
			j =  file->segment_list()->IndexOf(section->parent());
			if (j == NOT_ID)
				continue;

			ProjectNode *node = new ProjectNode(nodeSegments->child((int)j), NODE_SECTION, section);
			node->setText(QString::fromUtf8(section->name().c_str()));
			node->setIcon(nodeIcon(node->type()));
		}

		if (file->import_list()->count()) {
			ProjectNode *nodeImports = new ProjectNode(nodeFile, NODE_IMPORTS, file->import_list());
			nodeImports->setText(QString::fromUtf8(language[lsImports].c_str()));
			nodeImports->setIcon(nodeIcon(nodeImports->type()));
			for (i = 0; i < file->import_list()->count(); i++) {
				IImport *import = file->import_list()->item(i);

				ProjectNode *nodeImport = new ProjectNode(nodeImports, NODE_IMPORT, import);
				nodeImport->setText(QString::fromUtf8(import->name().c_str()));
				nodeImport->setIcon(nodeIcon(nodeImport->type(), import->excluded_from_import_protection()));
				setObjectNode(import, nodeImport);

				createFunctionsTree(nodeImport);

				if (import->name().empty()) {
					QList<ProjectNode *> nodeList = nodeImport->children();
					for (int k = 0; k < nodeList.count(); k++) {
						ProjectNode *child = nodeList[k];
						nodeImport->removeChild(child);
						nodeImports->addChild(child);
					}
					delete nodeImport;
				}
			}
		}

		if (file->export_list()->count()) {
			ProjectNode *nodeExports = new ProjectNode(nodeFile, NODE_EXPORTS, file->export_list());
			nodeExports->setText(QString::fromUtf8(language[lsExports].c_str()));
			nodeExports->setIcon(nodeIcon(nodeExports->type()));

			createFunctionsTree(nodeExports);
		}

		if (file->resource_list() && file->resource_list()->count()) {
			ProjectNode *nodeResources = new ProjectNode(nodeFile, NODE_RESOURCES, file->resource_list());
			nodeResources->setText(QString::fromUtf8(language[lsResources].c_str()));
			nodeResources->setIcon(nodeIcon(nodeResources->type()));
			std::vector<IResource *> resourceList;
			for (i = 0; i < file->resource_list()->count(); i++) {
				resourceList.push_back(file->resource_list()->item(i));
			}
			for (k = 0; k < resourceList.size(); k++) {
				IResource *resource = resourceList[k];
				for (j = 0; j < resource->count(); j++) {
					resourceList.push_back(resource->item(j));
				}
			}
			for (k = 0; k < resourceList.size(); k++) {
				IResource *resource = resourceList[k];

				ProjectNode *nodeParent = objectToNode(resource->owner());
				if (!nodeParent)
					nodeParent = nodeResources;
				ProjectNode *node = new ProjectNode(nodeParent, resource->is_directory() ? NODE_RESOURCE_FOLDER : NODE_RESOURCE, resource);
				node->setText(QString::fromUtf8(resource->name().c_str()));
				node->setIcon(nodeIcon(node->type(), node->type() == NODE_RESOURCE && resource->excluded_from_packing()));
				setObjectNode(resource, node);
			}
		}

		ProjectNode *nodeCalc = new ProjectNode(nodeFile, NODE_CALC, file);
		nodeCalc->setText(QString::fromUtf8(language[lsCalculator].c_str()));
		nodeCalc->setIcon(nodeIcon(nodeCalc->type()));
		
		ProjectNode *nodeDump = new ProjectNode(nodeFile, NODE_DUMP, file);
		nodeDump->setText(QString::fromUtf8(language[lsDump].c_str()));
		nodeDump->setIcon(nodeIcon(nodeDump->type()));
		setObjectNode(file, nodeDump);
	}
}

void InfoModel::updateResource(IResource *resource)
{
	ProjectNode *node = objectToNode(resource);
	if (!node)
		return;

	node->setIcon(nodeIcon(node->type(), resource->excluded_from_packing()));
	updateNode(node);
	emit modified();
}

void InfoModel::updateSegment(ISection *segment)
{
	ProjectNode *node = objectToNode(segment);
	if (!node)
		return;

	node->setIcon(nodeIcon(node->childCount() ? NODE_FOLDER : node->type(), segment->excluded_from_packing() || segment->excluded_from_memory_protection()));
	updateNode(node);
	emit modified();
}

void InfoModel::updateImport(IImport *import)
{
	ProjectNode *node = objectToNode(import);
	if (!node)
		return;

	node->setIcon(nodeIcon(node->type(), import->excluded_from_import_protection()));
	updateNode(node);
	emit modified();
}

QVariant InfoModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		if (section == 0)
			return QString::fromUtf8(language[lsDetails].c_str());
	}
	return QVariant();
}

/**
 * SearchModel
 */

SearchModel::SearchModel(QObject *parent)
	: QAbstractItemModel(parent)
{

}

void SearchModel::clear()
{
	if (items_.count())
		removeRows(0, items_.count());
}

void SearchModel::search(ProjectNode *directory, const QString &text, bool protectedFunctionsOnly)
{
	beginResetModel();
	QList<ProjectNode *> list;
	QRegExp filter(text, Qt::CaseInsensitive, QRegExp::Wildcard);

	items_.clear();
	list.append(directory);
	for (int i = 0; i < list.count(); i++) {
		ProjectNode *node = list[i];
		for (int j = 0; j < node->childCount(); j++) {
			list.insert(i + 1 + j, node->child(j));
		}

		if (node->properties())
			list.insert(i + 1, node->properties());

		if (node->parent() && node->contains(filter)) {
			if (protectedFunctionsOnly) {
				switch (node->type()) {
				case NODE_MAP_FUNCTION:
					{
						MapFunctionBundle *map = reinterpret_cast<MapFunctionBundle*>(node->data());
						FunctionBundle *func = map->owner()->owner()->function_list()->GetFunctionByName(map->name());
						if (!func || !func->need_compile())
							continue;
					}
					break;
				default:
					continue;
				}
			}
			items_.append(node);
		}
	}

	endResetModel();
}

QModelIndex SearchModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	ProjectNode *node = items_.value(row);
	if (!node)
		return QModelIndex();

	return createIndex(row, column, node);
}

QModelIndex SearchModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

int SearchModel::rowCount(const QModelIndex &parent) const
{
	if (parent.isValid())
		return 0;

	return items_.size();
}

int SearchModel::columnCount(const QModelIndex & /*parent*/) const
{
	return 1;
}

ProjectNode *SearchModel::indexToNode(const QModelIndex &index) const 
{
	if (index.isValid())
		return static_cast<ProjectNode *>(index.internalPointer());
	return NULL;
}

QVariant SearchModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	ProjectNode *node = indexToNode(index);
	assert(node);
	if (node != NULL) {
		switch (role) {
		case Qt::DisplayRole:
			return node->text(index.column());
		case Qt::DecorationRole:
			if (index.column() == 0)
				return node->icon();
			break;
		case Qt::ToolTipRole:
			{
				QString text = node->path();
				if (!text.isEmpty())
					text += " > " + node->text(index.column());
				return text;
			}
			break;
		}
	}
	return QVariant();
}

QVariant SearchModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole)
		return QString::fromUtf8(language[lsSearchResult].c_str());
	return QVariant();
}

QModelIndex SearchModel::nodeToIndex(ProjectNode *node) const
{
	int i = items_.indexOf(node);
	if (i == -1)
		return QModelIndex();

	return createIndex(i, 0, node);
}

void SearchModel::updateNode(ProjectNode *node)
{
	int i = items_.indexOf(node);
	if (i == -1)
		return;

	QModelIndex index = createIndex(i, 0, node);
	dataChanged(index, index);
}

bool SearchModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    bool res;

    beginRemoveRows(parent, position, position + rows - 1);
    if (position < 0 || position + rows > items_.size()) {
		res = false;
	} else {
		for (int row = position; row < rows; ++row)
			items_.removeAt(position);
		res = true;
	}
	endRemoveRows();

    return res;
}

void SearchModel::removeNode(ProjectNode *node)
{
	int i = items_.indexOf(node);
	if (i == -1)
		return;

	removeRows(i, 1);
}

/**
 * DirectoryModel
 */

DirectoryModel::DirectoryModel(QObject *parent)
	: QAbstractItemModel(parent), directory_(NULL)
{

}

Qt::ItemFlags DirectoryModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags res = QAbstractItemModel::flags(index);
	if (res & Qt::ItemIsSelectable) {
		ProjectNode *node = indexToNode(index);
		if (node) {
			if (node->type() == NODE_FILE_FOLDER || node->type() == NODE_FOLDER || node->type() == NODE_FILE || node->type() == NODE_LICENSE)
				res |= Qt::ItemIsEditable;
		}
	}
	return res;
}

void DirectoryModel::setDirectory(ProjectNode *directory)
{
	beginResetModel();
	directory_ = directory;
	if (directory_) {
		items_ = directory->children();
	} else {
		items_.clear();
	}

	endResetModel();
}

QModelIndex DirectoryModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	ProjectNode *node = items_.value(row);
	if (!node)
		return QModelIndex();

	return createIndex(row, column, node);
}

QModelIndex DirectoryModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

int DirectoryModel::rowCount(const QModelIndex &parent) const
{
	if (parent.isValid())
		return 0;

	return items_.size();
}

QStringList headerLabels(NodeType type)
{
	QStringList res;
	switch (type) {
	case NODE_FUNCTIONS:
	case NODE_FOLDER:
	case NODE_MAP_FOLDER:
		res << QString::fromUtf8(language[lsName].c_str()) << QString::fromUtf8(language[lsAddress].c_str()) <<  QString::fromUtf8(language[lsProtection].c_str());
		break;
	case NODE_RESOURCES:
	case NODE_RESOURCE_FOLDER:
	case NODE_LOAD_COMMANDS:
		res << QString::fromUtf8(language[lsName].c_str()) << QString::fromUtf8(language[lsAddress].c_str()) << QString::fromUtf8(language[lsSize].c_str());
		break;
	case NODE_SEGMENTS:
	case NODE_SEGMENT:
		res << QString::fromUtf8(language[lsName].c_str()) << QString::fromUtf8(language[lsAddress].c_str()) << QString::fromUtf8(language[lsSize].c_str()) << QString::fromUtf8(language[lsRawAddress].c_str()) << QString::fromUtf8(language[lsRawSize].c_str()) << QString::fromUtf8(language[lsFlags].c_str());
		break;
	case NODE_IMPORT:
	case NODE_IMPORT_FOLDER:
	case NODE_EXPORTS:
	case NODE_EXPORT_FOLDER:
		res << QString::fromUtf8(language[lsName].c_str()) << QString::fromUtf8(language[lsAddress].c_str());
		break;
	case NODE_LICENSES:
		res << QString::fromUtf8(language[lsCustomerName].c_str()) << QString::fromUtf8(language[lsEmail].c_str()) << QString::fromUtf8(language[lsDate].c_str());
		break;
	case NODE_FILES:
	case NODE_FILE_FOLDER:
	case NODE_ASSEMBLIES:
		res << QString::fromUtf8(language[lsName].c_str()) << QString::fromUtf8(language[lsFileName].c_str());
		break;
	default:
		res << QString::fromUtf8(language[lsName].c_str());
		break;
	}
	return res;
};

int DirectoryModel::columnCount(const QModelIndex & /*parent*/) const
{
	if (!directory_)
		return 0;

	return headerLabels(directory_->type()).size();
}

ProjectNode *DirectoryModel::indexToNode(const QModelIndex &index) const 
{
	if (index.isValid())
		return static_cast<ProjectNode *>(index.internalPointer());
	return NULL;
}

QVariant DirectoryModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	ProjectNode *node = indexToNode(index);
	if (node != NULL) {
		if (role == Qt::DisplayRole) {
			return node->text(index.column());
		} else if (role == Qt::DecorationRole) {
			if (index.column() == 0)
				return node->icon();
		}
	}
	return QVariant();
}

QVariant DirectoryModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (!directory_)
		return QVariant();

	if (role == Qt::DisplayRole) {
		return headerLabels(directory_->type()).value(section);
	}
	return QVariant();
}

QModelIndex DirectoryModel::nodeToIndex(ProjectNode *node) const
{
	int i = items_.indexOf(node);
	if (i == -1)
		return QModelIndex();

	return createIndex(i, 0, node);
}

void DirectoryModel::updateNode(ProjectNode *node)
{
	int i = items_.indexOf(node);
	if (i == -1)
		return;

	QModelIndex index = createIndex(i, 0, node);
	dataChanged(index, index);
}

bool DirectoryModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    bool res;

    beginRemoveRows(parent, position, position + rows - 1);
    if (position < 0 || position + rows > items_.size()) {
		res = false;
	} else {
		for (int row = 0; row < rows; ++row)
			items_.removeAt(position);
		res = true;
	}
	endRemoveRows();

    return res;
}

void DirectoryModel::removeNode(ProjectNode *node)
{
	ProjectNode *parent = directory_;
	while (parent) {
		if (parent == node) {
			setDirectory(NULL);
			return;
		}
		parent = parent->parent();
	}

	int i = items_.indexOf(node);
	if (i == -1)
		return;

	removeRows(i, 1);
}

/**
 * MapFunctionBundleListModel
 */

MapFunctionBundleListModel::MapFunctionBundleListModel(IFile &file, bool codeOnly, QObject *parent)
	: QAbstractItemModel(parent)
{
	MapFunctionBundleList *map_function_list = file.map_function_list();
	for (size_t i = 0; i < map_function_list->count(); i++) {
		MapFunctionBundle *func = map_function_list->item(i);
		switch (func->type()) {
		case otCode:
		case otExport:
		case otMarker:
		case otAPIMarker:
		case otString:
			break;
		case otUnknown:
			continue;
		default:
			if (codeOnly)
				continue;
			break;
		}

		mapFunctionList_.append(func);
	}

	items_ = mapFunctionList_;
}

int MapFunctionBundleListModel::rowCount(const QModelIndex &parent) const
{
	if (!parent.isValid())
		return items_.size();

	return 0;
}

int MapFunctionBundleListModel::columnCount(const QModelIndex &parent) const
{
	Q_UNUSED(parent);
	return 2;
}

QModelIndex MapFunctionBundleListModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	MapFunctionBundle *bundle = items_.value(row);
	if (!bundle)
		return QModelIndex();

	return createIndex(row, column, bundle);
}

QModelIndex MapFunctionBundleListModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

QVariant MapFunctionBundleListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

	MapFunctionBundle *func = items_.at(index.row());

	if (role == Qt::DisplayRole) {
		if (index.column() == 0) {
			return QString::fromUtf8(func->display_name().c_str());
		} else if (index.column() == 1) {
			return QString::fromLatin1(func->display_address().c_str());
		}
	} else if (role == Qt::DecorationRole)	{
		if (index.column() == 0) {
			switch (func->type()) {
			case otImport:
				return nodeIcon(NODE_IMPORT_FUNCTION);
			default:
				return functionIcon(func->owner()->owner()->function_list()->GetFunctionByName(func->name()));
			}
		}
	}
	return QVariant();
}

QVariant MapFunctionBundleListModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    switch (section) {
    case 0:
        return QString::fromUtf8(language[lsName].c_str());
    case 1:
        return QString::fromUtf8(language[lsAddress].c_str());
    }
    return QVariant();
}

void MapFunctionBundleListModel::search(const QString &text)
{
	beginResetModel();
	if (text.isEmpty()) {
		items_ = mapFunctionList_;
	} else {
		items_.clear();
		QRegExp filter(text, Qt::CaseInsensitive, QRegExp::Wildcard);

		for (int i = 0; i < mapFunctionList_.size(); i++) {
			MapFunctionBundle *func = mapFunctionList_[i];
			QString name = QString::fromUtf8(func->name().c_str());
			if (name.contains(filter))
				items_.append(func);
		}
	}

	endResetModel();
}

struct MapFunctionBundleListCompareHelper {
	int column;
	MapFunctionBundleListCompareHelper(int _column) : column(_column) {}
	bool operator () (const MapFunctionBundle *func1, const MapFunctionBundle *func2) const
	{
		switch (column) {
		case 0:
			return QString::fromUtf8(func1->display_name().c_str()).compare(QString::fromUtf8(func2->display_name().c_str()), Qt::CaseInsensitive) < 0;
		case 1:
			return func1->display_address() < func2->display_address();
		}
		return false;
	}
};

void MapFunctionBundleListModel::sort(int column, Qt::SortOrder order)
{
	beginResetModel();
	qSort(mapFunctionList_.begin(), mapFunctionList_.end(), MapFunctionBundleListCompareHelper(column));
	if (items_.size() == mapFunctionList_.size())
		items_ = mapFunctionList_;
	else
		qSort(items_.begin(), items_.end(), MapFunctionBundleListCompareHelper(column));
	endResetModel();
}

/**
 * MapFunctionListModel
 */

MapFunctionListModel::MapFunctionListModel(IArchitecture &file, bool codeOnly, QObject *parent)
	: QAbstractItemModel(parent)
{
	MapFunctionList *map_function_list = file.map_function_list();
	for (size_t i = 0; i < map_function_list->count(); i++) {
		MapFunction *func = map_function_list->item(i);
		switch (func->type()) {
		case otCode:
		case otExport:
		case otMarker:
		case otAPIMarker:
		case otString:
			break;
		case otUnknown:
			continue;
		default:
			if (codeOnly)
				continue;
			break;
		}

		mapFunctionList_.append(func);
	}

	items_ = mapFunctionList_;
}

int MapFunctionListModel::rowCount(const QModelIndex &parent) const
{
	if (!parent.isValid())
		return items_.size();

	return 0;
}

int MapFunctionListModel::columnCount(const QModelIndex &parent) const
{
	Q_UNUSED(parent);
	return 2;
}

QModelIndex MapFunctionListModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	MapFunction *func = items_.value(row);
	if (!func)
		return QModelIndex();

	return createIndex(row, column, func);
}

QModelIndex MapFunctionListModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

QVariant MapFunctionListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

	MapFunction *func = items_.at(index.row());

	if (role == Qt::DisplayRole) {
		if (index.column() == 0) {
			return QString::fromUtf8(func->display_name().c_str());
		} else if (index.column() == 1) {
			return QString::fromLatin1(func->display_address("").c_str());
		}
	} else if (role == Qt::DecorationRole)	{
		if (index.column() == 0) {
			switch (func->type()) {
			case otImport:
				return nodeIcon(NODE_IMPORT_FUNCTION);
			default:
				return functionIcon(func->owner()->owner()->function_list()->GetFunctionByAddress(func->address()));
			}
		}
	}
	return QVariant();
}

QVariant MapFunctionListModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    switch (section) {
    case 0:
        return QString::fromUtf8(language[lsName].c_str());
    case 1:
        return QString::fromUtf8(language[lsAddress].c_str());
    }
    return QVariant();
}

void MapFunctionListModel::search(const QString &text)
{
	beginResetModel();
	if (text.isEmpty()) {
		items_ = mapFunctionList_;
	} else {
		items_.clear();
		QRegExp filter(text, Qt::CaseInsensitive, QRegExp::Wildcard);

		for (int i = 0; i < mapFunctionList_.size(); i++) {
			MapFunction *func = mapFunctionList_[i];
			QString name = QString::fromUtf8(func->name().c_str());
			if (name.contains(filter))
				items_.append(func);
		}
	}

	endResetModel();
}

struct MapFunctionListCompareHelper {
	int column;
	MapFunctionListCompareHelper(int _column) : column(_column) {}
	bool operator () (const MapFunction *func1, const MapFunction *func2) const
	{
		switch (column) {
		case 0:
			return QString::fromUtf8(func1->display_name().c_str()).compare(QString::fromUtf8(func2->display_name().c_str()), Qt::CaseInsensitive) < 0;
		case 1:
			return func1->display_address("") < func2->display_address("");
		}
		return false;
	}
};

void MapFunctionListModel::sort(int column, Qt::SortOrder order)
{
	beginResetModel();
	qSort(mapFunctionList_.begin(), mapFunctionList_.end(), MapFunctionListCompareHelper(column));
	if (items_.size() == mapFunctionList_.size())
		items_ = mapFunctionList_;
	else
		qSort(items_.begin(), items_.end(), MapFunctionListCompareHelper(column));
	endResetModel();
}

/**
 * WatermarksModel
 */

WatermarksModel::WatermarksModel(QObject *parent)
	: BaseModel(parent)
{

}

QVariant WatermarksModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		if (section == 0)
			return QString::fromUtf8(language[lsName].c_str());
	}
	return QVariant();
}

Qt::ItemFlags WatermarksModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags res = QAbstractItemModel::flags(index);
	if (res & Qt::ItemIsSelectable) {
		ProjectNode *node = indexToNode(index);
		if (node) {
			if (node->type() == NODE_WATERMARK)
				res |= Qt::ItemIsEditable;
		}
	}
	return res;
}

ProjectNode *WatermarksModel::internalAddWatermark(Watermark *watermark)
{
	beginInsertRows(nodeToIndex(NULL), root()->childCount(), root()->childCount());
	ProjectNode *node = new ProjectNode(root(), NODE_WATERMARK, (void *)watermark);
	endInsertRows();

	setObjectNode(watermark, node);
	internalUpdateWatermark(watermark);
	return node;
}

ProjectNode *WatermarksModel::internalUpdateWatermark(Watermark *watermark)
{
	ProjectNode *node = objectToNode(watermark);
	if (!node)
		return NULL;

	node->setText(QString::fromUtf8(watermark->name().c_str()));
	node->setIcon(nodeIcon(node->type(), !watermark->enabled()));
	return node;
}

void WatermarksModel::setCore(Core *core)
{
	beginResetModel();
	BaseModel::setCore(core);

	if (core) {
		WatermarkManager *manager = core->watermark_manager();
		for (size_t i = 0; i < manager->count(); i++) {
			internalAddWatermark(manager->item(i));
		}
	}
	endResetModel();
}

void WatermarksModel::addWatermark(Watermark *watermark)
{
	updateNode(internalAddWatermark(watermark));
}

void WatermarksModel::updateWatermark(Watermark *watermark)
{
	updateNode(internalUpdateWatermark(watermark));
}

void WatermarksModel::removeWatermark(Watermark *watermark)
{
	removeNode(watermark);
}

QModelIndex WatermarksModel::indexByName(const QString &watermarkName) const
{
	for (int i = 0; i < root()->childCount(); i++) {
		ProjectNode *node = root()->child(i);
		if (node->text() == watermarkName)
			return nodeToIndex(node);
	}

	return QModelIndex();
}

WatermarkManager * WatermarksModel::manager() const
{
	return core() ? core()->watermark_manager() : NULL;
}

/**
 * WatermarkScanModel
 */

WatermarkScanModel::WatermarkScanModel(QObject *parent)
	: QAbstractItemModel(parent)
{

}

QModelIndex WatermarkScanModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	Watermark *watermark = items_.keys().value(row);
	if (!watermark)
		return QModelIndex();

	return createIndex(row, column, watermark);
}

QModelIndex WatermarkScanModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

int WatermarkScanModel::rowCount(const QModelIndex &parent) const
{
	if (parent.isValid())
		return 0;

	return items_.size();
}

int WatermarkScanModel::columnCount(const QModelIndex & /*parent*/) const
{
	return 2;
}

Watermark *WatermarkScanModel::indexToWatermark(const QModelIndex &index) const 
{
	if (index.isValid())
		return static_cast<Watermark *>(index.internalPointer());
	return NULL;
}

QVariant WatermarkScanModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	Watermark *watermark = indexToWatermark(index);
	assert(watermark);
	if (watermark != NULL ) {
		if (role == Qt::DisplayRole) {
			if (index.column() == 0) {
				return QString::fromUtf8(watermark->name().c_str());
			} else if (index.column() == 1) {
				return QString::number(items_[watermark]);
			}
		} else if (role == Qt::DecorationRole) {
			if (index.column() == 0)
				return watermark->enabled() ? QIcon(":/images/item_watermark.png") : QIcon(":/images/item_watermark_black.png");
		}
	}
	return QVariant();
}

QVariant WatermarkScanModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		if (section == 0) {
			return QString::fromUtf8(language[lsName].c_str());
		} else if (section == 1) {
			return QString::fromUtf8(language[lsCount].c_str());
		}
	}
	return QVariant();
}

void WatermarkScanModel::setWatermarkData(std::map<Watermark *, size_t> data)
{
	beginResetModel();
	items_.clear();
	for (std::map<Watermark *, size_t>::const_iterator it = data.begin(); it != data.end(); it++) {
		items_[it->first] = it->second;
	}

	endResetModel();
}

void WatermarkScanModel::removeWatermark(Watermark *watermark)
{
	int position = items_.keys().indexOf(watermark);
	if (position == -1)
		return;

	beginRemoveRows(QModelIndex(), position, position);
	items_.remove(watermark);
	endRemoveRows();
}

void WatermarkScanModel::clear()
{
	if (items_.count())
		removeRows(0, items_.count());
}

/**
 * TemplatesModel
 */

TemplatesModel::TemplatesModel(QObject *parent)
	: BaseModel(parent)
{

}

QVariant TemplatesModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		if (section == 0)
			return QString::fromUtf8(language[lsName].c_str());
	}
	return QVariant();
}

Qt::ItemFlags TemplatesModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags res = QAbstractItemModel::flags(index);
	if (res & Qt::ItemIsSelectable) {
		if (index.row() != 0) 
			res |= Qt::ItemIsEditable;
	}
	return res;
}

QVariant TemplatesModel::data(const QModelIndex &index, int role) const
{
	if (role == Qt::DisplayRole && index.row() == 0)
		return "(" + QString::fromUtf8(language[lsDefault].c_str()) + ")";
	return BaseModel::data(index, role);
}

void TemplatesModel::setCore(Core *core)
{
	beginResetModel();
	BaseModel::setCore(core);

	if (core) {
		ProjectTemplateManager *manager = core->template_manager();
		for (size_t i = 0; i < manager->count(); i++) {
			ProjectTemplate *pt = manager->item(i);
			internalAddTemplate(pt);
		}
	}
	endResetModel();
}

void TemplatesModel::updateTemplate(ProjectTemplate *pt)
{
	updateNode(internalUpdateTemplate(pt));
}

void TemplatesModel::removeTemplate(ProjectTemplate *pt)
{
	removeNode(pt);
}

ProjectNode * TemplatesModel::internalUpdateTemplate(ProjectTemplate *pt)
{
	ProjectNode *node = objectToNode(pt);
	if (!node)
		return NULL;

	node->setText(QString::fromUtf8(pt->name().c_str()));
	node->setIcon(nodeIcon(node->type()));
	return node;
}

ProjectNode * TemplatesModel::internalAddTemplate(ProjectTemplate *pt)
{
	beginInsertRows(nodeToIndex(NULL), root()->childCount(), root()->childCount());
	ProjectNode *node = new ProjectNode(root(), NODE_TEMPLATE, (void *)pt);
	endInsertRows();

	setObjectNode(pt, node);
	internalUpdateTemplate(pt);
	return node;
}

void TemplatesModel::addTemplate(ProjectTemplate * pt)
{
	updateNode(internalAddTemplate(pt));
}

/**
 * LogModel
 */

LogModel::LogModel(QObject *parent)
	: BaseModel(parent)
{

}

void LogModel::addMessage(MessageType type, IObject *sender, const QString &text)
{
	NodeType node_type;
	QString icon_name;
	if (type == mtWarning) {
		node_type = NODE_WARNING;
		icon_name = ":/images/warning.png";
	} else if (type == mtError) {
		node_type = NODE_ERROR;
		icon_name = ":/images/error.png";
	} else
		node_type = NODE_MESSAGE;

	ProjectNode *node;
	beginInsertRows(QModelIndex(), root()->childCount(), root()->childCount());
	node = new ProjectNode(root(), node_type, sender);
	endInsertRows();

	if (sender)
		setObjectNode(sender, node);

	node->setText(text);
	if (!icon_name.isEmpty())
		node->setIcon(QIcon(icon_name));
}

QVariant LogModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		return QString::fromUtf8(language[lsCompilationLog].c_str());
	}
	return QVariant();
}

void LogModel::clear()
{
	beginRemoveRows(QModelIndex(), 0, root()->childCount());
	BaseModel::clear();
	endRemoveRows();
}

void LogModel::removeObject(void *object)
{
	removeNode(object);
}

/**
 * ProjectTreeDelegate
 */

ProjectTreeDelegate::ProjectTreeDelegate(QObject *parent)
	: TreeViewItemDelegate(parent)
{

}

QWidget *ProjectTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const
{
	QLineEdit *editor = new LineEdit(parent);
	editor->setObjectName("editor");
	editor->setFrame(false);

	return editor;
}

void ProjectTreeDelegate::setModelData(QWidget *editor, QAbstractItemModel * /*model*/, const QModelIndex &index) const
{
	ProjectNode *node = static_cast<ProjectNode *>(index.internalPointer());
	if (!node)
		return;

	auto le = qobject_cast<QLineEdit *>(editor);
	assert(le);
	if (!le)
		return;

	QString text = le->text();

	switch (node->type()) {
	case NODE_FOLDER:
		static_cast<Folder *>(node->data())->set_name(text.toUtf8().constData());
		break;
#ifdef ULTIMATE
	case NODE_LICENSE:
		static_cast<License *>(node->data())->set_customer_name(text.toUtf8().constData());
		break;
	case NODE_FILE_FOLDER:
		static_cast<FileFolder *>(node->data())->set_name(text.toUtf8().constData());
		break;
	case NODE_FILE:
		static_cast<InternalFile *>(node->data())->set_name(text.toUtf8().constData());
		break;
#endif
	case NODE_WATERMARK:
		static_cast<Watermark *>(node->data())->set_name(text.toUtf8().constData());
		break;
	}
}

/**
 * WatermarksTreeDelegate
 */

WatermarksTreeDelegate::WatermarksTreeDelegate(QObject *parent)
	: TreeViewItemDelegate(parent)
{

}

QWidget *WatermarksTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const
{
	QLineEdit *editor = new LineEdit(parent);
	editor->setObjectName("editor");
	editor->setFrame(false);

	return editor;
}

void WatermarksTreeDelegate::setModelData(QWidget *editor, QAbstractItemModel * /*model*/, const QModelIndex &index) const
{
	ProjectNode *node = static_cast<ProjectNode *>(index.internalPointer());
	if (!node)
		return;

	QString text = qobject_cast<QLineEdit *>(editor)->text();

	switch (node->type()) {
	case NODE_WATERMARK:
		static_cast<Watermark *>(node->data())->set_name(text.toUtf8().constData());
		break;
	}
}

/**
 * TemplatesTreeDelegate
 */

TemplatesTreeDelegate::TemplatesTreeDelegate(QObject *parent)
	: TreeViewItemDelegate(parent)
{

}

QWidget *TemplatesTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const
{
	QLineEdit *editor = new LineEdit(parent);
	editor->setObjectName("editor");
	editor->setFrame(false);

	return editor;
}

void TemplatesTreeDelegate::setModelData(QWidget *editor, QAbstractItemModel * /*model*/, const QModelIndex &index) const
{
	ProjectNode *node = static_cast<ProjectNode *>(index.internalPointer());
	if (!node)
		return;

	QString text = qobject_cast<QLineEdit *>(editor)->text();
	if (text.isEmpty())
	{
		//FIXME i18n
		MessageDialog::warning(editor->parentWidget(), "Please provide meaningful name", QMessageBox::Ok);
		return;
	}
	switch (node->type()) {
	case NODE_TEMPLATE:
		static_cast<ProjectTemplate *>(node->data())->set_name(text.toUtf8().constData());
		break;
	}
}

/**
 * DumpModel
 */

DumpModel::DumpModel(QObject *parent)
	: QAbstractItemModel(parent), file_(NULL), rowCountCache_(0), cacheAddress_(0)
{

}

QModelIndex DumpModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	return createIndex(row, column);
}

QModelIndex DumpModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

int DumpModel::rowCount(const QModelIndex &parent) const
{
	if (parent.isValid())
		return 0;

	if(rowCountCache_ == 0) {
		QMapIterator<uint64_t, int> i(addrsToRows_);
		while (i.hasNext()) {
			i.next();
			rowCountCache_ += i.value();
		}
	}
	return rowCountCache_;
}

int DumpModel::columnCount(const QModelIndex & /*parent*/) const
{
	return 3;
}

void DumpModel::setFile(IArchitecture *file) 
{
	if (file_ == file)
		return;

	beginResetModel();
	file_ = file;
	rowCountCache_ = 0;
	addrsToRows_.clear();
	cacheAddress_ = 0;
	cache_.clear();

	if (file_) {
		size_t i;
		ISection *segment;
		std::vector<ISection *> segmentList;
		std::string formatName = file->owner()->format_name();

		if (formatName == "PE") {
			segment = file->segment_list()->GetSectionByAddress(file->image_base());
			if (segment)
				segmentList.push_back(segment);
		}

		for (i = 0; i < file->segment_list()->count(); i++) {
			segment = file->segment_list()->item(i);
			if (segment->memory_type() == mtNone || segment->size() == 0)
				continue;

			if (formatName == "ELF" && segment->name() != "PT_LOAD")
				continue;

			segmentList.push_back(segment);
		}

		uint64_t curAddr;
		int curRows = 0;
		for (i = 0; i < segmentList.size(); i++) {
			segment = segment = segmentList[i];
			uint64_t address = segment->address();
			uint32_t rows = (uint32_t)(segment->size() + 15) / 16;

			if(curRows == 0) {
				curAddr = address;
				curRows = rows;
				continue;
			} 
			uint64_t curEndAddr = curAddr + 16 * curRows;
			if(curEndAddr < address) { //gap
				addrsToRows_.insert(curAddr, curRows);
				curAddr = address;
				curRows = rows;
				continue;
			}
			uint64_t nextEndAddr = address + 16 * rows;
			if(nextEndAddr > curEndAddr) curRows = (nextEndAddr - curAddr) / 16;
		}
		if (curRows) addrsToRows_.insert(curAddr, curRows);
	}
	endResetModel();
}

QByteArray DumpModel::read(uint64_t address, int size) const
{
	if (!cacheAddress_ || address < cacheAddress_ || address + size > cacheAddress_ + cache_.size()) {
		if (file_->AddressSeek(address)) {
			ISection *segment = file_->selected_segment();
			cacheAddress_ = address;
			int cacheSize = (int)qMin(segment->physical_size(), (uint32_t)segment->size()) - (address - segment->address());
			if (cacheSize > 0x100000 && cacheSize > size) // 1Mbyte read ahead limit
				cacheSize = 0x100000;
			if (cacheSize) {
				cache_.resize(cacheSize);
				file_->Read(cache_.data(), cacheSize);
			} else {
				cache_.clear();
			}
		} else {
			return QByteArray();
		}
	}

	uint64_t offset = address - cacheAddress_;
	return cache_.mid(offset, qMin(cache_.size() - (int)offset, size));
}

QVariant DumpModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	if (role == Qt::DisplayRole) {
		uint64_t address = indexToAddress(index);
		switch (index.column()) {
		case 0:
			{
				QString res;
				ISection *segment = file_->segment_list()->GetSectionByAddress(address);
				if (segment)
					res = QString::fromLatin1(segment->name().c_str()).append(':');
				res.append(QString::fromUtf8(DisplayValue(file_->cpu_address_size(), address).c_str()));
				return res;
			}
			
		case 1:
			{
				QByteArray data = read(address, 0x10);
				unsigned char res[16*3] = "?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??";
				static char hex[] = "0123456789ABCDEF";
				for (int i = 0; i < 16; i++) {
					int value = -1;
					if (i < data.size()) {
						value = (unsigned char)data[i];
					} else {
						data = read(address + i, 1);
						if(data.size()) {
							value = (unsigned char)data[0];
						}
					}
					if (value >= 0) {
						res[3 * i] = hex[value >> 4];
						res[3 * i + 1] = hex[value & 0x0f];
					}
				}
				return QString((char *)res);
		}

		case 2:
			{
				QByteArray data = read(address, 0x10);
				char res[17], *ptr = res;
				for (int i = 0; i < 16; i++) {
					if (i < data.size()) {
						unsigned char value = data[i];
						if (value < 32 || value > 127) value = 0xB7;
						*ptr++ = value;
					} else {
						data = read(address + i, 1);
						if (data.size()) {
							unsigned char value = data[0];
							if (value < 32 || value > 127) value = 0xB7;
							*ptr++ = value;
						}
						else {
							*ptr++ = '?';
						}
					}
				}
				*ptr = 0;
				return QString::fromLatin1((char *)res);
			}
		}
	} else if (role == Qt::FontRole) {
		QFont font(MONOSPACE_FONT_FAMILY);
		return font;
	}

	return QVariant();
}

QVariant DumpModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		switch (section) {
		case 0:
			return QString::fromUtf8(language[lsAddress].c_str());
		case 1:
			return QString::fromUtf8(language[lsDump].c_str());
		case 2:
			return QString::fromUtf8(language[lsValue].c_str());
		}
	}
	return QVariant();
}

QModelIndex DumpModel::addressToIndex(uint64_t address)
{
	QMapIterator<uint64_t, int> i(addrsToRows_);
	int rows = 0;
	while (i.hasNext()) {
		i.next();
		if(address >= i.key() && i.value() > (int)((address - i.key()) / 16))
			return createIndex(rows + (int)((address - i.key()) / 16), 0);
		rows += i.value();
	}
	return QModelIndex();
}

uint64_t DumpModel::indexToAddress(const QModelIndex &index) const
{
	QMapIterator<uint64_t, int> i(addrsToRows_);
	int rows = 0;
	while (i.hasNext()) {
		i.next();
		if (rows + i.value() > index.row())
			return i.key() + (index.row() - rows) * 16;
		rows += i.value();
	}
	assert(0);
	return 0;
}

/**
 * DisasmModel
 */

DisasmModel::DisasmModel(QObject *parent)
	: QAbstractItemModel(parent), file_(NULL), rowCountCache_(0), func_(NULL)
{

}

QModelIndex DisasmModel::index(int row, int column, const QModelIndex &parent) const
{
	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	return createIndex(row, column);
}

QModelIndex DisasmModel::parent(const QModelIndex & /*index*/) const
{
	return QModelIndex();
}

int DisasmModel::rowCount(const QModelIndex &parent) const
{
	if (parent.isValid())
		return 0;

	if (rowCountCache_ == 0) {
		QMapIterator<uint64_t, int> i(addrsToRows_);
		while (i.hasNext()) {
			i.next();
			rowCountCache_ += i.value();
		}
	}
	return rowCountCache_;
}

int DisasmModel::columnCount(const QModelIndex & /*parent*/) const
{
	return 3;
}

QModelIndex DisasmModel::addressToIndex(uint64_t address)
{
	int row = addressOffset(address);
	if (row >= 0)
	{
		QModelIndex index = createIndex(row, 0);
		if (func_->count()) {
			QModelIndex end = createIndex(index.row() + (int)func_->count(), 2);
			func_->clear();
			dataChanged(index, end);
		}
		return index;
	}
	return QModelIndex();
}

int DisasmModel::addressOffset(uint64_t address) const
{
	QMapIterator<uint64_t, int> i(addrsToRows_);
	int ret = 0;
	while (i.hasNext()) {
		i.next();
		if (address >= i.key() && i.value() > (int)(address - i.key()))
		{
			return ret + (int)(address - i.key());
		}
		ret += i.value();
	}
	return -1;
}

uint64_t DisasmModel::offsetToAddress(int offset) const
{
	QMapIterator<uint64_t, int> i(addrsToRows_);
	while (i.hasNext()) {
		i.next();
		if (i.value() > offset)
		{
			return i.key() + offset;
		}
		offset -= i.value();
	}
	return 0;
}

ICommand *DisasmModel::indexToCommand(const QModelIndex &index) const
{
	if (func_->count()) {
		uint64_t address = func_->item(0)->address();
		int row = addressOffset(address);
		if (row >= 0 && row <= index.row()) {
			size_t commandIndex = index.row() - row;
			if (commandIndex < func_->count())
				return func_->item(commandIndex);
		}
	}
	return NULL;
}

void DisasmModel::setFile(IArchitecture *file) 
{
	if (file_ == file)
		return;

	beginResetModel();
	file_ = file;
	rowCountCache_ = 0;
	addrsToRows_.clear();
	delete func_;
	func_ = NULL;

	if (file_) {
		size_t i;
		ISection *segment;
		std::vector<ISection *> segmentList;
		std::string formatName = file->owner()->format_name();

		if (formatName == "PE" && file->AddressSeek(file->image_base()))
			segmentList.push_back(file->selected_segment());

		for (i = 0; i < file->segment_list()->count(); i++) {
			segment = file->segment_list()->item(i);
			if (segment->memory_type() == mtNone || segment->size() == 0)
				continue;

			if (formatName == "ELF" && segment->name() != "PT_LOAD")
				continue;

			segmentList.push_back(segment);
		}

		uint64_t curAddr;
		int curRows = 0;
		for (i = 0; i < segmentList.size(); i++) {
			segment = segment = segmentList[i];
			uint64_t address = segment->address();
			uint32_t rows = (uint32_t)segment->size();

			if (curRows == 0) {
				curAddr = address;
				curRows = rows;
				continue;
			}
			uint64_t curEndAddr = curAddr + curRows;
			if (curEndAddr < address) { //gap
				addrsToRows_.insert(curAddr, curRows);
				curAddr = address;
				curRows = rows;
				continue;
			}
			uint64_t nextEndAddr = address + rows;
			if (nextEndAddr > curEndAddr) curRows = nextEndAddr - curAddr;
		}
		if (curRows) addrsToRows_.insert(curAddr, curRows);
		func_ = file->function_list()->CreateFunction(file->cpu_address_size());
	}
	endResetModel();
}

QVariant DisasmModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	switch (role) {
	case Qt::DisplayRole:
	case Qt::Vmp::StaticTextRole:
	case Qt::Vmp::StaticColorRole: 
		{
			if (func_->count() > 100000)
				func_->clear();

			uint64_t address = offsetToAddress(index.row());
			ICommand *command = NULL;
			if (func_->count()) {
				int startIndex = addressOffset(func_->item(0)->address());
				if (startIndex >= 0 && startIndex <= index.row()) {
					size_t commandIndex = index.row() - startIndex;
					if (commandIndex < func_->count())
						command = func_->item(commandIndex);
					else if (commandIndex == func_->count())
						address = func_->item(func_->count() - 1)->next_address();
					else
						func_->clear();
				} else {
					func_->clear();
				}
			}

			if (!command)
				command = func_->ParseCommand(*file_, address, true);

			if (role == Qt::DisplayRole) {
				switch (index.column()) {
				case 0: 
					{
						QString res;
						ISection *segment = file_->segment_list()->GetSectionByAddress(address - address % file_->segment_alignment());
						if (segment)
							res = QString::fromLatin1(segment->name().c_str()).append(':');
						return res.append(QString::fromLatin1(command->display_address().c_str()));
					}
				case 1:
					return QString::fromLatin1(command->dump_str().c_str());
				case 2:
					return QString::fromUtf8(command->text().c_str());
				}
			} else if (role == Qt::Vmp::StaticTextRole) {
				if (index.column() == 2 && !command->comment().value.empty())
					return QString::fromUtf8(command->comment().display_value().c_str());
			} else if (role == Qt::Vmp::StaticColorRole) {
				if (index.column() == 2 && !command->comment().value.empty()) {
					switch (command->comment().type) {
					case ttFunction: case ttJmp:
						return QColor(Qt::blue);
					case ttImport:
						return QColor(Qt::darkRed);
					case ttExport:
						return QColor(Qt::red);
					case ttString:
						return QColor(Qt::darkGreen);
					case ttVariable:
						return QColor(Qt::magenta);
					case ttComment:
						return QColor(Qt::gray);
					default:
						return QColor();
					}
				}
			}
		}
		break;
	case Qt::FontRole: {
			return QFont(MONOSPACE_FONT_FAMILY);
		}
	}

	return QVariant();
}

QVariant DisasmModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
	if (role == Qt::DisplayRole) {
		switch (section) {
		case 0:
			return QString::fromUtf8(language[lsAddress].c_str());
		case 1:
			return QString::fromUtf8(language[lsDump].c_str());
		case 2:
			return QString::fromUtf8(language[lsCode].c_str());
		}
	}
	return QVariant();
}

/**
 * ProjectNode
 */

bool ItemModelSearcher::extractMatchingIndexes(const QModelIndex &parent)
{
	//TODO: âîçìîæíû äàëüíåéøèå îïòèìèçàöèè
	if (current_match_ == NULL)
		current_match_ = &match_before_;

	int rows = where_->rowCount(parent);
	for(int i = 0; i < rows; ++i)
	{
		QModelIndex idx0;
		for(int col = 0; col < where_->columnCount(parent); col++)
		{
			QModelIndex idx = where_->index(i, col, parent);
			if(idx.isValid())
			{
				if(col == 0)
					idx0 = idx;
				if(from_ == idx)
				{
					assert(current_match_ == &match_before_);
					if (match_before_.isValid() && !forward_)
						return true;
					if (!forward_ && !incremental_ && !match_before_.isValid())
						return false;
					current_match_ = &match_after_;
				} else
				{
					bool matched = (idx.data(Qt::DisplayRole).toString() + idx.data(Qt::Vmp::StaticTextRole).toString()).contains(*term_);
					if (matched)
					{
						*current_match_ = idx;
						if (incremental_ && current_match_ == &match_before_ && from_.isValid() && !match_after_.isValid())
							match_after_ = idx; //store wrapped result
						if (from_.isValid() && current_match_ == &match_after_ && forward_)
							return true;
						if (!from_.isValid() && forward_)
							return true;
					}
				}
			}
		}
		if (extractMatchingIndexes(idx0))
			return true;
	}
	return false;
}

QModelIndex ItemModelSearcher::find()
{
	assert(incremental_ == false || forward_ == true); //other modes unsupported
	if(incremental_ == false || forward_ == true)
	{
		bool from_matched = from_.isValid() && (from_.data(Qt::DisplayRole).toString() + from_.data(Qt::Vmp::StaticTextRole).toString()).contains(*term_);
		if(from_matched && incremental_)
			return from_;

		if (extractMatchingIndexes(QModelIndex()))
		{
			assert(current_match_ && current_match_->isValid());
			return *current_match_;
		}
		// 1. from_ invalid backward
		if (!from_.isValid() && !forward_ && match_before_.isValid())
		{
			return match_before_;
		}

		// 2. from_ valid incremental_ wrapped
		if (from_.isValid() && incremental_ && match_after_.isValid())
		{
			return match_after_;
		}
	}
	return QModelIndex();
}
