/***************************************************************************
            SIMPLY POWERFUL TOOLKIT (SPTK)
            CDialog.cpp  -  description
                                                                                       -------------------
            begin                : Fri Mar 31 2000
            copyright            : (C) 2000-2006 by Alexey S.Parshin
            email                : alexeyp@gmail.com
***************************************************************************/

/***************************************************************************
         This library is free software; you can redistribute it and/or modify it 
         under the terms of the GNU Library General Public License as published by
         the Free Software Foundation; either version 2 of the License, or (at 
         your option) any later version.

         This library 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 Library
         General Public License for more details.

         You should have received a copy of the GNU Library General Public License
         along with this library; if not, write to the Free Software Foundation, 
         Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

         Please report all bugs and problems to "alexeyp@gmail.com"
   ***************************************************************************/

#include <FL/Fl.H>
#include <FL/fl_draw.H>

#include <string.h>
#include <stdio.h>
#include <sptk3/CDatabase.h>
#include <sptk3/CMessageDialog.h>
#include <sptk3/CException.h>
#include <sptk3/CInput.h>
#include <sptk3/CQuery.h>
#include <sptk3/CRegistry.h>
#include <sptk3/CTabs.h>
#include <sptk3/CCheckButtons.h>
#include <sptk3/CRadioButtons.h>
#include <sptk3/string_ext.h>

using namespace std;
using namespace sptk;

static const unsigned headerHeight = 20;

//==============================================================================
void CDialogTabs::prepareNewPage(Fl_Group *page,bool autoColor) {
   CTabs::prepareNewPage(page,autoColor);
	if (pageCount() == 1) {
		box(FL_THIN_DOWN_FRAME);
		showTabs(false);
		page->box(FL_NO_BOX);
	} else {
		box(FL_THIN_UP_BOX);
		showTabs(true);
		page->box(FL_FLAT_BOX);
	}
}
//==============================================================================
CDialog::CDialog(int w, int h, const char *label)
: CWindow(w,h,label) {
	m_queriesBuilt = false;
	m_controlsScanned = false;
	m_keyValue = -1;
	m_defaultButton = NULL;

	fl_cursor(FL_CURSOR_WAIT);
	Fl::check();

	m_selectQuery = new CQuery;
	m_updateQuery = new CQuery;
	m_insertQuery = new CQuery;

	m_buttonGroup = new CGroup("",20,SP_ALIGN_BOTTOM);
	m_okButton = new CButton(SP_OK_BUTTON);
	m_cancelButton = new CButton(SP_CANCEL_BUTTON);
	m_buttonGroup->end();

	m_pages = new CDialogTabs;

	defaultButton(m_okButton);

	newPage("");

	m_modalResult = DMR_NONE;
}

CDialog::~CDialog() {
	delete m_selectQuery;
	delete m_updateQuery;
	delete m_insertQuery;
}

int CDialog::handle(int event) {
	int rc = Fl_Group::handle(event);
	if (rc) return rc;
	switch (event) {
		case FL_KEYBOARD:
			if (Fl::event_key() == FL_Enter) {
				Fl_Button *btn = dynamic_cast<Fl_Button *>(Fl::focus());
				if (btn)
					btn->do_callback();
				else if (m_defaultButton)
					m_defaultButton->do_callback();
				return 1;
			}
			break;
	}
	return 0;
}

void CDialog::defaultButton(CButton *newDefaultButton) {
	unsigned cnt = m_buttonGroup->children();
	for (unsigned bi = 0; bi < cnt; bi++) {
		CButton *button = (CButton *)m_buttonGroup->child(bi);
		button->defaultButton(button == newDefaultButton);
	}
	m_defaultButton = newDefaultButton;
}

bool CDialog::okPressed() {
	try {
		save();
	}
	catch (exception& e) {
		spError(e.what());
		return false;
	}
	return true;
}

bool CDialog::cancelPressed() {
	return true;
}

bool CDialog::showModal() {
	resize(x(),y(),w(),h());
	Fl_Widget *pressed;
	int x = (Fl::w() - w()) / 2;
	int y = (Fl::h() - h()) / 2;
	position(x,y);
	set_modal();
	fl_cursor(FL_CURSOR_DEFAULT);
	show();
	bool rc = false;

	m_modalResult = DMR_NONE;

	while (m_modalResult == DMR_NONE) {
		pressed = Fl::readqueue();

		if (!pressed) Fl::wait(0.1);
		else {
			fl_cursor(FL_CURSOR_WAIT);
			Fl::check();
			if (pressed == this || pressed == m_cancelButton) {
				if (cancelPressed()) {
					m_modalResult = DMR_CANCEL;
					break;
				}
			}
			if (pressed == m_okButton) {
				if (okPressed()) {
					rc = true;
					m_modalResult = DMR_OK;
					break;
				}
			}
			fl_cursor(FL_CURSOR_DEFAULT);
		}
	}

	if (m_modalResult == DMR_USER)
		rc = true;

	fl_cursor(FL_CURSOR_DEFAULT);
	hide();
	Fl::check();
	return rc;
}

void CDialog::database(CDatabase *db) {
	m_selectQuery->database(db);
	m_updateQuery->database(db);
	m_insertQuery->database(db);
}

