//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2013, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddTableFigure.cpp - Draw table figure of a model
//
////////////////////////////////////////////////////////////////////////////


#include "pgAdmin3.h"

// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/pen.h>

// App headers
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/figures/ddTextTableItemFigure.h"
#include "dd/dditems/figures/ddColumnFigure.h"
#include "hotdraw/main/hdDrawingView.h"
#include "hotdraw/main/hdDrawingEditor.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "dd/dditems/handles/ddAddColButtonHandle.h"
#include "dd/dditems/locators/ddAddColLocator.h"
#include "dd/dditems/handles/ddAddFkButtonHandle.h"
#include "dd/dditems/locators/ddAddFkLocator.h"
#include "dd/dditems/handles/ddRemoveTableButtonHandle.h"
#include "dd/dditems/locators/ddRemoveTableLocator.h"
#include "dd/dditems/handles/ddMinMaxTableButtonHandle.h"
#include "dd/dditems/locators/ddMinMaxTableLocator.h"
#include "dd/dditems/handles/ddScrollBarHandle.h"
#include "dd/dditems/locators/ddScrollBarTableLocator.h"
#include "dd/dditems/handles/ddSouthTableSizeHandle.h"
#include "dd/dditems/locators/ddTableBottomLocator.h"
#include "dd/ddmodel/ddDBReverseEngineering.h"
#include "hotdraw/utilities/hdGeometry.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "hotdraw/connectors/hdLocatorConnector.h"
#include "hotdraw/main/hdDrawing.h"
#include "dd/ddmodel/ddDatabaseDesign.h"

//Images
#include "images/ddAddColumn.pngc"
#include "images/ddRemoveColumn.pngc"
#include "images/ddAddForeignKey.pngc"
#include "images/ddMaximizeTable.pngc"
#include "images/ddMinimizeTable.pngc"
#include "images/ddRemoveTable.pngc"

/*
All figures title, colums, indexes are store at same array to improve performance in the following order:
	[0] = table border rect
	[1] = table title
	[2] = first column index
	[maxColIndex] = last column index
	[minIdxIndex] = first index index
	[maxIdxIndex] = last index index
*/

void ddTableFigure::Init(wxString tableName, int x, int y)
{
	setKindId(DDTABLEFIGURE);
	internalPadding = 2;
	externalPadding = 4;
	selectingFkDestination = false;

	//Set Value default Attributes
	fontColorAttribute->fontColor = wxColour(49, 79, 79);
	//Set Value default selected Attributes
	lineSelAttribute->pen().SetColour(wxColour(204, 0, 0));
	lineSelAttribute->pen().SetStyle(wxSOLID);
	lineSelAttribute->pen().SetWidth(1);
	fillSelAttribute->brush().SetColour(wxColour(255, 250, 205));
	fillAttribute->brush().SetColour(wxColour(248, 248, 255));
	fontSelColorAttribute->fontColor = wxColour(49, 79, 79);

	//Set table size, width and position
	rectangleFigure = new hdRectangleFigure();
	rectangleFigure->moveTo(0, x, y);
	add(rectangleFigure);

	tableTitle = new ddTextTableItemFigure(tableName, dt_null, NULL);
	tableTitle->setOwnerTable(this);
	tableTitle->setEditable(true);
	tableTitle->moveTo(0, x, y);
	tableTitle->disablePopUp();
	tableTitle->setShowDataType(false);
	add(tableTitle);
	tableTitle->moveTo(0, rectangleFigure->getBasicDisplayBox().x[0] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[0] + internalPadding / 2);

	//Intialize handles
	wxBitmap image = wxBitmap(*ddAddColumn_png_img);
	wxSize valueSize = wxSize(8, 8);
	figureHandles->addItem(new ddAddColButtonHandle((hdIFigure *)this, (hdILocator *)new ddAddColLocator(), image, valueSize));
	image = wxBitmap(*ddAddForeignKey_png_img);
	figureHandles->addItem(new ddAddFkButtonHandle((hdIFigure *)this, (hdILocator *)new ddAddFkLocator(), image, valueSize));
	image = wxBitmap(*ddRemoveTable_png_img);
	figureHandles->addItem(new ddRemoveTableButtonHandle((hdIFigure *)this, (hdILocator *)new ddRemoveTableLocator(), image, valueSize));
	image = wxBitmap(*ddMinimizeTable_png_img);
	wxBitmap image2 = wxBitmap(*ddMaximizeTable_png_img);
	figureHandles->addItem(new ddMinMaxTableButtonHandle((hdIFigure *)this, (hdILocator *)new ddMinMaxTableLocator(), image, image2, valueSize));
	figureHandles->addItem(new ddSouthTableSizeHandle(this, (hdILocator *)new ddTableBottomLocator()));

	//Intialize special handle
	valueSize = wxSize(10, colsRect.GetSize().GetHeight());
	scrollbar = new ddScrollBarHandle(this, (hdILocator *)new ddScrollBarTableLocator(), valueSize);

	//Intialize columns window (min is always 1 in both, with or without cols & indxs)
	colsRowsSize = 0;
	colsWindow = 0;
	idxsRowsSize = 0;
	idxsWindow = 0;

	//Initialize indexes (pointers to array segments)
	maxColIndex = 2;
	minIdxIndex = 2;
	maxIdxIndex = 2;

	//Initialize position where start to draw columns & indexes, this is the value to allow scrollbars
	beginDrawCols = 2;
	beginDrawIdxs = 2;

	//Initialize
	pkName = wxEmptyString;
	ukNames.clear();

	updateTableSize();

	basicDisplayBox.x[0] = x;
	basicDisplayBox.y[0] = y;
	belongsToSchema = false;
}

ddTableFigure::ddTableFigure(wxString tableName, int x, int y):
	hdCompositeFigure()
{
	Init(tableName, x, y);
}

ddTableFigure::ddTableFigure(wxString tableName, int posIdx, int x, int y):
	hdCompositeFigure()
{
	Init(tableName, 0, 0);
	//Check figure available positions for diagrams, add at least needed to allow initialization of the class
	int i, start;
	start = basicDisplayBox.CountPositions();
	for(i = start; i < (posIdx + 1); i++)
	{
		AddPosForNewDiagram();
	}
	syncInternalsPosAt(posIdx, x, y);
}

//Used by persistence classes
void ddTableFigure::InitTableValues(wxArrayString UniqueKeysName, wxString primaryKeyName, int bdc, int bdi, int maxcolsi, int minidxsi, int maxidxsi, int colsrs, int colsw, int idxsrs, int idxsw)
{
	ukNames = UniqueKeysName;
	pkName = primaryKeyName;
	beginDrawCols = bdc;
	beginDrawIdxs = bdi;
	maxColIndex = maxcolsi;
	minIdxIndex = minidxsi;
	maxIdxIndex = maxidxsi;
	colsRowsSize = colsrs;
	colsWindow = colsw;
	idxsRowsSize = idxsrs;
	idxsWindow = idxsw;
	updateTableSize();
}

ddTableFigure::~ddTableFigure()
{
	if(scrollbar)
	{
		if(figureHandles->existsObject(scrollbar))
			figureHandles->removeItem(scrollbar);
		delete scrollbar;
	}
}

void ddTableFigure::AddPosForNewDiagram()
{
	//Add new position to internal calculations figure
	fullSizeRect.addNewXYPosition();
	titleRect.addNewXYPosition();
	titleColsRect.addNewXYPosition();
	colsRect.addNewXYPosition();
	titleIndxsRect.addNewXYPosition();
	indxsRect.addNewXYPosition();
	unScrolledColsRect.addNewXYPosition();
	unScrolledFullSizeRect.addNewXYPosition();
	unScrolledTitleRect.addNewXYPosition();
	//Add to all figure figures
	hdCompositeFigure::AddPosForNewDiagram();
}

