#include "genericfieldseditor.h"
#include "genericfieldedit.h"
#include "genericfieldeditfactory.h"
#include "generictext.h"
#include "emphasistext.h"

#include <QHBoxLayout>
#include <QDebug>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QFrame>
#include <QComboBox>

/**
 * This class is used in creating the window that edits password entries. It
 * shows the standard fields (account name, group, username, email, password,
 * and URL), the requiredFields (notes), and custom fields (whatever the user
 * wants, but frequently these are security questions and answers).
 */
GenericFieldsEditor::GenericFieldsEditor(QList<fieldSpec> requiredFieldSpecs,
        QWidget *parent, bool descEdit) :
	QWidget(parent),
	m_requiredFieldSpecs(requiredFieldSpecs),
	m_descEdit(descEdit)
{
	setLayout(new QVBoxLayout());
	layout()->setAlignment(Qt::AlignTop);
	layout()->setContentsMargins(0,0,0,0);
	m_requiredFieldsWidget = new QWidget();
	m_extraFieldsWidget = new QWidget();
	m_newField = new QDialog();
	m_newField->setAccessibleName("Add Field");
	m_newFieldAddButton = new QPushButton(QIcon(":/images/plus.png"),"");
	m_newFieldAddButton->setToolTip("Add field");
	m_newFieldAddButton->setEnabled(!SignetApplication::get()->isDeviceEmulated());
	m_newFieldNameEdit = new QLineEdit();
	m_newFieldNameEdit->setAccessibleName("Field name");
	m_newFieldNameEdit->setEnabled(!SignetApplication::get()->isDeviceEmulated());

	connect(m_newFieldNameEdit, SIGNAL(returnPressed()), m_newFieldAddButton, SLOT(click()));

	m_fieldFrame = new QFrame();
	m_fieldFrame->setFrameShape(QFrame::HLine);
	m_newFieldFrame = new QFrame();
	m_newFieldFrame->setFrameShape(QFrame::HLine);
	layout()->addWidget(m_requiredFieldsWidget); // "Notes" field
	// the required fields are populated at the end of this method
	layout()->addWidget(m_fieldFrame);           // horizontal line
	layout()->addWidget(m_extraFieldsWidget);    // custom fields (excluding "Notes")
	// custom fields are populated by the loadFields() method
	layout()->addWidget(m_newFieldFrame);        // horizontal line
	layout()->addWidget(m_newField);             // New Field section

	// Configure the layout of the required fields
	m_requiredFieldsWidget->setLayout(new QVBoxLayout());
	m_requiredFieldsWidget->layout()->setAlignment(Qt::AlignTop);
	m_requiredFieldsWidget->layout()->setContentsMargins(0,0,0,0);
	// Loop through required fields which were passed in and add them to
	// the required fields section
	for (auto requiredFieldSpec : m_requiredFieldSpecs) {
		auto fieldEdit = createFieldEdit(requiredFieldSpec.name, requiredFieldSpec.type, false);
		m_requiredFieldsWidget->layout()->addWidget(fieldEdit->widget());
	}

	// Configure the layout of the extra fields
	m_extraFieldsWidget->setLayout(new QVBoxLayout());
	m_extraFieldsWidget->layout()->setAlignment(Qt::AlignTop);
	m_extraFieldsWidget->layout()->setContentsMargins(0,0,0,0);

	m_fieldFrame->hide();
}

void GenericFieldsEditor::setupNewFieldsUI() {
	// Set up the New Field section of the UI
	m_newFieldTypeCombo = new QComboBox();
	m_newFieldTypeCombo->setAccessibleName("Field Type");
	m_newFieldTypeCombo->addItem("Text", "Text");
	m_newFieldTypeCombo->addItem("Text (Secret)", ".Text");
	m_newFieldTypeCombo->addItem("Text block", "Text block");
	m_newFieldTypeCombo->addItem("Integer", "Integer");
	m_newFieldTypeCombo->setEnabled(!SignetApplication::get()->isDeviceEmulated());

	// The "New Field" layout is one line describing the section and
	// one line under it with a UI to add a new extra field.
	QVBoxLayout *newFieldLayout = new QVBoxLayout();
	m_newField->setLayout(newFieldLayout);
	newFieldLayout->setContentsMargins(0,0,0,0);
	// Section label goes on top
	QLabel *newFieldLabel = new emphasisText("New Field");
	newFieldLayout->addWidget(newFieldLabel);
	// Then the horizontal section goes below the label
	QLayout *newFieldDescLayout = new QHBoxLayout();
	newFieldDescLayout->addWidget(new genericText("Field Name"));
	newFieldDescLayout->addWidget(m_newFieldNameEdit);
	newFieldDescLayout->addWidget(m_newFieldTypeCombo);
	newFieldDescLayout->addWidget(m_newFieldAddButton);
	newFieldLayout->addLayout(newFieldDescLayout);
	connect(m_newFieldAddButton, SIGNAL(pressed()),  this, SLOT(addNewFieldUI()));
	connect(m_newFieldNameEdit,  SIGNAL(textEdited(QString)), this, SLOT(newFieldNameEdited(QString)));
}

