#include "tk_toplevel.h"
#include "tk_place_layout.h"

#include "tk_spinbox.h"
#include "tk_entry.h"
#include "tk_checkbutton.h"

#include "../../modulemanager/imodule.h"
#include "myvardef.h"

TkTopLevel::t_toplevel_map TkTopLevel::topLevelMap;

const Ptr<TkOptionDef> TkTopLevel::OPT_BORDERWIDTH=new TkOptionDef("borderwidth",TkIntOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_CLASS=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_MENU=new TkOptionDef("menu",TkMenu::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_RELIEF=new TkOptionDef("relief",TkReliefOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_SCREEN=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_USE=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_BACKGROUND=new TkOptionDef("background",TkColorOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_COLORMAP=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_CONTAINER=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_CURSOR=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_HEIGHT=new TkOptionDef("height",TkIntOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_HIGHLIGHTBACKGROUND=new TkOptionDef("highlightbackground",TkColorOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_HIGHLIGHTCOLOR=new TkOptionDef("highlightcolor",TkColorOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_HIGHLIGHTTHICKNESS=new TkOptionDef("highlightthickness",TkIntOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_PADX=new TkOptionDef("padx",TkIntOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_PADY=new TkOptionDef("pady",TkIntOption::type);
const Ptr<TkOptionDef> TkTopLevel::OPT_TAKEFOCUS=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_VISUAL=0;
const Ptr<TkOptionDef> TkTopLevel::OPT_WIDTH=new TkOptionDef("width",TkIntOption::type);

const Ptr<TkOptionDef> TkTopLevel::WO_DESTROY=new TkOptionDef("protocol",TkDestroyProtocolOption::type,false);
const Ptr<TkOptionDef> TkTopLevel::WO_RESIZABLE=new TkOptionDef("resizable",TkResizeOption::type,false);
const Ptr<TkOptionDef> TkTopLevel::WO_GEOMETRY=new TkOptionDef("geometry",TkRectOption::type,false);
const Ptr<TkOptionDef> TkTopLevel::WO_TITLE=new TkOptionDef("title",TkStringOption::type);
const Ptr<TkOptionDef> TkTopLevel::WO_STATE=new TkOptionDef("state",TkWinStateOption::type,false);

const Ptr<TkOptionDef> TkTopLevel::optAll[]=
{
	TkTopLevel::OPT_BORDERWIDTH,
	TkTopLevel::OPT_CLASS,
	TkTopLevel::OPT_MENU,
	TkTopLevel::OPT_RELIEF,
	TkTopLevel::OPT_SCREEN,
	TkTopLevel::OPT_USE,
	TkTopLevel::OPT_BACKGROUND,
	TkTopLevel::OPT_COLORMAP,
	TkTopLevel::OPT_CONTAINER,
	TkTopLevel::OPT_CURSOR,
	TkTopLevel::OPT_HEIGHT,
	TkTopLevel::OPT_HIGHLIGHTBACKGROUND,
	TkTopLevel::OPT_HIGHLIGHTCOLOR,
	TkTopLevel::OPT_HIGHLIGHTTHICKNESS,
	TkTopLevel::OPT_PADX,
	TkTopLevel::OPT_PADY,
	TkTopLevel::OPT_TAKEFOCUS,
	TkTopLevel::OPT_VISUAL,
	TkTopLevel::OPT_WIDTH
};

const char* TkTopLevel::type="TkTopLevel";
const char* TkTopLevel::tkClass="toplevel";


const Ptr<TkOptionDef>* TkTopLevel::getOptionAll() const
{
	return TkTopLevel::optAll;
}

const char* TkTopLevel::getType() const
{
	return TkTopLevel::type;
}

const char* TkTopLevel::getTkClass() const
{
	return TkTopLevel::tkClass;
}

Ptr<TkTopLevel>& TkTopLevel::createMainWindow(TclInterp* interp)
{
	TkTopLevel* topLevel=new TkTopLevel(interp);
	topLevel->guid=TclGuid::forceGuid(".");
	topLevel->created=true;
	
	topLevel->mainWindow=Tk_MainWindow(interp->getInterp());

	topLevel->obj=Tk_NameToWindow(interp->getInterp(),".",topLevel->mainWindow);
	topLevel->id=Tk_GetUid(".");

	topLevel->bindEvents();

	return *(new Ptr<TkTopLevel>(topLevel));
}

void TkTopLevel::doSerialize(TiXmlElement& element) const
{
	TkContainer::doSerialize(element);
	
	element.SetAttribute("geometry",getStrWMOption(WO_GEOMETRY).c_str());
	element.SetAttribute("title",getTitle());
}

void TkTopLevel::doDeserialize(const TiXmlElement& element)
{
	TkContainer::doDeserialize(element);

	setLayoutManager(new TkPlaceLayout());

	TkRectOption* geometry=new TkRectOption();
	geometry->fromStr(element.Attribute("geometry"));
	setWMOption(WO_GEOMETRY,geometry);

	const char* title=element.Attribute("title");
	if (title) setTitle(title);
}

TkTopLevel::TkTopLevel(Ptr<TclInterp> interp):TkContainer(interp)
{
	topLevelMap[guid.getId()]=this;
}

TkTopLevel::~TkTopLevel()
{
	topLevelMap.erase(guid.getId());
}

TkTopLevel* TkTopLevel::getTopLevelByGuid(const string& guid)
{
	t_toplevel_map::iterator it=topLevelMap.find(guid);
	if (it==topLevelMap.end()) return 0;
	return it->second;
}

/*
void createMenu()
{
	menu->create();
	interp->eval(string(getUid())+" configure -menu "+menu->getUid());
}*/

void TkTopLevel::setMenu(TkMenu* menu)
{
	menu->parent=this;
	this->menu=menu;

	if (created) menu->create();

	setOption(TkTopLevel::OPT_MENU,menu);
}

void TkTopLevel::setTitle(const string& title)
{
	setWMOption(TkTopLevel::WO_TITLE,new TkStringOption(title));
}

const char* TkTopLevel::getTitle() const
{
	interp->eval(string("wm title ")+getUid());
	return interp->getStrResult();
}

const TkRectOption& TkTopLevel::getBounds()
{
	int x,y,width,height;
	sscanf(getStrWMOption(WO_GEOMETRY).c_str(),"%dx%d+%d+%d",&width,&height,&x,&y);
		
	return *(new TkRectOption(x,y,width,height));
}

Tcl_Obj* TkTopLevel::getWMOption(const Ptr<TkOptionDef>& def) const
{
	string cmd="winfo ";
	cmd.append(def->getName());
	cmd.append(" ");
	cmd.append(getUid());
	interp->eval(cmd);

	return interp->getObjResult();
}

const string& TkTopLevel::getStrWMOption(const Ptr<TkOptionDef>& def) const
{
	return *(new string(Tcl_GetString(getWMOption(def))));
}
	
void TkTopLevel::setWMOption(const Ptr<TkOptionDef>& def,TObject* option)
{
	if (created)
	{
		if (option->hasParent(TkBaseOption::type))
		{
			((TkBaseOption*)option)->setWidget(this);
		}
		wndOptSet[def]=Ptr<TObject>(option);
		if (def->shouldQuote())
		{
			interp->eval(string("wm ")+def->getName()+" "+getUid()+" \""+option->toString()+"\"");
		}
		else
		{
			interp->eval(string("wm ")+def->getName()+" "+getUid()+" "+option->toString());
		}
	}
	else
	{
		bcWndOptSet[def]=Ptr<TObject>(option);
	}
}

void TkTopLevel::setResizable(bool resizeWidth,bool resizeHeight)
{
	setWMOption(WO_RESIZABLE,new TkResizeOption(resizeWidth,resizeHeight));
}

bool TkTopLevel::isResizableHeight()
{
	TkResizeOption rsz;
	rsz.fromObject(getWMOption(WO_RESIZABLE));
	
	return rsz.isResizableHeight();
}

bool TkTopLevel::isResizableWidth()
{
	TkResizeOption rsz;
	rsz.fromObject(getWMOption(WO_RESIZABLE));

	return rsz.isResizableWidth();
}

void TkTopLevel::setBounds(int width,int height,int x,int y)
{
	setWMOption(WO_GEOMETRY,new TkRectOption(x,y,width,height));
}

void TkTopLevel::setWindowState(TkWinStateOption::opts state)
{
	setWMOption(WO_STATE,new TkWinStateOption(state));	
}

void TkTopLevel::create()
{
	if (menu) menu->create();
	TkContainer::create();

	
	/*if (menu) 
	{
		createMenu();
	}*/

	//setting options
	for(t_option_set::iterator it=bcWndOptSet.begin();it!=bcWndOptSet.end();it++)
	{
		setWMOption(it->first,it->second);
	}
	bcWndOptSet.clear();
}

void TkTopLevel::destroy()
{
	interp->eval(string("destroy ")+getUid());
}

Ptr<TkTopLevelListener>& TkTopLevel::getWindowListener()
{
	return listener;
}

void TkTopLevel::setWindowListener(TkTopLevelListener* listener)
{
	this->listener=listener;
	setWMOption(WO_DESTROY,new TkDestroyProtocolOption(true));
}

TObject* TkTopLevel::clone()
{
	TkBase* obj=new TkTopLevel(interp);

	baseClone(obj);

	return obj;
}

void TkTopLevel::baseClone(TkBase* obj)
{
	TkContainer::baseClone(obj);
	TkTopLevel* topLevel=(TkTopLevel*)obj;

	for(t_option_set::iterator it=wndOptSet.begin();it!=wndOptSet.end();it++)
	{
		if (it->first->isSimple())
		{
			topLevel->setWMOption(it->first,it->second);
		}
	}
	for(t_option_set::iterator it=bcWndOptSet.begin();it!=bcWndOptSet.end();it++)
	{
		if (it->first->isSimple())
		{
			topLevel->setWMOption(it->first,it->second);
		}
	}	
	
	if (!menu.isNull())
		topLevel->menu=menu->clone();
}

void TkTopLevel::setBackgroundColor(TkColorOption* color)
{
	setOption(OPT_BACKGROUND,color);
}

Ptr<TkColorOption>& TkTopLevel::getBackgroundColor() const
{
	TkColorOption* colorOption=new TkColorOption();
	
	colorOption->fromObject(getOption(OPT_BACKGROUND));
	return *(new Ptr<TkColorOption>(colorOption));
}

const char* TkTopLevelListener::type="TkTopLevelListener";

const char* TkTopLevelListener::getType() const
{
	return type;
}

void TkTopLevelListener::onDestroy(TkTopLevel* source)
{
	source->destroy();
}

class DefaultMListener:public VarInstListener
{
	TclVar* tkVar;
public:
	DefaultMListener(TclVar* tkVar):tkVar(tkVar)
	{
	}
	void valueUpdated(VarInst* variable)
	{
		if (tkVar->typeOf(TclStringVar::type))
		{	
			TclStringVar* strVar=(TclStringVar*)tkVar;
			strVar->set(variable->toString());			
		}
		if (tkVar->typeOf(TclBoolVar::type))
		{
			TclBoolVar* boolVar=(TclBoolVar*)tkVar;
			if (tkVar->typeOf(VarBool::type)) // should be always, but just for case
			{
				boolVar->set(((VarBool*)variable)->get());
			}
		}

	}
};


class DefaultListener:public TclVarListener
{
	IModule* module;
	VarInst* var;
	TkBase* comm;
public:
	DefaultListener(IModule* module,VarInst* var,TkBase* comm,TclVar* mVar):module(module),var(var),comm(comm)
	{
		DefaultMListener* mListener=new DefaultMListener(mVar);
		var->addValueListener(mListener);
	}

	void valueUpdated(TclVar* variable)
	{
		try
		{
			if (variable->typeOf(TclStringVar::type))
			{
				TclStringVar* strVar=(TclStringVar*)variable;
				var->fromString(strVar->get());
			}
			if (variable->typeOf(TclBoolVar::type))
			{
				TclBoolVar* boolVar=(TclBoolVar*)variable;
				var->fromString(boolVar->get()?"true":"false");
			}
		}
		catch(ExceptionBase* e)
		{
			cout<<e->stackTrace();
		}
	}

};


void TkTopLevel::attachToModule(IModule* module)
{
	for(t_child_vector::iterator it=childs.begin();it!=childs.end();it++)
	{
		Ptr<TkBase> child=*it;

		Ptr<TXmlObject> xmlObject=(*it)->getTag();
		if (xmlObject.isNull()) continue;
		if (xmlObject->typeOf(MyVarDef::type))
		{
			MyVarDef* varDef=(MyVarDef*)xmlObject.asObject();
			VarInst* varInst=module->getVariable(varDef->def->getName());
			if (child->typeOf(TkSpinBox::type))
			{
				TkSpinBox* spinBox=(TkSpinBox*)child.asObject();
				spinBox->addValueListener(new DefaultListener(module,varInst,spinBox,spinBox->getDefaultVar()));
			}
			if (child->typeOf(TkEntry::type))
			{
				TkEntry* entry=(TkEntry*)child.asObject();
				entry->addValueListenter(new DefaultListener(module,varInst,entry,entry->getDefaultVar()));
//				entry->addValueListener(
			}
			if (child->typeOf(TkCheckButton::type))
			{
				TkCheckButton* checkButton=(TkCheckButton*)child.asObject();
				checkButton->addValueListener(new DefaultListener(module,varInst,checkButton,checkButton->getDefaultVar()));
			}
		}
	}
}