void ddTableFigure::RemovePosOfDiagram(int posIdx)
{
	//Remove position for internal calculations figure
	fullSizeRect.removeXYPosition(posIdx);
	titleRect.removeXYPosition(posIdx);
	titleColsRect.removeXYPosition(posIdx);
	colsRect.removeXYPosition(posIdx);
	titleIndxsRect.removeXYPosition(posIdx);
	indxsRect.removeXYPosition(posIdx);
	unScrolledColsRect.removeXYPosition(posIdx);
	unScrolledFullSizeRect.removeXYPosition(posIdx);
	unScrolledTitleRect.removeXYPosition(posIdx);
	//remove position at all figure figures
	hdCompositeFigure::RemovePosOfDiagram(posIdx);
}

ddColumnFigure *ddTableFigure::getColByName(wxString name)
{
	ddColumnFigure *out = NULL;
	ddColumnFigure *f;

	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	iterator->Next(); //Second figure is main title

	while(iterator->HasNext())
	{
		f = (ddColumnFigure *) iterator->Next();
		if(f->getColumnName(false).IsSameAs(name))
		{
			out = f;
			break;
		}
	}
	delete iterator;

	return out;
}

//WARNING: Columns SHOULD BE ADDED only using this function to avoid strange behaviors
void ddTableFigure::addColumn(int posIdx, ddColumnFigure *column)
{
	column->setOwnerTable(this);
	add(column);
	//Update Indexes
	if(maxColIndex == minIdxIndex) //maxColIndex == minIdxIndex means not indexes at this table, then update too
	{
		minIdxIndex++;
		maxIdxIndex++;
	}
	maxColIndex++;
	colsWindow++;  //by default add a column increase initial window
	colsRowsSize++;

	updateTableSize(true);

	//Fix column position at all available positions (Diagrams)
	int i;
	for(i = 0; i < basicDisplayBox.CountPositions(); i++)
	{
		syncInternalsPosAt(i, basicDisplayBox.x[i], basicDisplayBox.y[i]);
	}
}

//WARNING: Function should be called on a table generated from a storage or to sync values after a big change at model (not derived from hotdraw events)
void ddTableFigure::syncInternalsPosAt(int posIdx, int x, int y)
{
	basicDisplayBox.x[posIdx] = x;
	basicDisplayBox.y[posIdx] = y;
	rectangleFigure->moveTo(posIdx, x, y);
	tableTitle->moveTo(posIdx, rectangleFigure->getBasicDisplayBox().x[posIdx] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[posIdx] + internalPadding / 2);
	calcInternalSubAreas(posIdx);
	recalculateColsPos(posIdx);
}

//WARNING: Function should be called on a table generated from a storage or to sync values after a big change at model (not derived from hotdraw events)
void ddTableFigure::syncInternalsPosAt(wxArrayInt &x, wxArrayInt &y)
{
	unsigned int posIdx, pointsCount = tableTitle->getBasicDisplayBox().CountPositions(), finalValue = x.Count();
	//I need to check that figures inside figure have all points too
	while(pointsCount < finalValue)
	{
		AddPosForNewDiagram();
		pointsCount = tableTitle->getBasicDisplayBox().CountPositions();
	}

	//optimize this, because this is hack right now to avoid some weird problem when recreating figure status
	basicDisplayBox.x = x;
	basicDisplayBox.y = y;

	for(posIdx = 0; posIdx < finalValue; posIdx++)
	{
		rectangleFigure->moveTo(posIdx, x[posIdx], y[posIdx]);
		tableTitle->moveTo(posIdx, rectangleFigure->getBasicDisplayBox().x[posIdx] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[posIdx] + internalPadding / 2);
		calcInternalSubAreas(posIdx);
		recalculateColsPos(posIdx);
	}
}

//WARNING: Columns SHOULD BE ADDED only using this columns if was created as an image from storage one
void ddTableFigure::addColumnFromStorage(ddColumnFigure *column)
{
	add(column);
}

void ddTableFigure::removeColumn(int posIdx, ddColumnFigure *column)
{
	//Hack to allow to remove Fk before delete it.
	if(column->isPrimaryKey() || column->isUniqueKey())
	{
		column->setColumnKindToNone();
	}

	column->setOwnerTable(NULL);
	remove(column);

	if(column)
		delete column;
	//Update Indexes
	if(maxColIndex == minIdxIndex) //means not indexes at this table, then update too
	{
		minIdxIndex--;
		maxIdxIndex--;
	}
	maxColIndex--;
	if(colsRowsSize == colsWindow) //only decrease if size of window and columns is the same
		colsWindow--;
	colsRowsSize--;
	if(beginDrawCols > 2)
		beginDrawCols--;
	calcInternalSubAreas(posIdx);
	recalculateColsPos(posIdx);
	if(colsWindow == colsRowsSize) //if handle need to be removed, remove it
	{
		if(figureHandles->existsObject(scrollbar))
			figureHandles->removeItem(scrollbar);
	}
	//hack to update relationship position when table size change
	manuallyNotifyChange(posIdx);
	column = NULL;
}

void ddTableFigure::recalculateColsPos(int posIdx)
{
	wxFont font = fontAttribute->font();
	int defaultHeight = getColDefaultHeight(font);

	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //first figure is always Rect
	int horizontalPos = f->displayBox().x[posIdx] + 2;
	int verticalPos = 0;

	for(int i = 2; i < maxColIndex ; i++)
	{
		f = (hdIFigure *) figureFigures->getItemAt(i); //table title
		if( (i >= beginDrawCols) && (i <= (colsWindow + beginDrawCols)) ) //Visible to draw
		{
			verticalPos = colsRect.y[posIdx] + (defaultHeight * (i - beginDrawCols) + ((i - beginDrawCols) * internalPadding));
			f->moveTo(posIdx, horizontalPos, verticalPos);
		}
		else
			f->moveTo(posIdx, -65000, -65000); //any figure outside canvas (x<0 || y<0) is not draw & not used to calculate displaybox
	}
}



void ddTableFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
{
	int idx = view->getIdx();
	calcInternalSubAreas(idx);

	if(calcScrolled) //Hack to avoid pass view as parameter to calcInternalSubAreas() because is sometimes called outside a paint event
	{
		view->CalcScrolledPosition(fullSizeRect.x[idx], fullSizeRect.y[idx], &fullSizeRect.x[idx], &fullSizeRect.y[idx]);
		view->CalcScrolledPosition(titleRect.x[idx], titleRect.y[idx], &titleRect.x[idx], &titleRect.y[idx]);
		view->CalcScrolledPosition(titleColsRect.x[idx], titleColsRect.y[idx], &titleColsRect.x[idx], &titleColsRect.y[idx]);
		view->CalcScrolledPosition(colsRect.x[idx], colsRect.y[idx], &colsRect.x[idx], &colsRect.y[idx]);
		view->CalcScrolledPosition(titleIndxsRect.x[idx], titleIndxsRect.y[idx], &titleIndxsRect.x[idx], &titleIndxsRect.y[idx]);
		view->CalcScrolledPosition(indxsRect.x[idx], indxsRect.y[idx], &indxsRect.x[idx], &indxsRect.y[idx]);
		calcScrolled = false;
	}

	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //table rectangle
	f->draw(context, view);
	f = (hdIFigure *) figureFigures->getItemAt(1); //table title
	f->draw(context, view);

	for(int i = beginDrawCols; i < (colsWindow + beginDrawCols); i++)
	{
		f = (hdIFigure *) figureFigures->getItemAt(i); //table title
		if(f->displayBox().GetPosition(view->getIdx()).x > 0 && f->displayBox().GetPosition(view->getIdx()).y > 0)
		{
			f->draw(context, view);
		}
	}

	reapplyAttributes(context, view); //reset attributes to default of figure because can be modified at Draw functions.

	//Set Font for title "Columns"
	wxFont font = fontAttribute->font();
	int newSize = font.GetPointSize() * 0.7;
	font.SetPointSize(newSize);
	context.SetFont(font);

	//Draw Columns Title Line 1
	context.DrawLine(titleColsRect.GetTopLeft(idx), titleColsRect.GetTopRight(idx));
	//Draw Columns Title
	context.DrawText(wxT("Columns"), titleColsRect.x[idx] + 3, titleColsRect.y[idx]);
	//Draw Columns Title Line 2
	context.DrawLine(titleColsRect.GetBottomLeft(idx), titleColsRect.GetBottomRight(idx));
	//DrawVertical Lines
	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 11, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 11, titleIndxsRect.GetTopLeft(idx).y);
	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 22, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 22, titleIndxsRect.GetTopLeft(idx).y);
	//Draw Indexes Title Line 1
	context.DrawLine(titleIndxsRect.GetTopLeft(idx), titleIndxsRect.GetTopRight(idx));
	//Draw Indexes Title
	//disable until implemented in a future: context.DrawText(wxT("Indexes"),titleIndxsRect.x+3,titleIndxsRect.y);
	//Draw Indexes Title Line 2
	context.DrawLine(titleIndxsRect.GetBottomLeft(idx), titleIndxsRect.GetBottomRight(idx));

	context.SetFont(fontAttribute->font()); 		//after change font return always to initial one

	//Draw scrollbar is needed
	if(scrollbar && figureHandles->existsObject(scrollbar))
		scrollbar->draw(context, view);

	//Use this in a future
	//Hack to show message to select fk destination table
	if(selectingFkDestination)
	{
		context.SetTextForeground(*wxWHITE);
		wxBrush old = context.GetBrush();
		context.SetBrush(*wxBLACK_BRUSH);

		int w, h, x, y;
		context.GetTextExtent(wxString(wxT("Select Destination table of foreign key")), &w, &h);
		x = fullSizeRect.GetTopLeft(idx).x + (((fullSizeRect.GetTopRight(idx).x - fullSizeRect.GetTopLeft(idx).x) - w) / 2);
		y = fullSizeRect.GetTopLeft(idx).y - h - 2;
		context.DrawRectangle(wxRect(x, y, w, h));
		context.DrawText(wxString(wxT("Select Destination table of foreign key")), x, y);

		context.SetBrush(old);
		context.SetTextForeground(*wxBLACK);
		context.SetBackground(*wxWHITE);

		//don't draw anything else then don't reapply default attributes
	}
}

void ddTableFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
{
	int idx = view->getIdx();
	calcInternalSubAreas(idx);

	if(calcScrolled) //Hack to avoid pass view as parameter to calcInternalSubAreas() because is sometimes called outside a paint event
	{
		view->CalcScrolledPosition(fullSizeRect.x[idx], fullSizeRect.y[idx], &fullSizeRect.x[idx], &fullSizeRect.y[idx]);
		view->CalcScrolledPosition(titleRect.x[idx], titleRect.y[idx], &titleRect.x[idx], &titleRect.y[idx]);
		view->CalcScrolledPosition(titleColsRect.x[idx], titleColsRect.y[idx], &titleColsRect.x[idx], &titleColsRect.y[idx]);
		view->CalcScrolledPosition(colsRect.x[idx], colsRect.y[idx], &colsRect.x[idx], &colsRect.y[idx]);
		view->CalcScrolledPosition(titleIndxsRect.x[idx], titleIndxsRect.y[idx], &titleIndxsRect.x[idx], &titleIndxsRect.y[idx]);
		view->CalcScrolledPosition(indxsRect.x[idx], indxsRect.y[idx], &indxsRect.x[idx], &indxsRect.y[idx]);
		calcScrolled = false;
	}

	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //table rectangle
	f->drawSelected(context, view);
	f = (hdIFigure *) figureFigures->getItemAt(1); //table title
	f->drawSelected(context, view);

	for(int i = beginDrawCols; i < (colsWindow + beginDrawCols); i++)
	{
		f = (hdIFigure *) figureFigures->getItemAt(i); //table title
		if(f->displayBox().GetPosition(view->getIdx()).x > 0 && f->displayBox().GetPosition(view->getIdx()).y > 0)
		{
			f->drawSelected(context, view);
		}
	}

	reapplySelAttributes(context, view); //reset attributes to default of figure because can be modified at Draw functions.
	wxFont font = fontAttribute->font();
	float t = font.GetPointSize();
	int newSize = font.GetPointSize() * 0.7;
	font.SetPointSize(newSize);
	context.SetFont(font);

	//Draw Columns Title Line 1
	context.DrawLine(titleColsRect.GetTopLeft(idx), titleColsRect.GetTopRight(idx));
	//Draw Columns Title
	context.DrawText(wxT("Columns"), titleColsRect.x[idx] + 3, titleColsRect.y[idx]);
	//Draw Columns Title Line 2
	context.DrawLine(titleColsRect.GetBottomLeft(idx), titleColsRect.GetBottomRight(idx));
	//DrawVertical Lines
	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 11, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 11, titleIndxsRect.GetTopLeft(idx).y);
	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 22, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 22, titleIndxsRect.GetTopLeft(idx).y);
	//Draw Indexes Title Line 1
	context.DrawLine(titleIndxsRect.GetTopLeft(idx), titleIndxsRect.GetTopRight(idx));
	//Draw Indexes Title
	//disable until implemented in a future: context.DrawText(wxT("Indexes"),titleIndxsRect.x+3,titleIndxsRect.y);
	//Draw Indexes Title Line 2
	context.DrawLine(titleIndxsRect.GetBottomLeft(idx), titleIndxsRect.GetBottomRight(idx));
}

hdMultiPosRect &ddTableFigure::getBasicDisplayBox()
{
	return basicDisplayBox;
}

void ddTableFigure::setColsRowsWindow(int num)
{
	if(num > 0)
	{
		colsWindow = num;
		wxFont font = fontAttribute->font();
		colsRect.height = getColDefaultHeight(font) * colsWindow;
		colsRect.width = getFiguresMaxWidth();
	}
}

int ddTableFigure::getHeightFontMetric(wxString text, wxFont font)
{
	int width, height;
	wxBitmap emptyBitmap(*ddAddColumn_png_img);
	wxMemoryDC temp_dc;
	temp_dc.SelectObject(emptyBitmap);
	temp_dc.SetFont(font);
	temp_dc.GetTextExtent(text, &width, &height);
	return height;
}

int ddTableFigure::getColDefaultHeight(wxFont font)
{
	if(figureFigures->count() <= 0)
	{
		int width, height;
		wxBitmap emptyBitmap(*ddAddColumn_png_img);
		wxMemoryDC temp_dc;
		temp_dc.SelectObject(emptyBitmap);
		temp_dc.SetFont(font);
		temp_dc.GetTextExtent(wxT("NewColumn"), &width, &height);
		return height;
	}
	else
	{
		hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(1); //table title
		return f->displayBox().height;
	}
}

//Show select fk destination Message Hack
void ddTableFigure::setSelectFkDestMode(bool value)
{
	selectingFkDestination = value;
}

