/*
# PostgreSQL Database Modeler (pgModeler)
#
# (c) Copyright 2006-2026 - Raphael Araújo e Silva <raphael@pgmodeler.io>
#
# DEVELOPMENT, MAINTENANCE AND COMMERCIAL DISTRIBUTION BY:
# Nullptr Labs Software e Tecnologia LTDA <contact@nullptrlabs.io>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# The complete text of GPLv3 is at LICENSE file on source code root directory.
# Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
*/

#include "baseobjectwidget.h"
#include "baseobject.h"
#include "customuistyle.h"
#include "messagebox.h"
#include "permissionwidget.h"
#include "widgets/customsqlwidget.h"
#include "baseform.h"
#include "settings/generalconfigwidget.h"
#include "utilsns.h"
#include "dbobjects/pgsqltypewidget.h"

BaseObjectWidget::BaseObjectWidget(QWidget *parent, ObjectType obj_type): QWidget(parent)
{
	setWindowTitle("");
	setupUi(this);

	CustomUiStyle::setStyleHint(CustomUiStyle::AlertFrmHint, protected_obj_frm);
	CustomUiStyle::setStyleHint(CustomUiStyle::DefaultFrmHint, id_icon_frm);

	installEventFilter(new TabOrderManager(this));

	handled_obj_type = obj_type;
	operation_count = 0;
	new_object = false;
	model = nullptr;
	table = nullptr;
	relationship = nullptr;
	prev_schema = nullptr;
	op_list = nullptr;
	object = nullptr;
	object_px = DNaN;
	object_py = DNaN;
	schema_sel = nullptr;
	owner_sel = nullptr;
	tablespace_sel = nullptr;
	obj_assoc_wgt = nullptr;
	sql_preview_pg = nullptr;
	object_protected = false;

	connect(edt_perms_tb, &QPushButton::clicked, this, &BaseObjectWidget::editPermissions);
	connect(append_sql_tb, &QPushButton::clicked, this, &BaseObjectWidget::editCustomSQL);

	schema_sel = new ObjectSelectorWidget(ObjectType::Schema, this);
	collation_sel = new ObjectSelectorWidget(ObjectType::Collation, this);
	tablespace_sel = new ObjectSelectorWidget(ObjectType::Tablespace, this);
	owner_sel = new ObjectSelectorWidget(ObjectType::Role, this);

	configureBaseLayout();
}

bool BaseObjectWidget::eventFilter(QObject *object, QEvent *event)
{
	//Filters the ENTER/RETURN pressing forcing the parent form activate the "Apply" button
	if(event->type() == QEvent::KeyPress)
	{
		QKeyEvent *kevent=dynamic_cast<QKeyEvent *>(event);

		// If the object is protected we avoid accepting the enter hit
		if(!protected_obj_frm->isVisible() &&
			 (kevent->key()==Qt::Key_Return || kevent->key()==Qt::Key_Enter))
		{
			applyConfiguration();
			return true;
		}
	}

	return QWidget::eventFilter(object, event);
}

ObjectType BaseObjectWidget::getHandledObjectType()
{
	return handled_obj_type;
}

bool BaseObjectWidget::isHandledObjectProtected()
{
	return object_protected;
}

void BaseObjectWidget::showEvent(QShowEvent *)
{
	name_edt->setFocus();
}

void BaseObjectWidget::setRequiredField(QWidget *widget)
{
	if(widget)
	{
		QLabel *lbl = qobject_cast<QLabel *>(widget);
		//QLineEdit *edt = qobject_cast<QLineEdit *>(widget);
		//QTextEdit *txt = qobject_cast<QTextEdit *>(widget);
		QGroupBox *grp = qobject_cast<QGroupBox *>(widget);
		//ObjectSelectorWidget *sel = dynamic_cast<ObjectSelectorWidget *>(widget);
		//FileSelectorWidget *file_sel = dynamic_cast<FileSelectorWidget *>(widget);
		PgSQLTypeWidget *pgtype = dynamic_cast<PgSQLTypeWidget *>(widget);
		QString str_aux = " <span style='color: #ff0000;'>*</span> ";
		//QColor border_color = CustomTableWidget::getTableItemColor(CustomTableWidget::RemovedItemBgColor);

		if(lbl || pgtype || grp)
		{
			if(lbl)
				lbl->setText(str_aux + lbl->text());

			if(pgtype && !grp)
				grp = pgtype->findChild<QGroupBox *>();

			if(grp)
				grp->setTitle("* " + grp->title());
		}
		/* else if(edt || txt || sel || file_sel)
		{
			if(sel || file_sel)
			{
				widget->setStyleSheet(QString("%1 > QLineEdit { border: 2px solid %2; padding-top: 2px; padding-bottom: 2px; border-radius: 4px; }")
															.arg(widget->metaObject()->className(), border_color.name()));
			}
			else
				widget->setStyleSheet(QString("%1 { border: 2px solid %2; padding-top: 2px; padding-bottom: 2px; border-radius: 4px; }")
															.arg(widget->metaObject()->className()).arg(border_color.name()));
		} */

		str_aux = (!widget->toolTip().isEmpty() ? "\n" : "");
		widget->setToolTip(widget->toolTip() + str_aux + tr("Required field. Leaving this empty will raise errors!"));
	}
}

