void ModelValidationHelper::emitValidationCanceled(void)
{
	export_thread->quit();
	db_model->setInvalidated(error_count > 0);
	emit s_validationCanceled();
	emit s_validationInfoGenerated(ValidationInfo(trUtf8("Operation canceled by the user.")));
}
ModelValidationWidget::ModelValidationWidget(QWidget *parent): QWidget(parent)
{
	vector<QString> vers;

	setupUi(this);
	this->setModel(nullptr);

	connect(validate_btn, SIGNAL(clicked(bool)), this, SLOT(validateModel(void)));
	connect(&validation_helper, SIGNAL(s_validationInfoGenerated(ValidationInfo)), this, SLOT(updateValidation(ValidationInfo)));
	connect(&validation_helper, SIGNAL(s_progressUpdated(int,QString)), this, SLOT(updateProgress(int,QString)));
	connect(hide_tb, SIGNAL(clicked(bool)), this, SLOT(hide(void)));
	connect(fix_btn, SIGNAL(clicked(bool)), this, SLOT(applyFix(void)));
	connect(clear_btn, SIGNAL(clicked(bool)), this, SLOT(clearOutput(void)));
	connect(options_btn, SIGNAL(toggled(bool)), options_frm, SLOT(setVisible(bool)));
	connect(sql_validation_chk, SIGNAL(toggled(bool)), connections_cmb, SLOT(setEnabled(bool)));
	connect(sql_validation_chk, SIGNAL(toggled(bool)), version_cmb, SLOT(setEnabled(bool)));
	connect(fix_steps_chk, SIGNAL(toggled(bool)), fix_steps_sb, SLOT(setEnabled(bool)));

	SchemaParser::getPgSQLVersions(vers);
	version_cmb->addItem(trUtf8("Autodetect"));
	while(!vers.empty())
	{
		version_cmb->addItem(vers.back());
		vers.pop_back();
	}

	options_frm->setVisible(false);
	curr_step=0;
}
ModelValidationWidget::ModelValidationWidget(QWidget *parent): QWidget(parent)
{
	try
	{
		vector<QString> vers;

		setupUi(this);
		this->setModel(nullptr);

		swapobjectsids_wgt=nullptr;
		swapobjectsids_wgt=new SwapObjectsIdsWidget(this);

		SchemaParser::getPgSQLVersions(vers);
		version_cmb->addItem(trUtf8("Autodetect"));
		while(!vers.empty())
		{
			version_cmb->addItem(vers.back());
			vers.pop_back();
		}

		options_frm->setVisible(false);
		curr_step=0;

		validation_thread=new QThread(this);
		validation_helper.moveToThread(validation_thread);

		connect(&validation_helper, SIGNAL(s_validationInfoGenerated(ValidationInfo)), this, SLOT(updateValidation(ValidationInfo)));
		connect(&validation_helper, SIGNAL(s_progressUpdated(int,QString,ObjectType)), this, SLOT(updateProgress(int,QString,ObjectType)));
		connect(&validation_helper, SIGNAL(s_objectProcessed(QString,ObjectType)), this, SLOT(updateObjectName(QString,ObjectType)));
		connect(hide_tb, SIGNAL(clicked(void)), this, SLOT(hide(void)));
		connect(clear_btn, SIGNAL(clicked(void)), this, SLOT(clearOutput(void)));
		connect(options_btn, SIGNAL(toggled(bool)), options_frm, SLOT(setVisible(bool)));
		connect(sql_validation_chk, SIGNAL(toggled(bool)), connections_cmb, SLOT(setEnabled(bool)));
		connect(sql_validation_chk, SIGNAL(toggled(bool)), version_cmb, SLOT(setEnabled(bool)));
		connect(version_cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(configureValidation(void)));
		connect(connections_cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(configureValidation(void)));
		connect(sql_validation_chk, SIGNAL(toggled(bool)), this, SLOT(configureValidation(void)));
		connect(validation_thread, SIGNAL(started(void)), &validation_helper, SLOT(validateModel(void)));
		connect(validate_btn, SIGNAL(clicked(void)), this, SLOT(validateModel(void)));
		connect(validation_thread, SIGNAL(started(void)), &validation_helper, SLOT(applyFixes(void)));
    connect(validation_thread, &QThread::started, [=](){ validation_thread->setPriority(QThread::LowPriority); });
		connect(fix_btn, SIGNAL(clicked(void)), this, SLOT(applyFixes(void)));
		connect(&validation_helper, SIGNAL(s_validationFinished(void)), this, SLOT(reenableValidation(void)));
		connect(&validation_helper, SIGNAL(s_validationCanceled(void)), this, SLOT(reenableValidation(void)));
		connect(&validation_helper, SIGNAL(s_sqlValidationStarted(bool)), options_btn, SLOT(setDisabled(bool)));
		connect(&validation_helper, SIGNAL(s_sqlValidationStarted(bool)), clear_btn, SLOT(setDisabled(bool)));
		connect(&validation_helper, SIGNAL(s_sqlValidationStarted(bool)), options_frm, SLOT(setDisabled(bool)));
		connect(&validation_helper, SIGNAL(s_fixApplied(void)), this, SLOT(clearOutput(void)));
		connect(&validation_helper, SIGNAL(s_fixApplied(void)), prog_info_wgt, SLOT(show(void)));
		connect(cancel_btn, SIGNAL(clicked(void)), this, SLOT(cancelValidation(void)));
		connect(swap_ids_btn, SIGNAL(clicked(void)), this, SLOT(swapObjectsIds(void)));
	}
	catch(Exception &e)
	{
		throw Exception(e.getErrorMessage(),e.getErrorType(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
	}
}
void ModelValidationHelper::captureThreadError(Exception e)
{
	export_thread->quit();
	warn_count++;

	/* Indicates the model invalidation only when there are validation warnings (broken refs. or no unique name)
	sql errors are ignored since validator cannot fix SQL related problems */
	db_model->setInvalidated(error_count > 0);

	emit s_validationInfoGenerated(ValidationInfo(e));
}
void ModelValidationHelper::generateValidationInfo(unsigned val_type, BaseObject *object, vector<BaseObject *> refs)
{
  if(!refs.empty())
  {
    //Configures a validation info
    ValidationInfo info=ValidationInfo(val_type, object, refs);
    error_count++;

    val_infos.push_back(info);

    //Emit the signal containing the info
    emit s_validationInfoGenerated(info);
  }
}
void ModelValidationHelper::validateModel(void)
{
	if(!db_model)
		throw Exception(ERR_OPR_NOT_ALOC_OBJECT,__PRETTY_FUNCTION__,__FILE__,__LINE__);

	try
	{
		ObjectType types[]={ OBJ_ROLE, OBJ_TABLESPACE, OBJ_SCHEMA, OBJ_LANGUAGE, OBJ_FUNCTION,
												 OBJ_TYPE, OBJ_DOMAIN, OBJ_SEQUENCE, OBJ_OPERATOR, OBJ_OPFAMILY,
												 OBJ_OPCLASS, OBJ_COLLATION, OBJ_TABLE, OBJ_EXTENSION, OBJ_VIEW },
								aux_types[]={ OBJ_TABLE, OBJ_VIEW },
							 tab_obj_types[]={ OBJ_CONSTRAINT, OBJ_INDEX };
		unsigned i, i1, cnt, aux_cnt=sizeof(aux_types)/sizeof(ObjectType),
						count=sizeof(types)/sizeof(ObjectType), count1=sizeof(tab_obj_types)/sizeof(ObjectType);
		BaseObject *object=nullptr, *refer_obj=nullptr;
		vector<BaseObject *> refs, refs_aux, *obj_list=nullptr;
		vector<BaseObject *>::iterator itr;
		TableObject *tab_obj=nullptr;
		ValidationInfo info;
		Table *table=nullptr;
		Constraint *constr=nullptr;
		map<QString, vector<BaseObject *> > dup_objects;
		map<QString, vector<BaseObject *> >::iterator mitr;
		QString name, signal_msg="`%1' (%2)";

		warn_count=error_count=progress=0;
		val_infos.clear();
		valid_canceled=false;

		/* Step 1: Validating broken references. This situation happens when a object references another
		whose id is smaller than the id of the first one. */
		for(i=0; i < count && !valid_canceled; i++)
		{
			obj_list=db_model->getObjectList(types[i]);
			itr=obj_list->begin();

			while(itr!=obj_list->end()&& !valid_canceled)
			{
				object=(*itr);
				itr++;

				//Excluding the validation of system objects (created automatically)
				if(!object->isSystemObject())
				{
					emit s_objectProcessed(signal_msg.arg(object->getName()).arg(object->getTypeName()), object->getObjectType());

					db_model->getObjectReferences(object, refs);
					refs_aux.clear();

					while(!refs.empty() && !valid_canceled)
					{
						//Checking if the referrer object is a table object. In this case its parent table is considered
						tab_obj=dynamic_cast<TableObject *>(refs.back());
						constr=dynamic_cast<Constraint *>(tab_obj);

						/* If the current referrer object has an id less than reference object's id
						then it will be pushed into the list of invalid references. The only exception is
						for foreign keys that are discarded from any validation since they are always created
						at end of code defintion being free of any reference breaking. */
						if(object != refs.back() &&
							 (!constr || (constr && constr->getConstraintType()!=ConstraintType::foreign_key)) &&
							 ((!tab_obj && refs.back()->getObjectId() <= object->getObjectId()) ||
								(tab_obj && !tab_obj->isAddedByRelationship() &&
								 tab_obj->getParentTable()->getObjectId() <= object->getObjectId())))
						{
							if(tab_obj)
								refer_obj=tab_obj->getParentTable();
							else
								refer_obj=refs.back();

							//Push the referrer object only if not exists on the list
							if(std::find(refs_aux.begin(), refs_aux.end(), refer_obj)==refs_aux.end())
								refs_aux.push_back(refer_obj);
						}

						refs.pop_back();
					}

					//Case there is broken refereces to the object
					if(!refs_aux.empty())
					{
						//Configures a validation info
						info=ValidationInfo(ValidationInfo::BROKEN_REFERENCE, object, refs_aux);
						error_count++;

						val_infos.push_back(info);

						//Emit the signal containing the info
						emit s_validationInfoGenerated(info);
					}
				}
			}

			//Emit a signal containing the validation progress
			progress=((i+1)/static_cast<float>(count))*20;
			emit s_progressUpdated(progress, "");

			sleepThread(5);
		}


		/* Step 2: Validating name conflitcs between primary keys, unique keys, exclude constraints
		and indexs of all tables/views. The table and view names are checked too. */
		obj_list=db_model->getObjectList(OBJ_TABLE);
		itr=obj_list->begin();

		//Searching the model's tables and gathering all the constraints and index
		while(itr!=obj_list->end() && !valid_canceled)
		{
			table=dynamic_cast<Table *>(*itr);

			emit s_objectProcessed(signal_msg.arg(table->getName()).arg(object->getTypeName()), table->getObjectType());

			itr++;

			for(i=0; i < count1 && !valid_canceled; i++)
			{
				cnt=table->getObjectCount(tab_obj_types[i]);

				for(i1=0; i1 < cnt && !valid_canceled; i1++)
				{
					//Get the table object (constraint or index)
					tab_obj=dynamic_cast<TableObject *>(table->getObject(i1, tab_obj_types[i]));

					//Configures the full name of the object including the parent name
					name=tab_obj->getParentTable()->getSchema()->getName(true) + "." + tab_obj->getName(true);
					name.remove("\"");

					//Trying to convert the object to constraint
					constr=dynamic_cast<Constraint *>(tab_obj);

					/* If the object is an index or	a primary key, unique or exclude constraint,
					insert the object on duplicated	objects map */
					if((!constr ||
							(constr && (constr->getConstraintType()==ConstraintType::primary_key ||
													constr->getConstraintType()==ConstraintType::unique ||
													constr->getConstraintType()==ConstraintType::exclude))))
						dup_objects[name].push_back(tab_obj);
				}
			}

			sleepThread(5);
		}

		/* Inserting the tables and views to the map in order to check if there is table objects
			 that conflicts with thems */
		for(i=0; i < aux_cnt && !valid_canceled; i++)
		{
			obj_list=db_model->getObjectList(aux_types[i]);
			itr=obj_list->begin();
			while(itr!=obj_list->end() && !valid_canceled)
			{
				dup_objects[(*itr)->getName(true).remove("\"")].push_back(*itr);
				itr++;
			}

			sleepThread(5);
		}

		//Checking the map of duplicated objects
		mitr=dup_objects.begin();
		i=1;
		while(mitr!=dup_objects.end() && !valid_canceled)
		{
			/* If the vector of the current map element has more the one object
			indicates the duplicity thus generates a validation info */
			if(mitr->second.size() > 1)
			{
				refs.assign(mitr->second.begin() + 1, mitr->second.end());

				//Configures a validation info
				info=ValidationInfo(ValidationInfo::NO_UNIQUE_NAME, mitr->second.front(), refs);
				error_count++;
				refs.clear();

				val_infos.push_back(info);

				//Emit the signal containing the info
				emit s_validationInfoGenerated(info);
			}

			//Emit a signal containing the validation progress
			progress=20 + ((i/static_cast<float>(dup_objects.size()))*20);
			emit s_progressUpdated(progress, "");

			i++; mitr++;
			sleepThread(5);
		}

		if(!valid_canceled && !fix_mode)
		{
			//Step 3 (optional): Validating the SQL code onto a local DBMS.
			//Case the connection isn't specified indicates that the SQL validation will not be executed
			if(!conn)
			{
				//Emit a signal indicating the final progress
				emitValidationFinished();
			}
			//SQL validation only occurs when the model is completely validated.
			else
			{
				//If there is no errors start the dbms export thread
				if(error_count==0)
				{
					export_thread->start();
					emit s_sqlValidationStarted(true);
				}
				else
				{
					warn_count++;
					emitValidationFinished();
					emit s_validationInfoGenerated(ValidationInfo(trUtf8("There are pending errors! SQL validation will not be executed.")));
				}
			}
		}
	}
	catch(Exception &e)
	{
		throw Exception(e.getErrorMessage(), e.getErrorType(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
	}
}