int ddTableFigure::getFiguresMaxWidth()
{
	ddColumnFigure *cf;
	hdGeometry g;

	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	int maxWidth = 0;
	cf = (ddColumnFigure *) iterator->Next(); //Second figure is main title
	maxWidth = g.max(maxWidth, cf->displayBox().width + 20);
	while(iterator->HasNext())
	{
		cf = (ddColumnFigure *) iterator->Next();
		maxWidth = g.max(maxWidth, cf->displayBox().width);
	}
	delete iterator;
	if(figureHandles->existsObject(scrollbar))
		return maxWidth + 11;  //as defined at locator
	else
		return maxWidth;
}

void ddTableFigure::calcInternalSubAreas(int posIdx)
{
	calcScrolled = true;

	int maxWidth = getFiguresMaxWidth() + externalPadding;
	if(maxWidth < 100)
		maxWidth = 100;
	wxFont font = fontAttribute->font();
	int defaultHeight = getColDefaultHeight(font);

	hdRect db = basicDisplayBox.gethdRect(posIdx);

	//*** titleRect
	float t = font.GetPointSize();
	int newSize = font.GetPointSize() * 0.7;
	font.SetPointSize(newSize);
	int colsTitleHeight = getHeightFontMetric(wxT("Columns"), font);

	titleRect.x[posIdx] = db.x;
	titleRect.y[posIdx] = db.y;
	titleRect.width = maxWidth;
	titleRect.height = defaultHeight;

	titleColsRect.x[posIdx] = db.x;
	titleColsRect.y[posIdx] = titleRect.y[posIdx] + titleRect.height;
	titleColsRect.width = maxWidth;
	titleColsRect.height = colsTitleHeight;
	unScrolledTitleRect = titleColsRect;

	//*** colsRect
	colsRect.width = maxWidth;
	if(colsWindow > 0)
		colsRect.height = defaultHeight * colsWindow + (colsWindow * internalPadding);
	else
		colsRect.height = defaultHeight;
	colsRect.x[posIdx] = db.x;
	colsRect.y[posIdx] = titleRect.y[posIdx] + titleRect.height + titleColsRect.height;
	unScrolledColsRect = colsRect;

	//*** idxTitleRect
	titleIndxsRect.width = maxWidth;
	titleIndxsRect.height = colsTitleHeight;
	titleIndxsRect.x[posIdx] = db.x;
	titleIndxsRect.y[posIdx] = colsRect.y[posIdx] + colsRect.height;

	//*** indexesRect
	indxsRect.width = maxWidth;
	indxsRect.height = defaultHeight * idxsWindow + (idxsWindow * internalPadding);
	indxsRect.x[posIdx] = db.x;
	indxsRect.y[posIdx] = titleIndxsRect.y[posIdx] + titleIndxsRect.height;

	//*** FullTable Size
	fullSizeRect.width = maxWidth;
	fullSizeRect.height = titleRect.height + titleColsRect.height + colsRect.height + titleIndxsRect.height + indxsRect.height;
	fullSizeRect.x[posIdx] = db.x;
	fullSizeRect.y[posIdx] = titleRect.y[posIdx];
	unScrolledFullSizeRect = fullSizeRect;

	//Update size
	wxSize sizeValue = fullSizeRect.GetSize();
	rectangleFigure->setSize(sizeValue);
}

void ddTableFigure::updateTableSize(bool notifyChange)
{
	//Step 0: Recalculate displaybox size, in case of an external modification as change of datatype in a fk from a source (data is stored in original table)
	ddColumnFigure *cf;
	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	cf = (ddColumnFigure *) iterator->Next(); //Second figure is main title
	while(iterator->HasNext())
	{
		cf = (ddColumnFigure *) iterator->Next();
		cf->displayBoxUpdate();
	}
	delete iterator;

	//Step 1: Update table size
	calcInternalSubAreas(0);
	basicDisplayBox.SetSize(fullSizeRect.GetSize());
	if(notifyChange)
	{
		//hack to update relationship position when table size change, but need to be notified to all views only doing it right now for first view
		manuallyNotifyChange(0);
	}
}

hdMultiPosRect &ddTableFigure::getColsSpace()
{
	return unScrolledColsRect;
}

hdMultiPosRect &ddTableFigure::getFullSpace()
{
	return unScrolledFullSizeRect;
}

hdMultiPosRect &ddTableFigure::getTitleRect()
{
	return unScrolledTitleRect;
}


int ddTableFigure::getTotalColumns()
{
	return colsRowsSize;
}

int ddTableFigure::getColumnsWindow()
{
	return colsWindow;
}

void ddTableFigure::setColumnsWindow(int posIdx, int value, bool maximize)
{

	if(!maximize)
	{

		//if value >0 && <= max size table && table+offset < maxColIndex with window
		if( (value > 0) && (value <= colsRowsSize) && (maxColIndex >= ( beginDrawCols + value ) ) )
		{
			colsWindow = value;
			calcInternalSubAreas(posIdx);
			recalculateColsPos(posIdx);
		}

		//if special case of needing to modify beginDrawCols then do it
		if( (value > 0) && (value <= colsRowsSize) && (maxColIndex < ( beginDrawCols + value ) ) )
		{
			if( (beginDrawCols + colsWindow) == maxColIndex) //if index is at max
			{
				int diff = value - colsWindow; // value should be always higher tan colsWindows
				if(diff > 0 && (beginDrawCols - diff) >= 0 )
				{
					beginDrawCols -= diff;
					colsWindow = value;
					calcInternalSubAreas(posIdx);
					recalculateColsPos(posIdx);

				}
			}
		}
	}
	else
	{
		beginDrawCols = 2;
		colsWindow = value;
		calcInternalSubAreas(posIdx);
		recalculateColsPos(posIdx);
	}


	//Hide Scrollbar if needed
	if(colsWindow == colsRowsSize)
	{
		if(figureHandles->existsObject(scrollbar))
			figureHandles->removeItem(scrollbar);
	}
	else
	{
		if (!figureHandles->existsObject(scrollbar))
			figureHandles->addItem(scrollbar);
	}

}

void ddTableFigure::columnsWindowUp(int posIdx)  //move window from number to zero
{
	if( beginDrawCols > 2 )
	{
		beginDrawCols--;
		calcInternalSubAreas(posIdx);
		recalculateColsPos(posIdx);
	}
}

void ddTableFigure::columnsWindowDown(int posIdx)  //move window from number to maxcolumns
{
	if( (beginDrawCols + colsWindow) < maxColIndex)
	{
		beginDrawCols++;
		calcInternalSubAreas(posIdx);
		recalculateColsPos(posIdx);
	}
}

int ddTableFigure::getTopColWindowIndex()
{
	return (beginDrawCols - 2);
}

void ddTableFigure::setPkConstraintName(wxString name)
{
	pkName = name;
}

wxString ddTableFigure::getPkConstraintName()
{
	return pkName;
}

wxArrayString ddTableFigure::getAllColumnsNames()
{
	wxArrayString tmp;
	ddColumnFigure *f;
	tmp.Clear();
	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	iterator->Next(); //Second figure is main title

	while(iterator->HasNext())
	{
		f = (ddColumnFigure *) iterator->Next();
		tmp.Add(f->getColumnName(false));
	}
	delete iterator;
	return tmp;
}

wxArrayString ddTableFigure::getAllFkSourceColsNames(bool pk, int ukIndex)
{
	wxArrayString tmp;
	ddColumnFigure *f;
	tmp.Clear();
	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	iterator->Next(); //Second figure is main title

	while(iterator->HasNext())
	{
		f = (ddColumnFigure *) iterator->Next();
		if(pk)
		{
			if(f->isPrimaryKey())
				tmp.Add(f->getColumnName(false));
		}
		else
		{
			if(f->isUniqueKey(ukIndex))
				tmp.Add(f->getColumnName(false));
		}
	}
	delete iterator;
	return tmp;
}