void BaseObjectWidget::setAttributes(DatabaseModel *model, BaseObject *object, BaseObject *parent_obj)
{
	setAttributes(model, nullptr, object, parent_obj, DNaN, DNaN, false);
}

void BaseObjectWidget::disableReferencesSQL(BaseObject *object)
{
	TableObject *tab_obj = nullptr;

	for(auto &obj : object->getReferences())
	{
		tab_obj = dynamic_cast<TableObject *>(obj);

		//If the object is a relationship added does not do anything since the relationship itself will be disabled
		if(!tab_obj || (tab_obj && !tab_obj->isAddedByRelationship()))
		{
			obj->setSQLDisabled(disable_sql_chk->isChecked());

			//Update the parent table graphical representation to show the disabled child object
			if(tab_obj)
				tab_obj->getParentTable()->setModified(true);

			//Disable the references of the current object too
			disableReferencesSQL(obj);
		}
	}
}

void BaseObjectWidget::configureTabOrder(std::vector<QWidget *> widgets)
{
	ObjectSelectorWidget *obj_sel=nullptr;
	PgSQLTypeWidget *type_wgt=nullptr;
	std::vector<QWidget *> children, tab_order;
	int idx=0, cnt=0;

	widgets.insert(widgets.begin(),
	{ name_edt, alias_edt, schema_sel , collation_sel, owner_sel, tablespace_sel,
	  comment_edt, append_sql_tb, edt_perms_tb, disable_sql_chk });

	for(auto &wgt : widgets)
	{
		wgt->setFocusPolicy(Qt::StrongFocus);

		obj_sel=dynamic_cast<ObjectSelectorWidget *>(wgt);
		type_wgt=dynamic_cast<PgSQLTypeWidget *>(wgt);

		if(obj_sel)
			children={ obj_sel->rem_object_tb, obj_sel->sel_object_tb };
		else if(type_wgt)
		{
			children={ type_wgt->type_cmb, type_wgt->length_sb, type_wgt->precision_sb,
					   type_wgt->dimension_sb, type_wgt->interval_cmb, type_wgt->spatial_cmb,
					   type_wgt->srid_spb, type_wgt->var_z_chk, type_wgt->var_m_chk,
					   type_wgt->timezone_chk };
		}

		tab_order.push_back(wgt);

		for(auto &child : children)
		{
			child->setFocusPolicy(Qt::StrongFocus);
			tab_order.push_back(child);
		}
	}

	cnt=tab_order.size()-1;

	for(idx=0; idx < cnt; idx++)
		QWidget::setTabOrder(tab_order[idx], tab_order[idx+1]);
}

BaseObject *BaseObjectWidget::getHandledObject()
{
	return object;
}

QString BaseObjectWidget::getSQLCodePreview()
{
	if(!object)
		return "";

	return object->getSourceCode(SchemaParser::SqlCode);
}

void BaseObjectWidget::cancelChainedOperation()
{
	bool op_list_changed=false;

	if(op_list->isOperationChainStarted())
		op_list->finishOperationChain();

	if(operation_count < op_list->getCurrentSize())
	{
		op_list_changed=true;
		BaseObjectWidget::cancelConfiguration();
	}

	if(new_object && this->object)
	{
		if(!op_list_changed)
			delete this->object;

		this->object=nullptr;
	}
}

