//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// dlgSelectConnection.cpp - Connect to a database
//
//////////////////////////////////////////////////////////////////////////

// App headers
#include "pgAdmin3.h"

// wxWindows headers
#include <wx/wx.h>
#include <wx/bmpcbox.h>

// App headers
#include "frm/frmMain.h"
#include "dlg/dlgSelectConnection.h"
#include "dlg/dlgConnect.h"
#include "schema/pgServer.h"
#include "images/connect.pngc"

#define CTRLID_CBSERVER 4242
#define CTRLID_CBDATABASE 4243
#define CTRLID_CBUSERNAME 4244
#define CTRLID_CBROLENAME 4245
#define CTRLID_CBGROUP 4246

BEGIN_EVENT_TABLE(dlgSelectConnection, DialogWithHelp)
    EVT_COMBOBOX(CTRLID_CBGROUP,       dlgSelectConnection::OnChangeGroup)
	EVT_COMBOBOX(CTRLID_CBSERVER,      dlgSelectConnection::OnChangeServer)
	EVT_COMBOBOX(CTRLID_CBDATABASE,    dlgSelectConnection::OnChangeDatabase)
	EVT_TEXT(CTRLID_CBSERVER,          dlgSelectConnection::OnTextChange)
	EVT_TEXT(CTRLID_CBDATABASE,        dlgSelectConnection::OnTextChange)
	EVT_TEXT(CTRLID_CBUSERNAME,        dlgSelectConnection::OnTextChange)
	EVT_BUTTON (wxID_OK,               dlgSelectConnection::OnOK)
	EVT_BUTTON (wxID_CANCEL,           dlgSelectConnection::OnCancel)
END_EVENT_TABLE()

#define stUsername		CTRL_STATIC("stUsername")


dlgSelectConnection::dlgSelectConnection(wxWindow *parent, frmMain *form) :
	DialogWithHelp(form)
{
	long style = wxCB_DROPDOWN;
	remoteServer = NULL;

	SetFont(settings->GetSystemFont());
	LoadResource(parent, wxT("dlgSelectConnection"));

	SetIcon(*connect_png_ico);
	RestorePosition();

	if (form != NULL)
		style |= wxCB_READONLY;

	cbGroup = new wxComboBox(this, CTRLID_CBGROUP, wxEmptyString, ConvertDialogToPixels(wxPoint(70, 5)), ConvertDialogToPixels(wxSize(135, 12)), wxArrayString(), style);
	cbServer = new ctlComboBoxFix(this, CTRLID_CBSERVER, ConvertDialogToPixels(wxPoint(70, 20)), ConvertDialogToPixels(wxSize(135, 12)), style);
	cbDatabase = new wxComboBox(this, CTRLID_CBDATABASE, wxEmptyString, ConvertDialogToPixels(wxPoint(70, 35)), ConvertDialogToPixels(wxSize(135, 12)), wxArrayString(), style);
	cbUsername = new wxComboBox(this, CTRLID_CBUSERNAME, wxEmptyString, ConvertDialogToPixels(wxPoint(70, 50)), ConvertDialogToPixels(wxSize(135, 12)), wxArrayString(), style);
	cbRolename = new wxComboBox(this, CTRLID_CBROLENAME, wxEmptyString, ConvertDialogToPixels(wxPoint(70, 75)), ConvertDialogToPixels(wxSize(135, 12)), wxArrayString(), style);

	if (form == NULL)
	{
		cbServer->SetValue(settings->Read(wxT("QuickConnect/server"), wxEmptyString));
		cbGroup->SetValue(settings->Read(wxT("QuickConnect/group"), wxEmptyString));
		cbDatabase->SetValue(settings->Read(wxT("QuickConnect/database"), wxEmptyString));
		cbUsername->SetValue(settings->Read(wxT("QuickConnect/username"), wxEmptyString));
		cbRolename->SetValue(settings->Read(wxT("QuickConnect/rolename"), wxEmptyString));
	} else {
		wxTreeItemIdValue foldercookie, servercookie;
		wxTreeItemId folderitem, serveritem;
		ctlTree *browser = mainForm->GetBrowser();
		folderitem = browser->GetFirstChild(browser->GetRootItem(), foldercookie);
		cbGroup->Append("");
		while (folderitem)
		{
			wxString grp=browser->GetItemText(folderitem);
			cbGroup->Append(grp);
			folderitem = browser->GetNextChild(browser->GetRootItem(), foldercookie);
		}
	}

	btnOK->Enable(cbServer->GetValue().Length() > 0 && cbDatabase->GetValue().Length() > 0 && cbUsername->GetValue().Length() > 0);
}