ddColumnFigure *ddTableFigure::getColumnByName(wxString name)
{
	ddColumnFigure *f;
	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	iterator->Next(); //Second figure is main title

	while(iterator->HasNext())
	{
		f = (ddColumnFigure *) iterator->Next();
		if(f->getColumnName().IsSameAs(name))
		{
			return f;
		}
	}
	delete iterator;
	return NULL;
}

wxArrayString &ddTableFigure::getUkConstraintsNames()
{
	return ukNames;
}

wxString ddTableFigure::getTableName()
{
	ddTextTableItemFigure *c = (ddTextTableItemFigure *) figureFigures->getItemAt(1);
	c->setOneTimeNoAlias();
	return c->getText(false);
}

//set Null on all relationship items with a fk column to be delete or a pk to be removed (pk attribute)
void ddTableFigure::prepareForDeleteFkColumn(ddColumnFigure *column)
{
	hdIteratorBase *iterator = observersEnumerator();
	while(iterator->HasNext())
	{
		ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
		if(r->getStartFigure() == this)	//Only update FK of connection with this table as source. source ---<| destination
			r->prepareFkForDelete(column);
	}
	delete iterator;

}

//	Note about observers:
//	A table is observed by several relationships at same time, where that observers
//	are just looking for changes that will affect relationship behavior.
//	Ex: if I delete a pk on observed table (source) all observers (destination)
//		should modify their columns to remove that fk created from that pk column.
// Warning: when a relationship is created an observer is added to both sides of relationship
// because this behavior (needed for update connection) to identify if is an observer
// of source table or destination table, should be check end figure, start!=end and end=this is end figure
// If start = and is recursive
void ddTableFigure::updateFkObservers()
{
	hdIteratorBase *iterator = observersEnumerator();
	while(iterator->HasNext())
	{
		ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
		if(r->getStartFigure() == this)	//Only update FK of connection with this table as source. source ---<| destination
		{
			r->updateForeignKey();
		}
	}
	delete iterator;
}

//If a column change datatype, should alert all others table to adjust their size with new values
void ddTableFigure::updateSizeOfObservers()
{
	//For all tables that are observing this table, update their size
	hdIteratorBase *iterator = observersEnumerator();
	while(iterator->HasNext())
	{
		ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
		ddTableFigure *destFkTable = (ddTableFigure *) r->getEndFigure();
		destFkTable->updateTableSize();
	}
	delete iterator;
}

//drop foreign keys with this table as origin or destination because table is going to be deleted
void ddTableFigure::processDeleteAlert(hdDrawing *drawing)
{
	hdIteratorBase *iterator = observersEnumerator();
	bool repeatFlag;
	do
	{
		repeatFlag = false;
		iterator->ResetIterator();
		while(iterator->HasNext())
		{
			ddRelationshipFigure *rel = (ddRelationshipFigure *) iterator->Next();
			rel->disconnectStart();
			rel->disconnectEnd();

			drawing->getOwnerEditor()->removeFromAllSelections(rel);
			drawing->getOwnerEditor()->deleteModelFigure(rel);
			repeatFlag = true;
			break;
		}
	}
	while(repeatFlag);

	delete iterator;
}

void ddTableFigure::basicMoveBy(int posIdx, int x, int y)
{

	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0);
//Hack to avoid bug in if clause
	int width =  spaceForMovement.GetWidth();
	int height =  spaceForMovement.GetHeight();
	int bottom = f->displayBox().y[posIdx] + f->displayBox().height + y;
	int right = f->displayBox().x[posIdx] + f->displayBox().width + x;
	int left = f->displayBox().x[posIdx] + x;
	int top = f->displayBox().y[posIdx] + y;

//limit movemnt of table figures to canvas space
	if( (left > 0) && (top > 0) && (right < width) && (bottom < height) )
		hdCompositeFigure::basicMoveBy(posIdx, x, y);
}

//Validate status of table for SQL DDL generation
bool ddTableFigure::validateTable(wxString &errors)
{
	bool out = true;
	wxString tmp = wxEmptyString;
	ddColumnFigure *f;

	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //First figure is main rect
	iterator->Next(); //Second figure is main title

	while(iterator->HasNext())
	{
		f = (ddColumnFigure *) iterator->Next();
		if(!f->validateColumn(tmp))
		{
			out = false;
		}
	}

	if(!out)
	{
		errors.Append(wxT("\n"));
		errors.Append(wxT("Errors detected at table") + this->getTableName() + wxT(" \n"));
		errors.Append(tmp);
		errors.Append(wxT("\n"));
	}

	delete iterator;

	return out;
}

//Using some options from http://www.postgresql.org/docs/8.1/static/sql-createtable.html, but new options can be added in a future.
wxString ddTableFigure::generateSQLCreate(wxString schemaName)
{
	//Columns and table
	wxString tmp(wxT("CREATE TABLE "));
	if(!schemaName.IsEmpty())
	{
		tmp += wxT("\"") + schemaName + wxT("\".\"") + getTableName() + wxT("\" (\n");
	}
	else
	{
		tmp += wxT("\"") + getTableName() + wxT("\" (\n");
	}
	hdIteratorBase *iterator = figuresEnumerator();
	iterator->Next(); //Fixed Position for table rectangle
	iterator->Next(); //Fixed Position for table name
	while(iterator->HasNext())
	{
		ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
		tmp += column->generateSQL();
		if(column->isNotNull())
		{
			tmp += wxT(" NOT NULL");
		}
		if(iterator->HasNext())
		{
			tmp += wxT(" , \n");
		}
	}
	tmp += wxT("\n ); ");

	return tmp;
}

wxString ddTableFigure::generateSQLAlterPks(wxString schemaName)
{
	wxString tmp;
	hdIteratorBase *iterator = figuresEnumerator();
	//Pk, Uk Constraints
	iterator->Next(); //Fixed Position for table rectangle
	iterator->Next(); //Fixed Position for table name
	int contPk = 0;
	while(iterator->HasNext())
	{
		ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
		if(column->isPrimaryKey())
			contPk++;
	}
	if(contPk > 0)
	{
		tmp += wxT("\nALTER TABLE ");
		if(!schemaName.IsEmpty())
		{
			tmp += wxT("\"") + schemaName + wxT("\".\"") + getTableName() + wxT("\"");
		}
		else
		{
			tmp += wxT("\"") + getTableName() + wxT("\"") ;
		}

		tmp += wxT(" ADD ");

		if(!pkName.IsEmpty())
		{
			tmp += wxT("CONSTRAINT \"") + pkName + wxT("\" ");
		}
		tmp += wxT("PRIMARY KEY ( ");
		iterator->ResetIterator();
		iterator->Next(); //Fixed Position for table rectangle
		iterator->Next(); //Fixed Position for table name

		while(iterator->HasNext())
		{
			ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
			if(column->isPrimaryKey())
			{
				tmp += wxT("\"") + column->getColumnName() + wxT("\"");
				contPk--;
				if(contPk > 0)
				{
					tmp += wxT(" , ");
				}
				else
				{
					tmp += wxT(" ); ");
				}
			}
		}
	}
	delete iterator;

	return tmp;
}

wxString ddTableFigure::generateSQLAlterFks(wxString schemaName)
{
	wxString tmp;
	hdIteratorBase *iterator = figuresEnumerator();
	//Fk Constraint
	iterator = observersEnumerator();
	if(!iterator->HasNext())
	{
		tmp = wxEmptyString;
	}
	else
	{
		while(iterator->HasNext())
		{
			ddRelationshipFigure *rel = (ddRelationshipFigure *) iterator->Next();
			if(rel->getStartFigure() != this)
			{
				tmp += rel->generateSQL(schemaName);
			}
		}
	}
	delete iterator;
	return tmp;
}