void BaseObjectWidget::setAttributes(DatabaseModel *model, OperationList *op_list, BaseObject *object, BaseObject *parent_obj, double obj_px, double obj_py, bool uses_op_list)
{
	ObjectType obj_type, parent_type = ObjectType::BaseObject;

	/* Reseting the objects attributes in order to force them to be redefined
	every time this method is called */
	this->object = nullptr;
	this->model = nullptr;
	this->op_list = nullptr;
	this->relationship = nullptr;
	this->table = nullptr;

	if(!model || (uses_op_list && !op_list))
		throw Exception(ErrorCode::AsgNotAllocattedObject,PGM_FUNC,PGM_FILE,PGM_LINE);

	if(op_list)
	  operation_count = op_list->getCurrentSize();

	this->model = model;

	if(parent_obj)
	{
		parent_type = parent_obj->getObjectType();

		if(BaseTable::isBaseTable(parent_type))
			this->table = dynamic_cast<BaseTable *>(parent_obj);
		else if(parent_type == ObjectType::Relationship)
			this->relationship = dynamic_cast<Relationship *>(parent_obj);
		else if(parent_type != ObjectType::Database && parent_type != ObjectType::Schema)
			throw Exception(ErrorCode::AsgObjectInvalidType, PGM_FUNC, PGM_FILE, PGM_LINE);
	}
	else
	{
		TableObject *tab_obj = dynamic_cast<TableObject *>(object);

		if(object && object->getSchema())
			parent_obj = object->getSchema();
		else if(tab_obj && tab_obj->getParentTable())
			parent_obj = tab_obj->getParentTable();
		else
			parent_obj = model;
	}

	if(dynamic_cast<BaseGraphicObject *>(object))
		dynamic_cast<BaseGraphicObject *>(object)->setModified(false);

	this->op_list = op_list;
	this->object = object;

	// Updating the object's dependencies and references widget
	if(obj_assoc_wgt)
		obj_assoc_wgt->setAttributes(object, false);

	if(this->table)
	{
		this->object_px = this->table->getPosition().x();
		this->object_py = this->table->getPosition().y();
	}
	else
	{
		this->object_px = obj_px;
		this->object_py = obj_py;
	}

	name_edt->setFocus();
	edt_perms_tb->setEnabled(object != nullptr && !new_object);
	append_sql_tb->setEnabled(object != nullptr && !new_object);

	owner_sel->setModel(model);
	owner_sel->setSelectedObject(model->getDefaultObject(ObjectType::Role));

	schema_sel->setModel(model);
	schema_sel->setSelectedObject(model->getDefaultObject(ObjectType::Schema));

	tablespace_sel->setModel(model);
	tablespace_sel->setSelectedObject(model->getDefaultObject(ObjectType::Tablespace));

	collation_sel->setModel(model);
	collation_sel->setSelectedObject(model->getDefaultObject(ObjectType::Collation));

	if(object)
	{
		obj_id_lbl->setVisible(true);
		obj_id_lbl->setText(QString("#%1").arg(object->getObjectId()));

		if(handled_obj_type != ObjectType::BaseObject)
			name_edt->setText(object->getName());
		else
			name_edt->setText(object->getSignature());

		comment_edt->setPlainText(object->getComment());
		alias_edt->setText(object->getAlias());

		/* When creating a new table or relationship the object is pre allocated and the flag new_object is set.
		 * In order to avoid the selectors to have empty values, we check if the flag is false which means
		 * that the object is not new at all */
		if(!new_object)
		{
			schema_sel->setSelectedObject(object->getSchema());
			tablespace_sel->setSelectedObject(object->getTablespace());
			owner_sel->setSelectedObject(object->getOwner());
			collation_sel->setSelectedObject(object->getCollation());
		}
		/* Some objects like tables and views came with a pre selected schema even when
	   they are new objects. So we need to show this selected schema instead of the default one */
		else if(new_object && object->getSchema())
			schema_sel->setSelectedObject(object->getSchema());

		obj_type = object->getObjectType();
		object_protected = (parent_type != ObjectType::Relationship &&
												(object->isProtected() ||
												 (TableObject::isTableObject(obj_type) &&
													dynamic_cast<TableObject *>(object)->isAddedByRelationship())));
		protected_obj_frm->setVisible(object_protected);
		disable_sql_chk->setChecked(object->isSQLDisabled());
	}
	else
	{
		object_protected = false;
		obj_id_lbl->setVisible(false);
		protected_obj_frm->setVisible(false);

		if(parent_obj && parent_obj->getObjectType() == ObjectType::Schema)
			schema_sel->setSelectedObject(parent_obj);
	}
}