void GenericFieldsEditor::removeField(QString name)
{
	genericFieldEdit *fieldEdit = *m_fieldEditMap.find(name);
	m_fieldEditMap.remove(name);
	m_extraFields.remove(name);
	m_extraFieldsWidget->layout()->removeWidget(fieldEdit->widget());
	if  (!m_extraFields.size()) {
		m_fieldFrame->hide();
	}
	fieldEdit->widget()->deleteLater();
	emit edited();
}

void GenericFieldsEditor::newFieldNameEdited(QString val)
{
	Q_UNUSED(val);
}

genericFieldEdit *GenericFieldsEditor::addNewField(QString name, QString type, bool focus=true)
{
	if (!type.size()) {
		type = QString("text");
	}
	auto fieldEdit = createFieldEdit(name, type, true);
	QHBoxLayout *layout = ((QHBoxLayout *)(m_extraFieldsWidget->layout()));
	layout->insertWidget(layout->count(), fieldEdit->widget());
	m_fieldFrame->show();
	if (focus) {
		fieldEdit->setFocus();
	}
	return fieldEdit;
}

/**
 * This creates a generic field (e.g. to be a custom field added by the user),
 * adds it to m_fieldEditMap and then returns the field to the caller.
 */
genericFieldEdit *GenericFieldsEditor::createFieldEdit(QString name, QString type, bool canRemove)
{
	auto *fieldEditFactory = genericFieldEditFactory::get();
	genericFieldEdit *fieldEdit = nullptr;
	if (m_descEdit) {
		fieldEdit = fieldEditFactory->generate(name, QString("type desc"), canRemove, this);
		fieldEdit->fromString(type);
	} else {
		fieldEdit = fieldEditFactory->generate(name, type, canRemove, this);
	}
	m_fieldEditMap.insert(name, fieldEdit);
	if (canRemove) {
		m_extraFields.insert(name, fieldEdit);
	}
	connect(fieldEdit, SIGNAL(edited()), this, SIGNAL(edited()));
	connect(fieldEdit, SIGNAL(remove(QString)), this, SLOT(removeField(QString)));
	return fieldEdit;
}

void GenericFieldsEditor::addNewFieldUI()
{
	QString fieldName = m_newFieldNameEdit->text();
	if (fieldName.size()) {
		m_newFieldNameEdit->clear();
		QString dataType = m_newFieldTypeCombo->currentData().toString();
		addNewField(fieldName, dataType);
		emit edited();
	}
}

void GenericFieldsEditor::loadFields(genericFields &fields)
{
	//Remove deleted fields
	for (auto field : m_fieldEditMap) {
		bool fieldFound = false;
		for (int i = 0; i < fields.fieldCount(); i++) {
			if (fields.getField(i).name == field->name()) {
				fieldFound = true;
				break;
			}
		}
		if (!fieldFound) {
			for (auto spec : m_requiredFieldSpecs) {
				if (spec.name == field->name()) {
					fieldFound = true;
					break;
				}
			}
			if (!fieldFound) {
				removeField(field->name());
			}
		}
	}

	// Add each of the existing fields to the UI
	for (int i = 0; i < fields.fieldCount(); i++) {
		auto genericField = fields.getField(i);
		if (m_fieldEditMap.count(genericField.name)) {
			auto genericFieldEdit = *m_fieldEditMap.find(genericField.name);
			genericFieldEdit->fromString(genericField.value);
		} else {
			// Do not focus on fields when we load them
			// They only should get focus when the user adds the field
			auto fieldEdit = addNewField(genericField.name, genericField.type, false);
			fieldEdit->fromString(genericField.value);
		}
	}
}

void GenericFieldsEditor::saveFields(genericFields &fields)
{
	int i = 0;
	//Update and delete fields
	while (i < fields.fieldCount()) {
		auto field = fields.getField(i);
		if (m_fieldEditMap.count(field.name)) {
			auto fieldEdit = *(m_fieldEditMap.find(field.name));
			field.value = fieldEdit->toString();
			field.type = fieldEdit->type();
			fields.replaceField(i, field);
			i++;
		} else {
			fields.removeField(i);
		}
	}

	//Add new fields
	for (auto fieldEdit : m_fieldEditMap) {
		int i;
		for (i = 0; i < fields.fieldCount(); i++) {
			auto field = fields.getField(i);
			if (fieldEdit->name() == field.name) {
				break;
			}
		}
		if (i == fields.fieldCount()) {
			genericField feild;
			feild.name = fieldEdit->name();
			feild.type = fieldEdit->type();
			feild.value = fieldEdit->toString();
			fields.addField(feild);
		}
	}
}