dlgSelectConnection::~dlgSelectConnection()
{
	SavePosition();
}


wxString dlgSelectConnection::GetHelpPage() const
{
	return wxT("connect");
}

void dlgSelectConnection::OnChangeGroup(wxCommandEvent &ev)
{
	int sel = cbGroup->GetCurrentSelection();
	if (sel >= 0)
	{
		bool found=BuildServerCombo(currconn);
		if (!found) cbServer->SetSelection(0);
		OnChangeServer(ev);
	}
}
void dlgSelectConnection::OnChangeServer(wxCommandEvent &ev)
{
	int item;
	wxString olddatabase, oldusername;

	if (!GetServer())
		return;

	// Keep old value for these comboboxes so that we can restore them if needed
	olddatabase = cbDatabase->GetValue();
	oldusername = cbUsername->GetValue();

	// Clear the comboboxes
	cbDatabase->Clear();
	cbUsername->Clear();
	cbRolename->Clear();

	int sel = cbServer->GetCurrentSelection();
	if (sel >= 0)
	{
		remoteServer = (pgServer *)cbServer->wxItemContainer::GetClientData(sel);

		if (!remoteServer->GetConnected())
		{
			remoteServer->Connect(mainForm, remoteServer->GetStorePwd());
			if (!remoteServer->GetConnected())
			{
				wxLogError(wxT("%s"), remoteServer->GetLastError().c_str());
				return;
			}
		}
		if (remoteServer->GetConnected())
		{
			pgSetIterator set1(remoteServer->GetConnection(),
			                   wxT("SELECT DISTINCT datname\n")
			                   wxT("  FROM pg_database db\n")
			                   wxT(" WHERE datallowconn ORDER BY datname"));

			item = 0;
			int item2 = -1;
			wxString v;
			while(set1.RowsLeft())
			{
				v = set1.GetVal(wxT("datname"));
				cbDatabase->Append(v);
				if (v == olddatabase)
					item = cbDatabase->GetCount() - 1;
				else if (v.StartsWith("template") || v == "postgres")
					continue;
				else if (item2 == -1)
					item2 = cbDatabase->GetCount() - 1;
					
			}

			if (cbDatabase->GetCount())
				if (item2>=0)
					{cbDatabase->SetSelection(item2);}
				else 
					{cbDatabase->SetSelection(item);}

			pgSetIterator set2(remoteServer->GetConnection(),
			                   wxT("SELECT DISTINCT usename\n")
			                   wxT("FROM pg_user db\n")
			                   wxT("ORDER BY usename"));

			item = 0;
			while(set2.RowsLeft())
			{
				cbUsername->Append(set2.GetVal(wxT("usename")));
				if (set2.GetVal(wxT("usename")) == oldusername)
					item = cbUsername->GetCount() - 1;
			}

			if (cbUsername->GetCount())
				cbUsername->SetSelection(item);

			if (remoteServer->GetConnection()->BackendMinimumVersion(8, 1))
			{
				pgSetIterator set3(remoteServer->GetConnection(),
				                   wxT("SELECT DISTINCT rolname\n")
				                   wxT("FROM pg_roles db\n")
				                   wxT("ORDER BY rolname"));

				cbRolename->Append(wxEmptyString);

				while(set3.RowsLeft())
					cbRolename->Append(set3.GetVal(wxT("rolname")));

				cbRolename->Enable(true);
			}
			else
				cbRolename->Disable();

			cbRolename->SetValue(wxEmptyString);
		}

	}
	OnChangeDatabase(ev);
}