void BaseObjectWidget::configureBaseLayout()
{
	const std::map<QList<ObjectType>, QList<FieldLayoutCfg>> field_confs {
		{
			// Objects that has only the name field visible
			{ ObjectType::BaseObject, ObjectType::Cast, ObjectType::Role,
				ObjectType::Transform, ObjectType::GenericSql, ObjectType::Tag,
				ObjectType::Textbox, ObjectType::Parameter },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0 } }
		},

		{	// Objects that has the name and owner fields visible
			{ ObjectType::Language, ObjectType::Tablespace, ObjectType::EventTrigger,
				ObjectType::ForeignDataWrapper, ObjectType::ForeignServer, ObjectType::UserMapping },
			{ { name_lbl, name_edt, 0, 0 },
				{ owner_lbl, owner_sel, id_icon_frm, 0, 1 } },
		},

		{	// Objects that has the name and alias fields visible
			{ ObjectType::Relationship, ObjectType::BaseRelationship,
				ObjectType::Rule, ObjectType::Policy, ObjectType::Trigger },
			{ { name_lbl, name_edt, 0, 0 },
				{ alias_lbl, alias_edt, id_icon_frm, 0, 1 } },
		},

		{	// Objects that has the name and schema fields visible
			{ ObjectType::Extension },
			{ { name_lbl, name_edt, 0, 0 },
				{ schema_lbl, schema_sel, id_icon_frm, 0, 1 } },
		},

		{	// Objects that has the name, tablespace and owner fields visible
			{ ObjectType::Database },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0, 1, 2 },
				{ tablespace_lbl, tablespace_sel, 1, 0 },
				{ owner_lbl, owner_sel, 1, 1 } },
		},

		{
			// Objects that has the name, alias and collation field visible
			{ ObjectType::Column },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0, 1, 2 },
				{ alias_lbl, alias_edt, 1, 0 },
				{ collation_lbl, collation_sel, 1, 1 } },
		},

		{	// Objects that has the name, alias and tablespace fields visible
			{ ObjectType::Constraint, ObjectType::Index },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0, 1, 2 },
				{ alias_lbl, alias_edt, 1, 0 },
				{ tablespace_lbl, tablespace_sel, 1, 1 } },
		},

		{	// Objects that has the name, schema and owner fields visible
			{ ObjectType::Aggregate, ObjectType::Conversion, ObjectType::Function,
				ObjectType::OpClass, ObjectType::Operator, ObjectType::OpFamily,
				ObjectType::Procedure, ObjectType::Sequence },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0, 1, 2 },
				{ schema_lbl, schema_sel, 1, 0 },
				{ owner_lbl, owner_sel, 1, 1 } },
		},

		{	// Objects that has the name, alias and owner fields visible
			{ ObjectType::Schema },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0, 1, 2 },
				{ alias_lbl, alias_edt, 1, 0 },
				{ owner_lbl, owner_sel, 1, 1 } },
		},

		{	// Objects that has the name, alias, schema and owner fields visible
			{ ObjectType::ForeignTable },
			{ { name_lbl, name_edt, 0, 0 },
				{ alias_lbl, alias_edt, id_icon_frm, 0, 1 },
				{ schema_lbl, schema_sel, 1, 0 },
				{ owner_lbl, owner_sel, 1, 1 } },
		},

		{	// Objects that has the name, schema, collation and owner fields visible
			{ ObjectType::Collation, ObjectType::Domain, ObjectType::Type },
			{ { name_lbl, name_edt, 0, 0 },
				{ schema_lbl, schema_sel, id_icon_frm, 0, 1 },
				{ collation_lbl, collation_sel, 1, 0 },
				{ owner_lbl, owner_sel, 1, 1 } },
		},

		{	// Objects that has the name, alias, schema, tablespace and owner fields visible
			{ ObjectType::Table, ObjectType::View },
			{ { name_lbl, name_edt, id_icon_frm, 0, 0, 1, 2 },
				{ schema_lbl, schema_sel, 1, 0 },
				{ alias_lbl, alias_edt, 1, 1 },
				{ tablespace_lbl, tablespace_sel, 2, 0 },
				{ owner_lbl, owner_sel, 2, 1 } },
		},
	};

	baseobject_grid = GuiUtilsNs::createGridLayout(GuiUtilsNs::LtMargin, GuiUtilsNs::LtSpacing);
	baseobject_grid->setObjectName("baseobject_grid");

	misc_btns_lt = GuiUtilsNs::createHBoxLayout(0, GuiUtilsNs::LtSpacing);
	misc_btns_lt->setObjectName("misc_btns_lt");

	extra_wgts_lt = GuiUtilsNs::createVBoxLayout(0, GuiUtilsNs::LtSpacing);
	extra_wgts_lt->setObjectName("extra_wgts_lt ");

	baseobject_grid->addWidget(protected_obj_frm, 0, 0, 1, 0);

	for(auto &[obj_types, fields] : field_confs)
	{
		if(!obj_types.contains(handled_obj_type))
			continue;
		
		for(auto &field : fields)
		{
			/* Adding the pair (label/widget) to the grid layout. We shift the row by one
				* in order to leave the first row for the protected object frame */
			baseobject_grid->addLayout(GuiUtilsNs::createBuddyWidgetLayout(field.label, field.widget, field.append_widget),
																 field.row + 1, field.col,
																 field.row_span, field.col_span);
		}

		break;
	}

	baseobject_grid->addLayout(GuiUtilsNs::createBuddyWidgetLayout(comment_lbl, comment_edt),
															baseobject_grid->count(), 0, 1, 0);
	baseobject_grid->addLayout(extra_wgts_lt, baseobject_grid->count(), 0, 1, 0);

	misc_btns_lt->addWidget(append_sql_tb);
	misc_btns_lt->addWidget(edt_perms_tb);
	misc_btns_lt->addWidget(disable_sql_chk);
	misc_btns_lt->addItem(new QSpacerItem(20, 1, QSizePolicy::Expanding));

	baseobject_grid->addLayout(misc_btns_lt, baseobject_grid->count(), 0, 1, 0);
}

void BaseObjectWidget::configureTabbedLayout(QTabWidget *tab_widget, bool create_general_pg, bool create_assoc_pg)
{
	if(!tab_widget || !tab_widget->parentWidget())
		return;

	tab_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

	if(create_general_pg)
	{
		// General/basics fields page
		QWidget *page =  new QWidget(tab_widget);
		page->setObjectName("general_tab");
		page->setLayout(baseobject_grid);

		tab_widget->insertTab(0, page, GuiUtilsNs::getIcon("objects"), tr("General"));
		tab_widget->setCurrentIndex(0);
	}

	if(create_assoc_pg && handled_obj_type != ObjectType::Textbox)
	{
		// Dependencies and references page
		obj_assoc_wgt = new ObjectAssociationsWidget(tab_widget);
		obj_assoc_wgt->setObjectName("obj_assoc_tab");
		obj_assoc_wgt->layout()->setContentsMargins(GuiUtilsNs::LtMargins);
		tab_widget->addTab(obj_assoc_wgt, GuiUtilsNs::getIcon("associations"), tr("Associations"));
	}

	if(handled_obj_type != ObjectType::Tag &&
		 handled_obj_type != ObjectType::Textbox)
	{
		// Source code preview page
		sql_preview_pg = new QWidget(tab_widget);
		sql_preview_pg->setObjectName("sql_preview_tab");
		tab_widget->addTab(sql_preview_pg, GuiUtilsNs::getIcon("sqlpreview"), tr("SQL preview"));
		NumberedTextEditor *source_txt = GuiUtilsNs::createWidgetInParent<NumberedTextEditor>(GuiUtilsNs::LtMargin, sql_preview_pg, false);
		source_txt->setReadOnly(true);

		try
		{
			SyntaxHighlighter *source_hl = new SyntaxHighlighter(source_txt);
			source_hl->loadConfiguration(GlobalAttributes::getSQLHighlightConfPath());
		}
		catch(Exception &e)
		{
			source_txt->setPlainText(tr("** Failed to load the syntax highlight configuration! ** \n\n %1").arg(e.getExceptionsText()));
		}

		// Connecting the signal to handle source code preview
		connect(tab_widget, &QTabWidget::currentChanged, this, [this, source_txt, tab_widget](int){
			if(sql_preview_pg && tab_widget->currentWidget() == sql_preview_pg)
			{
				try
				{
					source_txt->setPlainText(getSQLCodePreview());
				}
				catch(Exception &e)
				{
					source_txt->setPlainText(QString("/* %1 */").arg(e.getExceptionsText()));
				}
			}
		});
	}

	/* Moving the frame that displays the protected object message
	 * to outside general tab, thus, it'll be places at the very top
	 * of the widget's layout so it can be visible above the tab widget */
	QVBoxLayout *wgt_lt = qobject_cast<QVBoxLayout *>(layout());

	if(wgt_lt)
	{
		baseobject_grid->removeWidget(protected_obj_frm);
		wgt_lt->insertWidget(0, protected_obj_frm);
	}
	else
	{
		/* If the widget's layout is not a QVBoxLayout we just display a debug
		 * message and keep the alert message in the general tab */
		qDebug().noquote().nospace() << "BaseObjectWidget::configureTabbedLayout(QTabWidget *, bool): The widget's ("
		<< tab_widget->parentWidget()->metaObject()->className() << ") layout must be QHBoxLayout or QVBoxLayout.";
	}

	configureFormFields(handled_obj_type);
	GuiUtilsNs::configureBuddyWidgets(tab_widget);
}