wxString ddTableFigure::generateSQLAlterUks(wxString schemaName)
{
	hdIteratorBase *iterator = figuresEnumerator();

	//Pk, Uk Constraints
	iterator->Next(); //Fixed Position for table rectangle
	iterator->Next(); //Fixed Position for table name
	int MaxUk = -1;
	while(iterator->HasNext())
	{
		ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
		if(column->isUniqueKey() && column->getUniqueConstraintIndex() > MaxUk)
			MaxUk = column->getUniqueConstraintIndex();
	}

	wxString tmp = wxEmptyString;
	if(MaxUk >= 0)
	{
		int i;
		for(i = 0; i <= MaxUk; i++)
		{
			tmp += wxT("\nALTER TABLE ");

			if(!schemaName.IsEmpty())
				tmp += wxT("\"") + schemaName + wxT("\".");
			tmp += wxT("\"") + getTableName() + wxT("\"") ;

			tmp += wxT(" ADD ");

			if(!getUkConstraintsNames()[i].IsEmpty())
			{
				tmp += wxT("CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ") ;
			}
			tmp += wxT("UNIQUE ( ");

			int countUk = 0;
			iterator->ResetIterator();
			iterator->Next(); //Fixed Position for table rectangle
			iterator->Next(); //Fixed Position for table name
			while(iterator->HasNext())
			{
				ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
				if(column->getUniqueConstraintIndex() == i)
					countUk++;
			}

			iterator->ResetIterator();
			iterator->Next(); //Fixed Position for table rectangle
			iterator->Next(); //Fixed Position for table name
			while(iterator->HasNext())
			{
				ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
				if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
				{
					tmp += wxT("\"") + column->getColumnName() + wxT("\"");
					countUk--;
					if(countUk > 0)
					{
						tmp += wxT(", ");
					}
				}
			}
			tmp += wxT(" ); ");
		}
	}
	delete iterator;
	return tmp;
}