wxString dlgSelectConnection::GetDatabase()
{
	return cbDatabase->GetValue();
}

wxString dlgSelectConnection::GetServerName()
{
	return cbServer->GetValue();
}

void dlgSelectConnection::OnChangeDatabase(wxCommandEvent &ev)
{
	btnOK->Enable(cbServer->GetValue().Length() > 0 && cbDatabase->GetValue().Length() > 0 && cbUsername->GetValue().Length() > 0);
}

void dlgSelectConnection::OnTextChange(wxCommandEvent &ev)
{
	btnOK->Enable(cbServer->GetValue().Length() > 0 && cbDatabase->GetValue().Length() > 0 && cbUsername->GetValue().Length() > 0);
}

void dlgSelectConnection::OnOK(wxCommandEvent &ev)
{
#ifdef __WXGTK__
	if (!btnOK->IsEnabled())
		return;
#endif

	if (cbDatabase->GetCurrentSelection() == wxNOT_FOUND ||
	        cbServer->GetCurrentSelection() == wxNOT_FOUND)
		remoteServer = NULL;

	EndModal(wxID_OK);
}


void dlgSelectConnection::OnCancel(wxCommandEvent &ev)
{
	EndModal(wxID_CANCEL);
}

pgConn *dlgSelectConnection::CreateConn(wxString &applicationname, bool &createdNew)
{
	/* gcc requires that we store this in temporary variables for some reason... */
	wxString serv = cbServer->GetValue();
	int sel=cbServer->GetSelection();
	if (sel>=0) {
		remoteServer = (pgServer *)cbServer->wxItemContainer::GetClientData(sel);
		if (remoteServer) {
			serv=remoteServer->GetIdentifier();
		}
	}
	wxString db = cbDatabase->GetValue();

	createdNew = true;

	long port = 0;
	if (serv.Find(':') > 0)
	{
		if (!serv.Mid(serv.Find(':') + 1).ToLong(&port))
		{
			wxMessageBox(_("Invalid port number specified."));
			return NULL;
		}
		serv = serv.Mid(0, serv.Find(':'));
	}

	wxString user = cbUsername->GetValue();
	wxString role = cbRolename->GetValue();

	if (cbConnection)
	{
		/* Check if selected combination already exists */
		for (unsigned int index = 0; index < cbConnection->GetCount() - 1; index++)
		{
			pgConn *conn = (pgConn *)cbConnection->GetClientData(index);
			if (conn &&
			        conn->GetHost() == serv &&
			        conn->GetPort() == port &&
			        conn->GetUser() == user &&
			        conn->GetRole() == role &&
			        conn->GetDbname() == db)
			{
				createdNew = false;
				return conn;
			}
		}
	}


	int sslmode = remoteServer ? remoteServer->GetSSL() : 0;
	wxString addconnstr;
	if (remoteServer) {
		addconnstr = remoteServer->GetConnStr();
	}
	return CreateConn(serv, db, user, port, role, addconnstr, sslmode, applicationname, true);
}

