/////////////////////////////////////////////////////////////////////////////
// Name:        dbobject.cpp
// Purpose:     Database Objects
// Author:      Daniel Horak
// Modified by:
// RCS-ID:      $Id: dbobject.cc,v 1.5 2004/01/04 18:32:16 horakdan Exp $
// Copyright:   (c) Daniel Horak
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWindows headers
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/notebook.h>
#include <wx/ogl/ogl.h>
#include <wx/listimpl.cpp>
#include "config.h"
#include "xml.h"
#include "dbobject.h"
#include "schema.h"

#define ID_LIST			100

DBObject::DBObject(DBObjectType type, const wxString& typestr, DataDesignerProject *project, DataDesignerContainer *container)
	: m_type(type), m_typestr(typestr), m_project(project), m_container(container), m_appended(FALSE), m_shape(NULL)
{
}

DBObject::~DBObject()
{
	DataDesignerSchema *schema;
	
	if (m_appended) {
		m_project->Delete(m_treeitemid);
	}

	if (m_shape) {
		m_shape->Show(FALSE);
		schema = m_project->GetSchema();
		if (schema)
			schema->RemoveShape(m_shape);
	}
}

wxTreeItemId DBObject::AppendItem()
{
	if (! m_appended) {
		m_treeitemid = m_project->AppendItem(m_container->GetTreeItemId(), m_name, -1, -1, new DataDesignerItemData(this));
		m_appended = TRUE;
	}
	
	return m_treeitemid;
}

wxTreeItemId DBObject::InsertItem(const wxTreeItemId& prev)
{
	m_treeitemid = m_project->InsertItem(m_container->GetTreeItemId(), prev, m_name, -1, -1, new DataDesignerItemData(this));
	m_appended = TRUE;
	
	return m_treeitemid;
}

void DBObject::LoadXmlNode(wxXmlNode *node)
{
	wxXmlNode	*child;
	m_name = node->GetPropVal("name", wxEmptyString);
	m_pname = node->GetPropVal("pname", wxEmptyString);
	
	child = node->GetChildren();
	while (child) {
		if (child->GetName() == "remark") {
			LoadTextNode(child, "remark", m_remark);
		} else if (child->GetName() == "description") {
			LoadTextNode(child, "description", m_desc);
		}		
		child = child->GetNext();
	}
	
	m_project->SetItemText(m_treeitemid, m_name);
}

wxXmlNode* DBObject::GetXmlNode()
{
	wxXmlNode	*node;
	
	node = new wxXmlNode(wxXML_ELEMENT_NODE, m_typestr);
	node->AddProperty("name", m_name);
	node->AddProperty("pname", m_pname);
	node->AddChild(GetTextNode("description", m_desc));
	node->AddChild(GetTextNode("remark", m_remark));

	return node;
}

void DBObject::LoadTextNode(wxXmlNode *node, const wxString& name, wxString& value)
{
	wxXmlNode	*elem;
	
	if (! value.IsEmpty()) {
		wxLogMessage("parameter '%s' has been already set to %s", name.c_str(), value.c_str());
		return;
	}
	elem = node->GetChildren();
	if (elem) {
		if (elem->GetType() == wxXML_TEXT_NODE) {
			value = elem->GetContent();
		} else {
			wxLogMessage("child of '%s' node must text node", name.c_str());
		}
	} else {
		wxLogMessage("no child of '%s' node", name.c_str());
		value = wxEmptyString;
	}
}

wxXmlNode *DBObject::GetTextNode(const wxString& name, const wxString& value)
{
	wxXmlNode	*elem, *txt;

	if (value == wxEmptyString)
		return NULL;
	
	txt = new wxXmlNode(wxXML_TEXT_NODE, wxEmptyString, value);
	elem = new wxXmlNode(wxXML_ELEMENT_NODE, name);
	elem->AddChild(txt);
	
	return elem;
}