void BaseObjectWidget::configureTabbedLayout(bool create_attr_page, const QString &attr_pg_name, const QString &attr_pg_icon)
{
	QLayout *curr_layout = layout();
	bool is_hbox = qobject_cast<QHBoxLayout *>(curr_layout) != nullptr,
			 is_vbox = qobject_cast<QVBoxLayout *>(curr_layout) != nullptr;

	if(!is_hbox && !is_vbox)
	{
		qDebug().noquote() << "BaseObjectWidget::configureTabbedLayout(bool, QString, QString): The widget's layout must be QHBoxLayout or QVBoxLayout.";
		return;
	}

	QLayout *new_layout = nullptr;
	QLayoutItem *item = nullptr;
	QTabWidget *attribs_tbw = nullptr;

	if(is_hbox)
		new_layout = new QHBoxLayout;
	else
		new_layout = new QVBoxLayout;

	new_layout->setContentsMargins(curr_layout->contentsMargins());
	new_layout->setSpacing(curr_layout->spacing());
	new_layout->setObjectName(curr_layout->objectName());

	/* Moving all items from the current widget's layout
	 * to the new layout */
	while(!curr_layout->isEmpty())
	{
		item = curr_layout->takeAt(0);

		if(item)
			new_layout->addItem(item);
	}

	/* We have to delete the current layout to be able
	 * to reconfigure the widget's layout */
	delete curr_layout;

	// Create a default tab widget and configure a tabbed layout in it
	attribs_tbw = GuiUtilsNs::createWidgetInParent<QTabWidget>(GuiUtilsNs::LtMargin, this);

	if(create_attr_page)
	{
		QWidget *attr_page = new QWidget(this);
		attr_page->setLayout(new_layout);
		attribs_tbw->addTab(attr_page,
												attr_pg_icon.isEmpty() ? GuiUtilsNs::getIcon("attribute") : GuiUtilsNs::getIcon(attr_pg_icon),
												attr_pg_name.isEmpty() ? tr("Attributes") : attr_pg_name);
	}
	else
	{
		// Adding the new layout in the middle space in extra_wgts_lt
		extra_wgts_lt->addLayout(new_layout);
	}

	configureTabbedLayout(attribs_tbw);
}