pgConn *dlgSelectConnection::CreateConn(wxString &server, wxString &dbname, wxString &username, int port, wxString &rolename, wxString &addconnstr, int sslmode, wxString &applicationname, bool writeMRU)
{
	pgConn *newconn;
	newconn = new pgConn(server, wxEmptyString, wxEmptyString, dbname, username, wxT(""), port, rolename, addconnstr, sslmode, 0, applicationname);
	if (newconn->GetStatus() != PGCONN_OK &&
	        newconn->GetLastError().Cmp(wxString(PQnoPasswordSupplied, wxConvUTF8)) == 0)
	{
		/* Needs password */
		delete newconn;
		newconn = NULL;

		wxString txt;
		txt.Printf(_("Please enter password for user %s\non server %s"), username.c_str(), server.c_str());
		dlgConnect dlg(NULL, txt, false);
		if (dlg.Go() != wxID_OK)
			return NULL;

		newconn = new pgConn(server, wxEmptyString, wxEmptyString, dbname, username, dlg.GetPassword(), port, rolename, addconnstr, sslmode, 0, applicationname);
	}

	if (newconn)
	{
		if (newconn->GetStatus() != PGCONN_OK)
		{
			wxMessageBox(wxT("Connection failed: ") + newconn->GetLastError());
			return NULL;
		}

		if (writeMRU)
		{
			settings->Write(wxT("QuickConnect/server"), cbServer->GetValue());
			settings->Write(wxT("QuickConnect/group"), cbGroup->GetValue());
			settings->Write(wxT("QuickConnect/database"), cbDatabase->GetValue());
			settings->Write(wxT("QuickConnect/username"), cbUsername->GetValue());
			settings->Write(wxT("QuickConnect/rolename"), cbRolename->GetValue());
		}
	}
	return newconn;
}
bool dlgSelectConnection::BuildServerCombo(pgConn *conn) {
	bool foundServer = false;
	if (mainForm != NULL)
	{
		ctlTree *browser = mainForm->GetBrowser();
		wxTreeItemIdValue foldercookie, servercookie;
		wxTreeItemId folderitem, serveritem;
		pgObject *object;
		pgServer *server;
		wxString grp;
		cbServer->Clear();
		cbServer->Refresh();		
	    if (cbGroup->GetCurrentSelection() != wxNOT_FOUND) {
			grp=cbGroup->GetValue();
		}
		folderitem = browser->GetFirstChild(browser->GetRootItem(), foldercookie);
		while (folderitem)
		{
			bool isGroup=true;
				if (grp.Length()>0) 
					if (browser->GetItemText(folderitem)!=grp ) isGroup=false;
			if (browser->ItemHasChildren(folderitem) && isGroup)
			{
				serveritem = browser->GetFirstChild(folderitem, servercookie);
				while (serveritem)
				{
					object = browser->GetObject(serveritem);
					if (object && object->IsCreatedBy(serverFactory))
					{
						server = (pgServer *)object;
						wxString txt=browser->GetItemText(serveritem);
						cbServer->Append(txt, (void *)server);
						if (server->GetConnected() &&
						        server->GetConnection()->GetHost() == conn->GetHost() &&
						        server->GetConnection()->GetPort() == conn->GetPort())
						{
							cbServer->SetSelection(cbServer->GetCount() - 1);
							remoteServer = server;
							foundServer = true;
						}
						cbServer->SetClientData(cbServer->GetCount() - 1, server);
					}
					serveritem = browser->GetNextChild(folderitem, servercookie);
				}
			}
			folderitem = browser->GetNextChild(browser->GetRootItem(), foldercookie);
		}
	}
	
	cbServer->SetFocus();

	wxCommandEvent ev;
	OnChangeServer(ev);

	if (foundServer)
	{

		unsigned int index = 0;
		for (; index < cbDatabase->GetCount(); index++)
		{
			if (cbDatabase->GetString(index) == conn->GetDbname())
			{
				cbDatabase->SetSelection(index);
				break;
			}
		}
		for (index = 0; index < cbUsername->GetCount(); index++)
		{
			if (cbUsername->GetString(index) == conn->GetUser())
			{
				cbUsername->SetSelection(index);
				break;
			}
		}
		for (index = 0; index < cbRolename->GetCount(); index++)
		{
			if (cbRolename->GetString(index) == conn->GetRole())
			{
				cbRolename->SetSelection(index);
				break;
			}
		}
	}
	
	return foundServer;
}
int dlgSelectConnection::Go(pgConn *conn, wxBitmapComboBox *cb)
{
	bool foundServer = false;
	cbConnection = cb;
	currconn=conn;
	wxString grpname=settings->Read(wxT("QuickConnect/group"), wxEmptyString);
	int pos=cbGroup->FindString(grpname);
	cbGroup->SetSelection(pos);
	foundServer = BuildServerCombo(conn);

	return ShowModal();
}