void DBObject::LoadBoolNode(wxXmlNode *node, const wxString& name, bool& value, bool defval)
{
	wxXmlNode	*elem;
	wxString	boolval;
	
	elem = node->GetChildren();
	if (elem) {
		if (elem->GetType() == wxXML_TEXT_NODE) {
			boolval = elem->GetContent();
			
			if (boolval == "true")
				value = TRUE;
			else if (boolval == "false")
				value = FALSE;
			else {
				wxLogMessage("wrong '%s' specification '%s'", name.c_str(), boolval.c_str());
				value = defval;
			}
		} else {
			wxLogMessage("child of '%s' node must text node", name.c_str());
		}
	} else {
		wxLogMessage("no child of '%s' node", name.c_str());
		value = defval;
	}
}

wxXmlNode *DBObject::GetBoolNode(const wxString& name, const bool& value)
{
	wxXmlNode *elem, *val;
	
	val = new wxXmlNode(wxXML_TEXT_NODE, wxEmptyString, value ? "true" : "false");
	elem = new wxXmlNode(wxXML_ELEMENT_NODE, name);
	elem->AddChild(val);
	
	return elem;
}

void DBObject::LoadBoolProperty(wxXmlNode *node, const wxString& name, bool& value, bool defval)
{
	wxString	boolval;
	
	boolval = node->GetPropVal(name, wxEmptyString);
	if ((boolval == "Y") || (boolval == "y") || (boolval == "T") || (boolval =="t"))
		value = TRUE;
	else if ((boolval == "N") || (boolval == "n") || (boolval == "F") || (boolval =="f"))
		value = FALSE;
	else {
		if (boolval != wxEmptyString)
			wxLogMessage("wrong '%s' specification '%s'", name.c_str(), boolval.c_str());
		value = defval;
	}
}

wxXmlProperty *DBObject::GetBoolProperty(const wxString& name, const bool& value)
{
	wxXmlProperty *prop;
	
	prop = new wxXmlProperty();
	prop->SetName(name);
	prop->SetValue(value ? "Y" : "N");
	
	return prop;
}

void DBObject::CreateShape()
{
}

void DBObject::CreatePName()
{
	m_pname = m_name;
	m_pname.MakeLower();
	m_pname.Replace(" ", "_");
}

void DBObject::CreateNextName()
{
	if (m_container)
		m_name.Printf("%s%d", m_typestr.c_str(), m_container->GetNextId());
	else
		m_name.Printf("%s", m_typestr.c_str());
}

WX_DEFINE_LIST(DBObjectList);

/*
 * Editor
 */
BEGIN_EVENT_TABLE(DBObjectEditor, wxDialog)
	EVT_BUTTON	(ID_BUTTON_COPYNAME,		DBObjectEditor::OnCopyName)
END_EVENT_TABLE()