void BaseObjectWidget::configureFormFields(ObjectType obj_type, bool inst_ev_filter)
{
	disable_sql_chk->setVisible(obj_type!=ObjectType::BaseObject && obj_type!=ObjectType::Permission &&
															obj_type!=ObjectType::Textbox && obj_type!=ObjectType::Tag &&
															obj_type!=ObjectType::Parameter);

	alias_edt->setVisible(BaseObject::acceptsAlias(obj_type));
	alias_lbl->setVisible(BaseObject::acceptsAlias(obj_type));

	edt_perms_tb->setVisible(Permission::acceptsPermission(obj_type));
	append_sql_tb->setVisible(BaseObject::acceptsCustomSQL(obj_type));

	schema_lbl->setVisible(BaseObject::acceptsSchema(obj_type));
	schema_sel->setVisible(BaseObject::acceptsSchema(obj_type));

	owner_lbl->setVisible(BaseObject::acceptsOwner(obj_type));
	owner_sel->setVisible(BaseObject::acceptsOwner(obj_type));

	tablespace_lbl->setVisible(BaseObject::acceptsTablespace(obj_type));
	tablespace_sel->setVisible(BaseObject::acceptsTablespace(obj_type));

	collation_lbl->setVisible(BaseObject::acceptsCollation(obj_type));
	collation_sel->setVisible(BaseObject::acceptsCollation(obj_type));

	comment_lbl->setVisible(BaseObject::acceptsComment(obj_type));
	comment_edt->setVisible(BaseObject::acceptsComment(obj_type));

	if(obj_type != ObjectType::BaseObject)
	{
		obj_icon_lbl->setPixmap(GuiUtilsNs::getPixmap(obj_type));
		obj_icon_lbl->setToolTip(BaseObject::getTypeName(obj_type));

		if(obj_type != ObjectType::Permission && obj_type != ObjectType::Cast &&
				obj_type != ObjectType::UserMapping && obj_type != ObjectType::Transform)
		{
			setRequiredField(name_lbl);
			setRequiredField(name_edt);
		}
		else
		{
			QFont font = name_edt->font();
			name_edt->setReadOnly(true);
			font.setItalic(true);
			name_edt->setFont(font);
		}

		if(obj_type!=ObjectType::Extension)
		{
			setRequiredField(schema_lbl);
			setRequiredField(schema_sel);
		}
	}

	if(inst_ev_filter)
	{
		//Install the event filter into all children object in order to capture key press
		for(auto &wgt : this->findChildren<QWidget *>())
		{
			//Avoids install event filters in objects that are inteneded to edit multiple lines
			if(wgt &&
					wgt->metaObject()->className() != QString("QPlainTextEdit") &&
					wgt->metaObject()->className() != QString("NumberedTextEditor"))
				wgt->installEventFilter(this);
		}
	}
}

QString BaseObjectWidget::generateVersionsInterval(unsigned ver_interv_id, const QString &ini_ver, const QString &end_ver)
{
	if(ver_interv_id==UntilVersion && !ini_ver.isEmpty())
		return (UtilsNs::EntityLt + "= " + ini_ver);

	if(ver_interv_id==VersionsInterval && !ini_ver.isEmpty() && !end_ver.isEmpty())
		return (UtilsNs::EntityGt + "= " + ini_ver + UtilsNs::EntityAmp + UtilsNs::EntityLt + "= " + end_ver);

	if(ver_interv_id==AfterVersion &&  !ini_ver.isEmpty())
		return (UtilsNs::EntityGt + "= " + ini_ver);

	return "";
}

QFrame *BaseObjectWidget::generateInformationFrame(const QString &msg)
{
	QFrame *info_frm = nullptr;
	QHBoxLayout *hbox = nullptr;
	QLabel *ico_lbl = nullptr, *msg_lbl = nullptr;
	QFont font;

	info_frm = new QFrame;

	font.setItalic(false);
	font.setBold(false);
	info_frm->setFont(font);

	info_frm->setObjectName("info_frm");
	info_frm->setFrameShape(QFrame::StyledPanel);
	info_frm->setFrameShadow(QFrame::Raised);
	info_frm->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
	CustomUiStyle::setStyleHint(CustomUiStyle::InfoFrmHint, info_frm);

	hbox = GuiUtilsNs::createHBoxLayout(GuiUtilsNs::LtMargin, GuiUtilsNs::LtSpacing, info_frm);
	hbox->setObjectName("grid");

	ico_lbl = new QLabel(info_frm);
	ico_lbl->setObjectName("icon_lbl");
	ico_lbl->setMinimumSize(QSize(25, 25));
	ico_lbl->setMaximumSize(QSize(25, 25));
	ico_lbl->setScaledContents(true);
	ico_lbl->setPixmap(GuiUtilsNs::getPixmap("info"));
	ico_lbl->setAlignment(Qt::AlignLeft|Qt::AlignTop);

	msg_lbl = new QLabel(info_frm);
	msg_lbl->setFont(font);
	msg_lbl->setObjectName("message_lbl");
	msg_lbl->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
	msg_lbl->setWordWrap(true);
	msg_lbl->setText(msg);

	hbox->addWidget(ico_lbl);
	hbox->addWidget(msg_lbl);

	return info_frm;
}

void BaseObjectWidget::highlightVersionSpecificFields(std::map<QString, std::vector<QWidget *> > &fields,
																											std::map< QWidget *, std::vector<QString> > *values)
{
	QString field_name;

	for(auto itr : fields)
	{
		for(auto wgt : itr.second)
		{
			if(values && values->count(wgt) > 0)
			{
				field_name+="<br/>" + tr("Value(s)") + ": (";
				for(auto value : values->at(wgt))
				{
					field_name += value;
					field_name+=", ";
				}

				field_name.remove(field_name.length() - 2, 2);
				field_name+=")";
			}

			wgt->setStyleSheet("QWidget {	font-weight: bold; font-style: italic; text-decoration: underline; }");
			wgt->setToolTip("<p>PostgreSQL" + itr.first + QString(" %1</p>").arg(field_name));
		}
	}
}

void BaseObjectWidget::editPermissions()
{
	BaseObject *parent_obj = nullptr;
	BaseForm parent_form(this);
	PermissionWidget *permission_wgt=new PermissionWidget;

	if(this->relationship)
		parent_obj = this->relationship;

	permission_wgt->setAttributes(this->model, parent_obj, this->object);
	parent_form.setMainWidget(permission_wgt);
	parent_form.setButtonConfiguration(Messagebox::CloseButton);

	GeneralConfigWidget::restoreWidgetGeometry(&parent_form, permission_wgt->metaObject()->className());
	parent_form.exec();
	GeneralConfigWidget::saveWidgetGeometry(&parent_form, permission_wgt->metaObject()->className());
}