wxString ddTableFigure::generateAltersTable(pgConn *connection, wxString schemaName, ddDatabaseDesign *design)
{
	wxString out;

	OID oidTable = ddImportDBUtils::getTableOID(connection, schemaName, getTableName());
	if(oidTable == -1)
	{
		wxMessageBox(wxString::Format(_("Cannot build an ALTER TABLE statement for a non existing table (%s.%s)."),
		                              schemaName.c_str(), getTableName().c_str()), _("Error when trying to get table OID"),  wxICON_ERROR);
		return wxEmptyString;
	}

	ddStubTable *dbTable = ddImportDBUtils::getTable(connection, getTableName(), oidTable);
	if(!dbTable)
	{
		wxMessageBox(wxString::Format(_("Cannot reverse engineering table %s.%s."),
		                              schemaName.c_str(), getTableName().c_str()), _("Error when trying to get stub table"),  wxICON_ERROR);
		return wxEmptyString;
	}

	//--------------- DETECT TABLE LEVEL CHANGES

	// Check if TABLE was renamed
	// **Not supported yet**

	// Check if PRIMARY KEY constraint was renamed
	// Some models don't set a name for the primary key, so we ignore those models
	if(this->getPkConstraintName().Len() > 0 && !dbTable->PrimaryKeyName.IsSameAs(this->getPkConstraintName()))
	{
		out += wxT("\n");
		out += wxT("ALTER INDEX \"") + dbTable->PrimaryKeyName + wxT("\" RENAME TO \"") + this->getPkConstraintName() + wxT("\";");
		out += wxT("\n");
	}

	// Check if a UNIQUE KEY constraint was renamed
	// **Not supported yet** (is it possible?)

	//--------------- DETECT COLUMN LEVEL CHANGES

	// Check if A COLUMN was renamed
	// **Not supported yet**

	// Look for columns that exist in the database but not in the model
	bool pkRemovedflag = false;
	bool ukRemoved = false;
	stubColsHashMap::iterator it;
	ddStubColumn *item;

	for (it = dbTable->cols.begin(); it != dbTable->cols.end(); ++it)
	{
		wxString key = it->first;
		item = it->second;
		ddColumnFigure *column = getColumnByName(key);

		// Check for changes at the column
		if(column)
		{
			// Generate ALTER COLUMN statement if needed
			// Datatype change, length, and precision are checked

			// Temporary conversion fix to datatype of designer should be improved in a future
			wxString dataType = item->typeColumn->Name();
			bool sameDatatype = true, sameScale = true, samePrecision = true;
			int s = -1, p = -1;
			bool useScale = true, needps = false;

			s = item->typeColumn->Length();
			p = item->typeColumn->Precision();

			if(dataType.IsSameAs(wxT("character varying"), false))
			{
				needps = true;
				dataType = wxT("varchar(n)");
			}

			else if(dataType.IsSameAs(wxT("numeric"), false))
			{
				needps = true;
				useScale = false;
				dataType = wxT("numeric(p,s)");
			}
			else if(dataType.IsSameAs(wxT("interval"), false))
			{
				needps = true;
				dataType = wxT("interval(n)");
			}
			else if(dataType.IsSameAs(wxT("bit"), false))
			{
				needps = true;
				dataType = wxT("bit(n)");
			}
			else if(dataType.IsSameAs(wxT("char"), false))
			{
				needps = true;
				dataType = wxT("char(n)");
			}
			else if(dataType.IsSameAs(wxT("varbit"), false))
			{
				needps = true;
				dataType = wxT("varbit(n)");
			}
			else if(dataType.IsSameAs(wxT("character"), false))
			{
				needps = true;
				dataType = wxT("char(n)");
			}

			if(needps)
			{
				if(useScale)
				{
					samePrecision = column->getPrecision() == s;
				}
				else
				{
					samePrecision = column->getPrecision() == s;
					sameScale = column->getScale() == p;
				}
			}


			sameDatatype = column->getRawDataType().IsSameAs(dataType, false);

			if(!samePrecision || !sameScale || !sameDatatype)
			{
				out += wxT("\n");
				out += wxT("ALTER TABLE ");

				if(!schemaName.IsEmpty())
					out += wxT("\"") + schemaName + wxT("\".");
				out += wxT("\"") + getTableName() + wxT("\"") ;

				out	+= wxT(" ALTER COLUMN ") + column->generateSQL(true) + wxT(";");
				out += wxT("\n");
			}
		}
		else  // DROP COLUMN because it doesn't exist in the model anymore
		{
			out += wxT("\n");
			out += wxT("ALTER TABLE ");

			if(!schemaName.IsEmpty())
				out += wxT("\"") + schemaName + wxT("\".");
			out += wxT("\"") + getTableName() + wxT("\"") ;

			out += wxT(" DROP COLUMN \"") + key + wxT("\";");
			out += wxT("\n");
			if(item->isPrimaryKey)
			{
				pkRemovedflag = true;
			}
		}
	}

	// Look for columns that exist in the model but not in the database
	hdIteratorBase *iteratorPK = figuresEnumerator();
	iteratorPK->Next(); //Fixed Position for table rectangle
	iteratorPK->Next(); //Fixed Position for table name

	bool pkAddedflag = false;
	while(iteratorPK->HasNext())
	{
		ddColumnFigure *column = (ddColumnFigure *) iteratorPK->Next();
		if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())  //Exist in model but not in db
		{
			out += wxT("\n");
			out += wxT("ALTER TABLE ");
			if(!schemaName.IsEmpty())
				out += wxT("\"") + schemaName + wxT("\".");
			out += wxT("\"") + getTableName() + wxT("\"") ;
			out += wxT(" ADD COLUMN ") + column->generateSQL() + wxT(";");
			out += wxT("\n");
			if(column->isPrimaryKey())
			{
				pkAddedflag = true;
			}
		}
	}

	// Look for all PRIMARY columns in the database's tables
	bool pkChanged = false;
	iteratorPK->ResetIterator();
	iteratorPK->Next(); //Fixed Position for table rectangle
	iteratorPK->Next(); //Fixed Position for table name
	while(iteratorPK->HasNext())
	{
		ddColumnFigure *column = (ddColumnFigure *) iteratorPK->Next();
		if(column->isPrimaryKey())
		{
			if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())
			{
				pkChanged = true;
			}
			else
			{
				if(!dbTable->cols[column->getColumnName()]->isPrimaryKey)
				{
					pkChanged = true;
				}
			}
		}
	}
	delete iteratorPK;

	// Look for all PRIMARY columns in the model
	stubColsHashMap::iterator itCol;
	ddStubColumn *itemCol;
	for (itCol = dbTable->cols.begin(); itCol != dbTable->cols.end(); ++itCol)
	{
		wxString colStubName = itCol->first;
		itemCol = itCol->second;
		if(itemCol->isPrimaryKey)
		{
			ddColumnFigure *col = getColumnByName(colStubName);
			if(col != NULL)
			{
				if(!col->isPrimaryKey())
				{
					pkChanged = true;
				}
			}
			else
			{
				pkChanged = true;
			}
		}
	}


	// Handle the addition of a new column to the primary key
	if(pkAddedflag || pkRemovedflag || pkChanged)
	{
		// Drop existing primary key
		out += wxT("\n");
		out += wxT("ALTER TABLE ");
		if(!schemaName.IsEmpty())
			out += wxT("\"") + schemaName + wxT("\".");
		out += wxT("\"") + getTableName() + wxT("\"") ;
		out += wxT(" DROP CONSTRAINT \"") + this->getPkConstraintName() + wxT("\";");
		// Create the new one
		out += generateSQLAlterPks(schemaName);
	}

	// Handle changes from NOT NULL to NULL (always after dropping PK)
	for (it = dbTable->cols.begin(); it != dbTable->cols.end(); ++it)
	{
		wxString key = it->first;
		item = it->second;
		ddColumnFigure *column = getColumnByName(key);

		// Check for changes at the column level
		if(column)
		{
			// Generate ALTER COLUMN statement if needed
			// Datatype change, length and precision are handled.

			// Temporary conversion fix to datatype of designer should be improved in a future
			wxString dataType = item->typeColumn->Name();
			bool sameDatatype = true, sameScale = true, samePrecision = true;
			int s = -1, p = -1;

			// Model has now NULL constraint, so drop the NOT NULL constraint
			if(column->isNotNull())
			{
				if(!item->isNotNull)
				{
					out += wxT("\n");
					out += wxT("ALTER TABLE ");
					if(!schemaName.IsEmpty())
						out += wxT("\"") + schemaName + wxT("\".");
					out += wxT("\"") + getTableName() + wxT("\"") ;
					out += wxT(" ALTER COLUMN \"") + column->getColumnName() + _("\" SET NOT NULL;");
					out += wxT("\n");
				}
			}
			else if(!column->isNotNull())
			{
				if(item->isNotNull)
				{
					out += wxT("\n");
					out += wxT("ALTER TABLE \"");
					if(!schemaName.IsEmpty())
						out += wxT("\"") + schemaName + wxT("\".");
					out += wxT("\"") + getTableName() + wxT("\"") ;
					out += wxT("\" ALTER COLUMN \"") + column->getColumnName() + wxT("\" DROP NOT NULL;");
					out += wxT("\n");
				}
			}
		}
	}

	// Check UK conditions
	int i, maxUkn = this->getUkConstraintsNames().Count();
	for(i = 0; i < maxUkn; i++)
	{
		if(this->getUkConstraintsNames()[i].Len() == 0)
		{
			wxMessageBox(wxString::Format(_("Some UNIQUE keys on table %s have no name.\nYou should set a name for them, so that pgAdmin can check consistency with the already available database constraints."),
			                              getTableName().c_str()), _("Trying to build ALTER sentences"),  wxICON_ERROR);
			return wxEmptyString;
		}
	}

	// Search for UNIQUE key deleted from model, but available in the database
	// Two steps process: first drop then delete from wxarraystring
	int maxUkStub = dbTable->UniqueKeysNames.Count();
	for(i = 0; i < maxUkStub; i++)
	{
		// Drop UK [in db but not in model]
		if(this->getUkConstraintsNames().Index(dbTable->UniqueKeysNames[i]) == wxNOT_FOUND)
		{
			// Drop it
			out += wxT("\n");
			out += wxT("DROP INDEX \"") + dbTable->UniqueKeysNames[i] + wxT("\";");
			out += wxT("\n");

			// This index metadata is not useful anymore, so erase it at temporary stub table.
			stubColsHashMap::iterator itDelUkIdx;
			ddStubColumn *itemDelUkIdx;
			for (itDelUkIdx = dbTable->cols.begin(); itDelUkIdx != dbTable->cols.end(); ++itDelUkIdx)
			{
				wxString keyDelUkIdx = itDelUkIdx->first;
				itemDelUkIdx = itDelUkIdx->second;
				if(itemDelUkIdx->uniqueKeyIndex == i)
				{
					itemDelUkIdx->uniqueKeyIndex = -1;
				}
			}
		}
	}

	maxUkStub = dbTable->UniqueKeysNames.Count();
	for(i = 0; i < maxUkStub; i++)
	{
		// Drop UK [in db but not in model]
		if(this->getUkConstraintsNames().Index(dbTable->UniqueKeysNames[i]) == wxNOT_FOUND)
		{
			dbTable->UniqueKeysNames.RemoveAt(i);
			maxUkStub = dbTable->UniqueKeysNames.Count();
		}
	}

	// Search for new UK in model to add ADD CONSTRAINT clause
	int maxUkModel = this->getUkConstraintsNames().Count();
	for(i = 0; i < maxUkModel; i++)
	{
		// Add UK [in model but not in db]
		if(dbTable->UniqueKeysNames.Index(this->getUkConstraintsNames()[i]) == wxNOT_FOUND)
		{
			// Create it
			out += wxT("\nALTER TABLE ");
			if(!schemaName.IsEmpty())
				out += wxT("\"") + schemaName + wxT("\".");
			out += wxT("\"") + getTableName() + wxT("\"") ;
			out += wxT(" ADD ");
			if(!getUkConstraintsNames()[i].IsEmpty())
			{
				out += wxT(" CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ");
			}
			out += wxT(" UNIQUE ( ");

			int countUk = 0;
			hdIteratorBase *iteratorUk = figuresEnumerator();
			iteratorUk->Next(); //Fixed Position for table rectangle
			iteratorUk->Next(); //Fixed Position for table name
			while(iteratorUk->HasNext())
			{
				ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
				if(column->getUniqueConstraintIndex() == i)
					countUk++;
			}

			iteratorUk->ResetIterator();
			iteratorUk->Next(); //Fixed Position for table rectangle
			iteratorUk->Next(); //Fixed Position for table name
			while(iteratorUk->HasNext())
			{
				ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
				if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
				{
					out += wxT("\"") + column->getColumnName() + wxT("\"");
					countUk--;
					if(countUk > 0)
					{
						out += wxT(", ");
					}
					else
					{
						out += wxT(");");
					}
				}
			}
			delete iteratorUk;
		}
	}

	// After delete/create UK, look for changes at existing UK
	// Unified UK indexes at both dbtable and ddTableFigure [same index at dbtable that in tablefigure]
	// BUT in table figure exists indexes without an equivalent in dbtable
	maxUkStub = this->getUkConstraintsNames().Count();
	for(i = 0; i < maxUkStub; i++)
	{
		int oldIndex = dbTable->UniqueKeysNames.Index(this->getUkConstraintsNames()[i]);
		int newIndex = i;

		if(oldIndex != wxNOT_FOUND)
		{
			stubColsHashMap::iterator itDelUkIdx;
			ddStubColumn *itemDelUkIdx;
			for (itDelUkIdx = dbTable->cols.begin(); itDelUkIdx != dbTable->cols.end(); ++itDelUkIdx)
			{
				wxString keyDelUkIdx = itDelUkIdx->first;
				itemDelUkIdx = itDelUkIdx->second;
				if(item->uniqueKeyIndex == oldIndex)
				{
					item->uniqueKeyIndex = newIndex;
				}
			}
		}
	}

	// Generate again those UK that fill one of these conditions:
	// 1. UK at tablefigure have more columns that at stub
	// 2. Uk at stub have more column that at tablefigure
	// Right now, UK constraints have unified indexes (same index at tablefigure and stub)
	maxUkStub = this->getUkConstraintsNames().Count();

	// for each UK
	for(i = 0; i < maxUkStub; i++)
	{
		// Only for UKs with number unified that exists at both sides [db and tablefigure] [with boundaries check]
		int boundarie1 = getUkConstraintsNames().Count();
		int boundarie2 = dbTable->UniqueKeysNames.Count();
		if( (i < boundarie1) && (i < boundarie2) && getUkConstraintsNames().Index( dbTable->UniqueKeysNames[i]) != wxNOT_FOUND )
		{
			// Assumption
			bool createUkAgain = false;

			// CHECK FIRST CONDITION --->   1. UK at tablefigure have more columns that at stub?
			hdIteratorBase *iteratorUk = figuresEnumerator();
			iteratorUk->Next(); //Fixed Position for table rectangle
			iteratorUk->Next(); //Fixed Position for table name

			while(iteratorUk->HasNext())
			{
				ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
				// For each UK column of the constraint with same index (position i, constraint at getUkConstraintsNames()[] )
				if(column->getUniqueConstraintIndex() == i)
				{
					if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())  //found at dbtable that uk column
					{
						createUkAgain = true;
					}
					else
					{
						ddStubColumn *stubCol = dbTable->cols[column->getColumnName()];
						if(!stubCol->isUniqueKey())
						{
							createUkAgain = true;
						}
					}
				}
			}
			delete iteratorUk;

			// CHECK SECOND CONDITION --->   UK at stub have more uk column of same index that at tablefigure?
			stubColsHashMap::iterator itCol;
			ddStubColumn *itemCol;
			for (itCol = dbTable->cols.begin(); itCol != dbTable->cols.end(); ++itCol)
			{
				bool isAtFigure = false;
				wxString colStubName = itCol->first;
				itemCol = itCol->second;
				if(itemCol->uniqueKeyIndex == i)
				{
					ddColumnFigure *colUk = getColumnByName(colStubName);
					if(colUk == NULL)
					{
						createUkAgain = true;
					}
					else
					{
						if(!colUk->isUniqueKey())
						{
							createUkAgain = true;
						}
					}
				}
			}

			if(createUkAgain)
			{
				// Drop it
				out += wxT("\n");
				out += wxT("DROP INDEX \"") + dbTable->UniqueKeysNames[i] + wxT("\";");
				out += wxT("\n");

				// Create it
				out += wxT("\nALTER TABLE ");
				if(!schemaName.IsEmpty())
					out += wxT("\"") + schemaName + wxT("\".");
				out += wxT("\"") + getTableName() + wxT("\"") ;
				out += wxT(" ADD ");
				if(!getUkConstraintsNames()[i].IsEmpty())
				{
					out += wxT(" CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ");
				}
				out += wxT(" UNIQUE ( ");

				int countUk = 0;
				hdIteratorBase *iteratorUk = figuresEnumerator();
				iteratorUk->Next(); //Fixed Position for table rectangle
				iteratorUk->Next(); //Fixed Position for table name
				while(iteratorUk->HasNext())
				{
					ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
					if(column->getUniqueConstraintIndex() == i)
						countUk++;
				}

				iteratorUk->ResetIterator();
				iteratorUk->Next(); //Fixed Position for table rectangle
				iteratorUk->Next(); //Fixed Position for table name
				while(iteratorUk->HasNext())
				{
					ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
					if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
					{
						out += wxT("\"") + column->getColumnName() + wxT("\"");
						countUk--;
						if(countUk > 0)
						{
							out += wxT(", ");
						}
						else
						{
							out += wxT(" );");
						}
					}
				}
				delete iteratorUk;
			}
		}
	}

	// Validate all FKs have a name defined at model
	hdIteratorBase *iteratorRelations = observersEnumerator();
	while(iteratorRelations->HasNext())
	{
		ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
		if(r->getConstraintName().Len() == 0)	// Add to list, FKs with this table as destination. source ---<| destination
		{
			wxMessageBox(wxString::Format(_("Some foreign keys keys on table %s have no name.\nYou should set a name for them, so that pgAdmin can check consistency with the already available database constraints."),
			                              getTableName().c_str()), _("Trying to build ALTER sentences"),  wxICON_ERROR);
			return wxEmptyString;
		}
	}

	//Check Foreign Keys
	// FK at model are same (including attributes and columns) at db
	iteratorRelations->ResetIterator();
	while(iteratorRelations->HasNext())
	{
		ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
		if(r->getEndFigure() == this)	// Only check FK this table as destination. source ---<| destination
		{
			// Check relationship from model exists a db?
			if(ddImportDBUtils::existsFk(connection, dbTable->OIDTable, schemaName, r->getConstraintName(), r->getStartTable()->getTableName()))
			{
				// Columns and other properties are exactly the same? if not, then drop and create again
				if(!ddImportDBUtils::isModelSameDbFk(connection, dbTable->OIDTable, schemaName, r->getConstraintName(), r->getStartTable()->getTableName(), r->getEndTable()->getTableName(), dbTable, r))
				{
					// Drop it first
					out += wxT("\n");
					out += wxT("ALTER TABLE ");
					if(!schemaName.IsEmpty())
						out += wxT("\"") + schemaName + wxT("\".");
					out += wxT("\"") + this->getTableName() + wxT("\"") ;
					out += wxT(" DROP CONSTRAINT \"") + r->getConstraintName() + wxT("\";");
					out += wxT("\n");

					// Then Add it again with changes
					out += r->generateSQL(schemaName);

				}
			}
			else //relationship only exists at model and not in db
			{
				//Create because it doesn't exists
				out += r->generateSQL(schemaName);
			}
		}
	}

	// Check foreign keys available in the database but not in the model
	// because we need to drop them
	// First, create a list of FKs at destination table in the model
	wxArrayString validFks;
	iteratorRelations->ResetIterator();
	while(iteratorRelations->HasNext())
	{
		ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
		if(r->getEndFigure() == this)	// Add to list, FKs with this table as destination. source ---<| destination
		{
			validFks.Add(r->getConstraintName());
		}
	}
	delete iteratorRelations;

	wxArrayString fksToDelete = ddImportDBUtils::getFkAtDbNotInModel(connection, dbTable->OIDTable, schemaName, validFks , design);

	int max = fksToDelete.Count();
	for(i = 0; i < max; i++)
	{
		// Drop it
		out += wxT("\n");
		out += wxT("ALTER TABLE ");
		if(!schemaName.IsEmpty())
			out += wxT("\"") + schemaName + wxT("\".");
		out += wxT("\"") + this->getTableName() + wxT("\"") ;
		out += wxT(" DROP CONSTRAINT \"") + fksToDelete[i] + wxT("\";");
		out += wxT("\n");
	}

	if(dbTable)
		delete dbTable;
	return out;
}