DBObjectEditor::DBObjectEditor(const wxString& title, const wxSize& size, DBObject *object, bool edit)
	: wxDialog(NULL, -1, title, wxDefaultPosition, size, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
	    m_object(object), m_button_x(10), m_edit(edit)
{
	m_notebook	= new wxNotebook(this, -1);

	m_panel_button	= new wxPanel(this);
	
	wxLayoutConstraints *c = new wxLayoutConstraints;
	c->top.SameAs	(this, wxTop);
	c->left.SameAs	(this, wxLeft);
	c->right.SameAs	(this, wxRight);
	c->bottom.SameAs(m_panel_button, wxTop);
	m_notebook->SetConstraints(c);
	
	c = new wxLayoutConstraints;
	c->height.Absolute(40);
	c->left.SameAs	(this, wxLeft);
	c->right.SameAs	(this, wxRight);
	c->bottom.SameAs(this, wxBottom);
	m_panel_button->SetConstraints(c);

	m_page_general	= new wxPanel(m_notebook);
	m_page_remark	= new wxPanel(m_notebook);
	
	// info on main
	new wxStaticText(m_page_general, -1, _("Logical Name"), wxPoint(10,10), wxSize(80,-1), wxALIGN_RIGHT);
	it1 = new wxTextCtrl(m_page_general, -1, wxEmptyString, wxPoint(100,10), wxSize(100,-1));
	if (m_edit)
		it1->Disable();
		
	m_button_copyname = new wxButton(m_page_general, ID_BUTTON_COPYNAME, ">>", wxPoint(220,10), wxSize(30,-1));

	new wxStaticText(m_page_general, -1, _("Physical Name"), wxPoint(260,10), wxSize(100,-1), wxALIGN_RIGHT);
	it4 = new wxTextCtrl(m_page_general, -1, wxEmptyString, wxPoint(370,10), wxSize(100,-1));

	new wxStaticText(m_page_general, -1, _("Description"), wxPoint(10,35), wxSize(80,-1), wxALIGN_RIGHT);
	it2 = new wxTextCtrl(m_page_general, -1, wxEmptyString, wxPoint(100,35), wxSize(370,-1));
	
	// remark
	it3 = new wxTextCtrl(m_page_remark, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_PROCESS_TAB);

	c = new wxLayoutConstraints;
	c->top.SameAs	(m_page_remark, wxTop);
	c->left.SameAs	(m_page_remark, wxLeft);
	c->right.SameAs	(m_page_remark, wxRight);
	c->bottom.SameAs(m_page_remark, wxBottom);
	it3->SetConstraints(c);
	
	m_page_general->SetAutoLayout(TRUE);
	m_page_remark->SetAutoLayout(TRUE);
	
	m_notebook->AddPage(m_page_general, _("General"));
	m_notebook->AddPage(m_page_remark, _("Remark"));
	
	SetAutoLayout(TRUE);

	m_button_ok	= AddButton(wxID_OK, _("OK"), wxSize(60,-1));
	m_button_cancel	= AddButton(wxID_CANCEL, _("Cancel"), wxSize(60,-1));
	
	m_button_ok->SetDefault();
	
	if (m_edit)
		it2->SetFocus();
	else
		it1->SetFocus();

	Center();
	Layout();		// needed in WXMSW to use layout constraints
}

wxButton *DBObjectEditor::AddButton(wxWindowID id, const wxString& label, const wxSize& size)
{
	wxButton	*button;
	
	button = new wxButton(m_panel_button, id, label, wxPoint(m_button_x, 10), size);
	m_button_x += size.x + 20;
	
	return button;
}

bool DBObjectEditor::TransferDataFromWindow()
{
	DBObject	*object = GetObject();
	
	if (it1->IsModified())
		object->m_name	= it1->GetValue();
		
	object->m_desc 		= it2->GetValue();
	object->m_remark 	= it3->GetValue();
	object->m_pname 	= it4->GetValue();
	
	if (object->m_pname.IsEmpty())
		object->CreatePName();
		
	/* we must also set that the project is modified */
	object->GetProject()->GetSplitter()->GetView()->GetDocument()->Modify(TRUE);
	
	/* refresh schema, should be optimized, so the schema is refreshed only when some visible change is made */
	if (object->GetProject()->GetSchema())
		object->GetProject()->GetSchema()->Refresh();

	return TRUE;
}

bool DBObjectEditor::TransferDataToWindow()
{
	DBObject	*object = GetObject();
	
	if (object->m_name.IsEmpty())
		object->CreateNextName();
	
	it1->SetValue(object->m_name);
	it2->SetValue(object->m_desc);
	it3->SetValue(object->m_remark);
	it4->SetValue(object->m_pname);

	return TRUE;
}

bool DBObjectEditor::Validate()
{
	if (m_edit == FALSE) {
		// check only when creating a new object, during editing it is not possible to change object's name
		if (it1->GetValue().IsEmpty()) {
			wxMessageBox(_("Cannot create object without a name"), _("Error"), wxOK | wxICON_ERROR);
			return FALSE;
		}
		if (GetObject()->GetContainer()->GetObjectByName(it1->GetValue())) {
			wxMessageBox(_("Object with the same name already exists"), _("Error"), wxOK | wxICON_ERROR);
			return FALSE;
		}
	}
	return TRUE;
}

void DBObjectEditor::OnCopyName(wxCommandEvent& event)
{
	if (! it1->GetValue().IsEmpty()) {
		DBObject	*object = GetObject();
		
		object->CreatePName();
		it4->SetValue(object->m_pname);
	}
}

/*
 * ObjectList Control
 */
BEGIN_EVENT_TABLE(DBObjectListCtrl, wxPanel)
	EVT_LIST_ITEM_ACTIVATED	(ID_LIST,		DBObjectListCtrl::OnActivated)
	EVT_LIST_ITEM_SELECTED	(ID_LIST,		DBObjectListCtrl::OnSelected)
	EVT_BUTTON		(ID_BUTTON_ADD,		DBObjectListCtrl::OnAdd)
	EVT_BUTTON		(ID_BUTTON_EDIT,	DBObjectListCtrl::OnEdit)
	EVT_BUTTON		(ID_BUTTON_DELETE,	DBObjectListCtrl::OnDelete)
	EVT_BUTTON		(ID_BUTTON_MOVEUP,	DBObjectListCtrl::OnMoveUp)
	EVT_BUTTON		(ID_BUTTON_MOVEDOWN,	DBObjectListCtrl::OnMoveDown)
END_EVENT_TABLE()

DBObjectListCtrl::DBObjectListCtrl(wxWindow *parent, DataDesignerContainer *container)
	: wxPanel(parent), m_current(-1), m_container(container)
{
	m_list = new wxListCtrl(this, ID_LIST);
	m_list->SetWindowStyleFlag(wxLC_REPORT | wxSUNKEN_BORDER | wxLC_SINGLE_SEL);
	m_list->InsertColumn(0, _("Name"));
	
	m_bpanel = new wxPanel(this);
	
	wxLayoutConstraints *c = new wxLayoutConstraints;
	c->top.SameAs(this, wxTop);
	c->left.SameAs(this, wxLeft);
	c->right.SameAs(this, wxRight);
	c->bottom.SameAs(m_bpanel, wxTop);
	m_list->SetConstraints(c);
	
	c = new wxLayoutConstraints;
	c->height.Absolute(40);
	c->left.SameAs(this, wxLeft);
	c->right.SameAs(this, wxRight);
	c->bottom.SameAs(this, wxBottom);
	m_bpanel->SetConstraints(c);
	
	m_button_add	= new wxButton(m_bpanel, ID_BUTTON_ADD, _("Add"), wxPoint(10,10), wxSize(60,-1));
	m_button_edit	= new wxButton(m_bpanel, ID_BUTTON_EDIT, _("Edit"), wxPoint(80,10), wxSize(60,-1));
	m_button_delete	= new wxButton(m_bpanel, ID_BUTTON_DELETE, _("Delete"), wxPoint(150,10), wxSize(60,-1));
	m_button_moveup	= new wxButton(m_bpanel, ID_BUTTON_MOVEUP, _("Up"), wxPoint(220,10), wxSize(40,-1));
	m_button_movedown	= new wxButton(m_bpanel, ID_BUTTON_MOVEDOWN, _("Down"), wxPoint(270,10), wxSize(40,-1));
	
	SetAutoLayout(TRUE);
}

DBObjectListCtrl::~DBObjectListCtrl()
{
}

long DBObjectListCtrl::InsertColumn(long index, const wxString& heading)
{
	return m_list->InsertColumn(index, heading);
}

long DBObjectListCtrl::InsertItem(long index, const wxString& label)
{
	return m_list->InsertItem(index, label);
}

bool DBObjectListCtrl::SetColumnWidth(int col, int width)
{
	return m_list->SetColumnWidth(col, width);
}

bool DBObjectListCtrl::SetColumnTitle(int col, const wxString& title)
{
	wxListItem	item;
	
	item.m_mask = wxLIST_MASK_TEXT;
	item.m_text = title;
	return m_list->SetColumn(col, item);
}

bool DBObjectListCtrl::SetItemData(long item, long data)
{
	return m_list->SetItemData(item, data);
}

long DBObjectListCtrl::SetItem(long item, long col, const wxString& label)
{
	return m_list->SetItem(item, col, label);
}

void DBObjectListCtrl::AddObject(long item, DBObject *object)
{
	InsertItem(item, object->m_name);
	SetItemData(item, (long)object);
	SetObject(item, object);
}

void DBObjectListCtrl::OnActivated(wxListEvent& event)
{
	m_current = event.GetIndex();

#ifdef ENABLE_DEBUG
	wxLogMessage("OnActivated - m_current=%ld", m_current);
#endif

	OnEditCommon();	
}

void DBObjectListCtrl::OnSelected(wxListEvent& event)
{
	m_current = event.GetIndex();

#ifdef ENABLE_DEBUG
	wxLogMessage("OnSelected - m_current=%ld", m_current);
#endif
	
	if (m_current == 0)
		m_button_moveup->Disable();
	else
		m_button_moveup->Enable();

	if (m_current == m_list->GetItemCount() - 1)
		m_button_movedown->Disable();
	else
		m_button_movedown->Enable();
}

void DBObjectListCtrl::OnAdd(wxCommandEvent& event)
{
	DBObject	*object;
	wxDialog	*editor;
	
	object = m_container->CreateObject();
	if (object == NULL) {
		wxLogMessage("OnAdd - object == NULL");
		return;
	}
	editor = object->Editor(FALSE);
	if (editor) {
		if (editor->ShowModal() == wxID_OK) {
			object->AppendItem();
			AddObject(m_list->GetItemCount(), object);
		} else {
			delete object;
		}
		editor->Destroy();
	}
}

void DBObjectListCtrl::OnEdit(wxCommandEvent& event)
{
	OnEditCommon();
}

void DBObjectListCtrl::OnEditCommon()
{
	DBObject	*object;
	wxDialog	*editor;
	int		res;
	
	if (m_current == -1)
		return;

	object = (DBObject *)(m_list->GetItemData(m_current));
	if (object == NULL) {
		wxLogMessage("OnEditCommon - object == NULL");
		return;
	}
	editor = object->Editor(TRUE);
	if (editor) {
		if ((res = editor->ShowModal()) == wxID_OK) {
			SetObject(m_current, object);
			m_list->SetItemState(m_current, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
			m_list->EnsureVisible(m_current);
		}
		editor->Destroy();
	}
}

void DBObjectListCtrl::OnDelete(wxCommandEvent& event)
{
	DBObject	*object;
	DataDesignerProject	*project;
	
#ifdef ENABLE_DEBUG
	wxLogMessage("OnDelete - m_current=%ld", m_current);
#endif
	
	if (m_current == -1)
		return;

	object = (DBObject *)(m_list->GetItemData(m_current));
	if (object == NULL) {
		wxLogMessage("OnDelete - object == NULL");
		return;
	}
	m_list->DeleteItem(m_current);
	delete object;
}

void DBObjectListCtrl::OnMoveUp(wxCommandEvent& event)
{
	DBObject		*selobj,
				*prevobj;
	DataDesignerProject	*project;
	
	// if no item selected or first item selected, then return
	if ((m_current == -1) || (m_current == 0))
		return;

	selobj	= (DBObject *)(m_list->GetItemData(m_current));
	prevobj	= (DBObject *)(m_list->GetItemData(m_current - 1));
	project = selobj->GetProject();
	project->GetSplitter()->GetView()->GetDocument()->Modify(TRUE);
	
	// move in list
	SwapItems(m_current, m_current - 1);

	// move in tree
	project->SwapItems(selobj->GetTreeItemId(), prevobj->GetTreeItemId());
}

void DBObjectListCtrl::OnMoveDown(wxCommandEvent& event)
{
	DBObject		*selobj,
				*nextobj;
	DataDesignerProject	*project;
	
	// if no item selected or last item selected, then return
	if ((m_current == -1) || (m_current == (m_list->GetItemCount() - 1)))
		return;

	selobj	= (DBObject *)(m_list->GetItemData(m_current));
	nextobj	= (DBObject *)(m_list->GetItemData(m_current + 1));
	project = selobj->GetProject();
	project->GetSplitter()->GetView()->GetDocument()->Modify(TRUE);

	// move in list
	SwapItems(m_current, m_current + 1);

	// move in tree
	project->SwapItems(selobj->GetTreeItemId(), nextobj->GetTreeItemId());
}

void DBObjectListCtrl::SwapItems(long item1, long item2)
{
	DBObject	*object1 = (DBObject *)(m_list->GetItemData(item1));
	DBObject	*object2 = (DBObject *)(m_list->GetItemData(item2));
	
	m_list->SetItemData(item1, (long)object2);
	m_list->SetItemData(item2, (long)object1);
	
	m_list->SetItem(item1, 0, object2->m_name);
	m_list->SetItem(item2, 0, object1->m_name);
	
	SetObject(item1, object2);
	SetObject(item2, object1);

	m_current == -1;

	m_list->SetItemState(item2, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}


syntax highlighted by Code2HTML, v. 0.9.1