CDatabase * CDialog::database() const {
	return m_selectQuery->database();
}

void CDialog::table(const string tableName) {
	if (m_tableName != tableName) {
		m_tableName = tableName;
		m_queriesBuilt = false;
	}
}

void CDialog::table(CDatabase *db,const string tb,const string key) {
	database(db);
	table(tb);
	keyField(key);
}

void CDialog::keyField(const string fldName) {
	if (m_keyField != fldName) {
		m_keyValue = -1;
		m_keyField = fldName;
		m_queriesBuilt = false;
	}
}

void CDialog::keyValue(int val) {
	m_keyValue = val;
	try {
		load();
	}
	catch (exception& e) {
		m_keyValue = -1;
		spError(e.what());
	}
}

bool CDialog::buildQueries() {
	string columnNames;
	string paramNames;
	string updateNames;

	if (!m_controlsScanned) scanControls();

	if (m_queriesBuilt) return true;

	CControlList::const_iterator itor = m_defaultFields.begin();
	bool first = true;
	for (; itor != m_defaultFields.end(); itor++) {
		const CControl* control = itor->second;
		const string& fieldName = control->fieldName();
		if (!first) {
			columnNames += "," + fieldName;
			paramNames  += ",:" + fieldName;
			updateNames += "," + fieldName + "=:" + fieldName;
		} else {
			columnNames = fieldName;
			paramNames  = ":" + fieldName;
			updateNames = fieldName + "=:" + fieldName;
		}
		first = false;
	}

	m_selectQuery->sql("SELECT "+columnNames+" FROM "+m_tableName+" WHERE "+m_keyField+"=:key");
	m_insertQuery->sql("INSERT INTO "+m_tableName+"("+columnNames+") VALUES ("+paramNames+")");
	m_updateQuery->sql("UPDATE "+m_tableName+" SET "+updateNames+" WHERE "+m_keyField+"=:key");

	m_queriesBuilt = true;

	return true;
}

bool CDialog::load() {
	buildQueries();
	reset();
	if (m_keyValue > 0) {
		m_selectQuery->param("key").setInteger(m_keyValue);
		m_selectQuery->open();
		CControlList::iterator itor = m_defaultFields.begin();
		for (; itor != m_defaultFields.end(); itor++) {
			try {
				CControl* control = itor->second;
				control->load(m_selectQuery);
			}
			catch (exception& e) {
				spError(e.what());
			}
		}
		m_selectQuery->close();
	}
	return true;
}

bool CDialog::save() {

	if (!m_controlsScanned) scanControls();

	if (!m_defaultFields.size()) return true;

   // Validating data in controls
	CControlList::iterator itor = m_defaultFields.begin();
	for (; itor != m_defaultFields.end(); itor++) {
		CControl* control = itor->second;
		if (!control->valid()) {
			Fl::focus(control->control());
			throw CException(control->label() + " entry has the incorrect value!");
		}
		if (control->flags() & FGE_MANDATORY) {
			string test = control->data();
			if (!trim(test).length()) {
				Fl::focus(control->control());
				throw CException(control->label() + " entry can't be empty!");
			}
		}
	}

	if (!database()) return true;
	buildQueries();
	CQuery *query = m_insertQuery;

	if (m_keyValue > 0) {
		query = m_updateQuery;
		query->param("key").setInteger(m_keyValue);
	}

	itor = m_defaultFields.begin();
	for (; itor != m_defaultFields.end(); itor++) {
		CControl* control = itor->second;
		control->save(query);
	}
	query->exec();

	return true;
}

void CDialog::load(const CXmlNode& node) {
	CGroupXml::load(node);
    loadPosition(node);
}

void CDialog::save(CXmlNode& node) const {
	CGroupXml::save(node);
    savePosition(node);
	node.name("dialog");
}

CControl& CDialog::operator [] (string fieldName) {
	if (!m_controlsScanned) scanControls();
	CControlList::iterator itor = m_allFields.find(fieldName);
	if (itor != m_allFields.end())
		return *itor->second;
	throw CException("The dialog window doesn't have a field '"+fieldName+"'");
}

CButton *CDialog::addExtraButton(CButtonKind buttonKind,const char *label,Fl_Callback_p callbackFunction) {
	m_buttonGroup->begin();
	CButton *extraButton = new CButton(buttonKind,SP_ALIGN_RIGHT,label);
	m_buttonGroup->end();
	if (callbackFunction)
		extraButton->callback(callbackFunction);
	return extraButton;
}

int CDialog::keyValue() const {
	return m_keyValue;
}

void CDialog::alert(const string s) const {
	spWarning(s.c_str());
}

void CDialog::scanControls() {
	m_allFields.clear();
	m_defaultFields.clear();
	m_allFields << *m_pages;
	m_defaultFields = m_allFields;
	m_defaultFields.remove(m_specialProcessingControls);
	m_controlsScanned = true;
	m_queriesBuilt = false;
}

Fl_Group* CDialog::newPage(const char *label,bool autoColor) {
	return m_pages->newPage(label,autoColor);
}

Fl_Group* CDialog::newScroll(const char *label,bool autoColor) {
	return m_pages->newScroll(label,autoColor);
}

bool CDialog::reset() {
	if (!m_controlsScanned) scanControls();
	m_allFields.reset();
	return true;
}