void BaseObjectWidget::editCustomSQL()
{
	BaseForm parent_form(this);
	CustomSQLWidget *customsql_wgt = new CustomSQLWidget;

	customsql_wgt->setAttributes(this->model, this->object);
	parent_form.setMainWidget(customsql_wgt);

	GeneralConfigWidget::restoreWidgetGeometry(&parent_form, customsql_wgt->metaObject()->className());
	parent_form.exec();
	GeneralConfigWidget::saveWidgetGeometry(&parent_form, customsql_wgt->metaObject()->className());
}

void BaseObjectWidget::applyConfiguration()
{
	if(object)
	{
		try
		{
			BaseObject *aux_obj=nullptr, *aux_obj1=nullptr, *parent_obj=nullptr;
			bool new_obj;
			ObjectType obj_type=object->getObjectType();
			QString obj_name;

			qApp->setOverrideCursor(Qt::WaitCursor);
			obj_name=BaseObject::formatName(name_edt->text().toUtf8(), obj_type==ObjectType::Operator);

			if(this->object->acceptsSchema() &&  schema_sel->getSelectedObject())
				obj_name=schema_sel->getSelectedObject()->getName(true) + "." + obj_name;

			//Checking the object duplicity
			if(obj_type!=ObjectType::Database && obj_type!=ObjectType::Permission && obj_type!=ObjectType::Parameter)
			{
				if(table)
				{
					//Validationg the object against the siblings on parent table
					parent_obj=table;
					aux_obj=table->getObject(obj_name,obj_type);
					aux_obj1=table->getObject(object->getName(),obj_type);
					new_obj=(!aux_obj && !aux_obj1);
				}
				else if(relationship)
				{
					//Validationg the object against the siblings on parent relationship
					parent_obj=relationship;
					aux_obj=relationship->getObject(obj_name,obj_type);
					aux_obj1=relationship->getObject(object->getName(),obj_type);
					new_obj=(!aux_obj && !aux_obj1);
				}
				//Validationg the object against the other objects on model
				else
				{
					parent_obj=model;
					aux_obj=model->getObject(obj_name,obj_type);

					/* Special case for tables and views. Its necessary to make an additional
					checking on table list when the configured object is a view or a checking
					on view list when the configured object is a table, this because PostgreSQL
					does not accepts tables and views have the same name on the same schema */
					if(BaseTable::isBaseTable(obj_type))
						aux_obj = model->getObject(obj_name, { ObjectType::Table, ObjectType::ForeignTable, ObjectType::View });

					new_obj = (aux_obj == nullptr);
				}

				//Raises an error if another object is found with the same name as the editing object
				if(!new_obj && aux_obj && aux_obj!=object)
				{
					throw Exception(Exception::getErrorMessage(ErrorCode::AsgDuplicatedObject)
									.arg(obj_name)
									.arg(BaseObject::getTypeName(obj_type))
									.arg(parent_obj->getName(true))
									.arg(parent_obj->getTypeName()),
									ErrorCode::AsgDuplicatedObject,PGM_FUNC,PGM_FILE,PGM_LINE);
				}
			}

			//Renames the object (only cast object aren't renamed)
			if(obj_type!=ObjectType::Cast)
			{
				prev_name=object->getName();
				object->setName(name_edt->text().trimmed().toUtf8());
			}

			if(object->acceptsAlias())
				object->setAlias(alias_edt->text().trimmed());

			//Sets the object's comment
			if(object->acceptsComment())
				object->setComment(comment_edt->toPlainText().toUtf8());

			//Sets the object's tablespace
			if(object->acceptsTablespace())
				object->setTablespace(tablespace_sel->getSelectedObject());

			//Sets the object's owner
			if(object->acceptsOwner())
				object->setOwner(owner_sel->getSelectedObject());

			//Sets the object's collation
			if(object->acceptsCollation())
				object->setCollation(collation_sel->getSelectedObject());

			//Sets the object's schema			
			if(object->acceptsSchema())
			{
				Schema *esquema=dynamic_cast<Schema *>(schema_sel->getSelectedObject());
				this->prev_schema=dynamic_cast<Schema *>(object->getSchema());
				object->setSchema(esquema);
			}

			if(!object->isProtected() && !object->isSystemObject())
				GuiUtilsNs::disableObjectSQL(object, disable_sql_chk->isChecked());
		}
		catch(Exception &e)
		{
			//qApp->restoreOverrideCursor();
			throw Exception(e.getErrorMessage(),e.getErrorCode(),PGM_FUNC,PGM_FILE,PGM_LINE, &e);
		}
	}
}

void BaseObjectWidget::finishConfiguration()
{
	try
	{
		if(this->object)
		{
			ObjectType obj_type=this->object->getObjectType();
			BaseGraphicObject *graph_obj=dynamic_cast<BaseGraphicObject *>(this->object);
			TableObject *tab_obj=dynamic_cast<TableObject *>(this->object);
			std::vector<BaseObject *> ref_objs;

			if(graph_obj && !std::isnan(object_px) && !std::isnan(object_py))
				graph_obj->setPosition(QPointF(object_px, object_py));

			if(new_object)
			{
				//If the object is a table object and the parent table is specified, adds it to table
				if(table && TableObject::isTableObject(obj_type))
					table->addObject(this->object);
				//Adding the object on the relationship, if specified
				else if(relationship && (obj_type==ObjectType::Column || obj_type==ObjectType::Constraint))
					relationship->addObject(dynamic_cast<TableObject *>(this->object));
				//Adding the object on the model
				else if(obj_type!=ObjectType::Parameter)
					model->addObject(this->object);

				registerNewObject();
				new_object=false;
			}
			else
			{
				//If the object is being updated, validates its SQL definition
				if(obj_type==ObjectType::BaseRelationship || obj_type==ObjectType::Textbox || obj_type==ObjectType::Tag)
					this->object->getSourceCode(SchemaParser::XmlCode);
				else
					this->object->getSourceCode(SchemaParser::SqlCode);
			}

			for(auto &obj : object->getReferences())
			{
				obj->setCodeInvalidated(true);

				if(obj->getObjectType() == ObjectType::Column)
					dynamic_cast<Column *>(obj)->getParentTable()->setModified(true);
			}

			object->setCodeInvalidated(true);

			//If the object is graphical (or a table object), updates it (or its parent) on the scene
			if(graph_obj || tab_obj)
			{
				if(!graph_obj && tab_obj && tab_obj->getObjectType()!=ObjectType::Parameter)
				{
					if(this->table)
						graph_obj=dynamic_cast<BaseGraphicObject *>(this->table);
					else
						graph_obj=dynamic_cast<BaseGraphicObject *>(this->relationship);

					graph_obj->setModified(true);
					graph_obj->setCodeInvalidated(true);
				}
				// If the new object is a graphica one, we assign the postion prior to its addtion to the parent
				else if(graph_obj)
					graph_obj->setModified(true);

				/* Updates the visual schemas when the objects is moved to another or a
					table object is added to a table */
				if(object->getSchema())
					dynamic_cast<Schema *>(object->getSchema())->setModified(true);
				else if(tab_obj && tab_obj->getParentTable() &&
						tab_obj->getParentTable()->getSchema())
					dynamic_cast<Schema *>(tab_obj->getParentTable() ->getSchema())->setModified(true);

				if(prev_schema && object->getSchema()!=prev_schema)
					prev_schema->setModified(true);
			}

			object->clearDependencies();
			object->updateDependencies();

			emit s_objectManipulated();
			emit s_closeRequested();
		}

		qApp->restoreOverrideCursor();
	}
	catch(Exception &e)
	{
		if(e.getErrorCode()==ErrorCode::AsgObjectInvalidDefinition)
		{
			throw Exception(Exception::getErrorMessage(ErrorCode::RequiredFieldsNotFilled)
											.arg(this->object->getName()).arg(this->object->getTypeName()),
											ErrorCode::RequiredFieldsNotFilled,PGM_FUNC,PGM_FILE,PGM_LINE,&e);
		}

		throw Exception(e.getErrorMessage(),e.getErrorCode(),PGM_FUNC,PGM_FILE,PGM_LINE,&e);
	}
}

void BaseObjectWidget::cancelConfiguration()
{
	if(!object)
		return;

	ObjectType obj_type;
	obj_type=this->object->getObjectType();

	if(new_object)
	{
		TableObject *tab_obj=dynamic_cast<TableObject *>(this->object);

		//Removes the object from its parent
		if(!table && !tab_obj && model->getObjectIndex(this->object) >= 0)
			model->removeObject(this->object);
		else if(table && table->getObjectIndex(tab_obj) >= 0)
			table->removeObject(tab_obj);
		else if(relationship && relationship->getObjectIndex(tab_obj) >= 0)
			relationship->removeObject(tab_obj);

		if(!BaseTable::isBaseTable(obj_type) && obj_type != ObjectType::Relationship)
		{
			if(!op_list->isObjectRegistered(this->object, Operation::ObjCreated))
				delete this->object;

			this->object=nullptr;
		}
	}

	//If the object is not a new one, restore its previous state
	if(op_list &&
	  ((!new_object && obj_type!=ObjectType::Database && obj_type!=ObjectType::Permission && operation_count != op_list->getCurrentSize()) ||
		 (new_object && (BaseTable::isBaseTable(obj_type) || obj_type==ObjectType::Relationship))))
	{
		try
		{
			op_list->undoOperation();
			op_list->removeLastOperation();
		}
		catch(Exception &){}
	}

	qApp->restoreOverrideCursor();
	emit s_objectManipulated();
}

void BaseObjectWidget::registerNewObject()
{
	try
	{
		if(this->new_object && op_list && !op_list->isObjectRegistered(this->object, Operation::ObjCreated))
		{
			//If the object is a new one is necessary register it on the operation list
			if(this->table)
				op_list->registerObject(this->object, Operation::ObjCreated, -1, this->table);
			else if(this->relationship)
				op_list->registerObject(this->object, Operation::ObjCreated, -1, this->relationship);
			else
				op_list->registerObject(this->object, Operation::ObjCreated);
		}
	}
	catch(Exception &e)
	{
		throw Exception(e.getErrorMessage(),e.getErrorCode(),PGM_FUNC,PGM_FILE,PGM_LINE,&e);
	}
}
