/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "inc_stl.h"

#include "DragWindow.h"
#include "Metview.h"
#include <Xm/RowColumn.h>
#include "IconObject.h"
#include "IconClass.h"
#include "Request.h"
#include "Drop.h"
#include "XMLabel.h"
#include "XPixmap.h"
#include "Dropping.h"
#include "Language.h"

DragWindow::DragWindow()
{
}

DragWindow::~DragWindow()
{
}

void DragWindow::inputCB(Widget w,XtPointer from,XEvent* event, Boolean *)
{
    if (event->xany.type != ButtonPress || event->xbutton.button != 3)
		return;

	DragWindow* t = (DragWindow*)from;
	t->click(event);
}


void DragWindow::click(XEvent* event)
{
	int button   = event->xbutton.button;
	menu_x_ = event->xbutton.x;
	menu_y_ = event->xbutton.y;

	if(button != 3) return;

	static string cursel = "** Current selection **";

	IconObject* o = objectAt(event->xbutton.x,event->xbutton.y);
	
	current_ = o ? vector<IconObjectH>(1, o) : selection(); 

	Widget m = menu();    
	if(m == 0) return;

	XMLabel title(XtNameToWidget(m,"title"));
	title.set(current_.size() == 1 ? current_[0]->name() : cursel);

	XmMenuPosition(m,(XButtonPressedEvent*)event);
	XtManageChild(m);
}


void DragWindow::install(Widget d)
{
	widget_ = d;
	XtAddEventHandler(d,ButtonPressMask,True,inputCB,this);
    RegisterDropSource(MvApplication::getService(),d,sendDropCB,(void*)this);
	RegisterDropTarget(MvApplication::getService(),d,receiveDropCB,(void*)this);
	XtAddCallback (d, XmNsendmsgCallback, sendmsgCB, (XtPointer) this);
	XtAddCallback (d, XmNanswerCallback, answerCB, (XtPointer) this);
	//XtAddCallback (d, XmNdropCallback, dropCB, (XtPointer) this);
	XtAddCallback (d, XmNmoveCallback, moveCB, (XtPointer) this);
	XtAddCallback (d, XmNdblClickCallback, doubleClickCB, (XtPointer) this);
	XtAddCallback (d, XmNrenameCallback, renameCB, (XtPointer) this);
	XtAddCallback (d, XmNrenamableCallback, renamableCB, (XtPointer) this);
	XtAddCallback (d, XmNflyOverCallback, flyOverCB, (XtPointer) this);
	XtAddCallback (d, XmNgetFullNameCallback, getFullNameCB, (XtPointer) this);

	XtVaSetValues(d,XmNuserData,this,0);

	DropSite::install(d);
}

void DragWindow::sendDropCB(Widget  w,dropid *id, void  *o)
{
	((DragWindow*)o)->sendDropCB(w, id);
}

// Quick and durty, should be somewhere else


void DragWindow::sendDropCB(Widget  w,dropid *id)
{
	cout << "DragWindow::sendDropCB" << endl;
	// Prepare drop in in an external Window...
	MvRequest r = id->header;
	r("_SERVICE") = id->service;

	// Get extra information from the target window
	r("_CONTEXT") = id->context;

	set<IconObject*> icons;
	for(int i=0; i < id->count; i++)
	{
		IconObject* o = objectOf(id->cb[i].icon);
		if(o) icons.insert(o);
	}

	set<IconObject*>::iterator j;

	switch(id->action)
	{
		/* Send name and classes ... */

		case DROP_ACTION_NAME_AND_CLASS:
			for(j = icons.begin(); j != icons.end(); ++j)
			{
				r("NAME")   += (*j)->fullName().c_str();
				r("CLASS")  += (*j)->className().c_str();
			}
			MvApplication::callService(id->service,r,0);
			break;

		/* Send definitions ... */

		case DROP_ACTION_DEFINITION:
			for(j = icons.begin(); j != icons.end(); ++j)
			{
				Request m = (*j)->request();
				m = (*j)->language().expand(m,EXPAND_2ND_NAME);
				m("NAME")   += (*j)->fullName().c_str();
				m("CLASS")  += (*j)->className().c_str();
				r = r + m;
			}
			MvApplication::callService(id->service,r,0);
			break;

		/* Execute and send ... */

		case DROP_ACTION_RESOLVE:
			MvRequest mode(id->mode);
			new Drop(icons,r,id->service,mode);
			break;

	}
}

void DragWindow::receiveDropCB(Widget w,dropid *id, void*o)
{
	((DragWindow*)o)->receiveDropCB(w, id);
}

void DragWindow::receiveDropCB(Widget,dropid *id)
{
	cout << "DragWindow::receiveDropCB" << endl;
}

void DragWindow::sendmsgCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->sendmsgCB ( widget, cb );
}

void DragWindow::flyOverCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->flyOverCB ( widget, cb );
}

void DragWindow::answerCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->answerCB ( widget, cb );
}

void DragWindow::dropCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->dropCB ( widget, cb );
}

void DragWindow::moveCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->moveCB ( widget, cb );
}

void DragWindow::renameCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->renameCB ( widget, cb );
}

void DragWindow::renamableCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->renamableCB ( widget, cb );
}

void DragWindow::doubleClickCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->doubleClickCB ( widget, cb );
}

void DragWindow::getFullNameCB( Widget widget, XtPointer data, XtPointer cb )
{
	DragWindow* o = (DragWindow*) data;
	o->getFullNameCB ( widget, cb );
}


void DragWindow::dropCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	cout << "dropCB " << o << endl;
}

void DragWindow::answerCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	cout << "answerCB " << o << endl;
}

void DragWindow::sendmsgCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;

	// call a virtual method to get the Dropping Object:

	static Dropping* object;
	
	object =  dropping(objectOf(cb->icon),cb->copy);

	cb->message    = &object;
	cb->msg_length = sizeof(&object);

}

void DragWindow::doubleClickCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	if(o) doubleClick(o);
}

void DragWindow::moveCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	IconObject* p = 0;
	if(o) {
		if(cb->event) 
			p = objectAt(cb->event->xbutton.x,cb->event->xbutton.y);

		if(p == o) p = 0;
		
		if(cb->copy)
		{
			cb->move_it = False;
			copy(o,cb->x,cb->y,p);
			selectNone();
		}
		else
			move(o,cb->x,cb->y,p);
	}
}

void DragWindow::renameCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	if(o) rename(o,cb->new_name);
}

void DragWindow::renamableCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	if(o) cb->rename_it = renamable(o);
}

void DragWindow::getFullNameCB(Widget,XtPointer data)
{
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	if(o) 
	{
		strcpy(cb->icon_fullName, o->fullName().c_str());
	}
}


void DragWindow::doubleClick(IconObject*)
{
} 

void DragWindow::move(IconObject*,int,int,IconObject*)
{
} 

void DragWindow::copy(IconObject*,int,int,IconObject*)
{
} 

void DragWindow::rename(IconObject*,const string&)
{
}

typedef pair<DragWindow*,vector<IconObjectH>*> Basic;

static Boolean getSelection(Widget drag,Icon icon,XtPointer data)
{
	Basic* d = static_cast<Basic*>(data);

	if(DragIsIconSelected(drag,icon))
	{
		IconObject* o = d->first->objectOf(icon);
		if(o) d->second->push_back(o);
	}
	return True;
}

vector<IconObjectH> DragWindow::selection()
{
	vector<IconObjectH> result;
	Basic d(this,&result);

	DragScanIcons(widget_,getSelection,&d);

	return result;
}

IconObject* DragWindow::objectOf(Icon icon)
{
	return static_cast<IconObject*>(DragGetIconData(widget_,icon));
}

const IconClass* DragWindow::classOf(Icon icon)
{
	return &IconClass::find(DragGetIconClass(widget_,icon));
}

bool DragWindow::renamable(IconObject* o)
{
	return o->renamable();
}

void DragWindow::addIcon(IconObject* o, int x, int y)
{     
	const char* n = o->name().c_str();
   	const char* c = o->className().c_str();
	//const char* fn = o->fullName().c_str();

	if(o->isLink()) DragSetFontTag(widget_,"italic");

   	Icon i = DragAddIcon(widget_, c, n, o, x, y);

	static XPixmap lock("lock_icon");
	static XPixmap timer("timer_icon");

	if(o->locked())  DragAddIconPixmap(widget_,i,lock.name().c_str(),16,16);
	if(o->isInTimer()) DragAddIconPixmap(widget_,i,timer.name().c_str(),16,16);

	if(o->isLink()) DragSetFontTag(widget_,"normal");
}

void DragWindow::iconPosition(IconObject* o,int& x,int& y)
{
	Icon i = DragFindIconByData(widget_,o);
	if(i) {
		Position px,py;
		DragFindPositionIcon(widget_,i,&px,&py);
		x = px;
		y = py;
	}
}

void DragWindow::closeIcon(IconObject* o)
{     
   	Icon i = DragFindIconByData(widget_,o);
   	if (i) 	DragOpenIcon(widget_, i, False);
}

void DragWindow::openIcon(IconObject* o)
{     
   	Icon i = DragFindIconByData(widget_,o);
   	if (i) 	DragOpenIcon(widget_, i, True);
}

void DragWindow::removeAllIcons()
{
	  DragDeleteAllIcons(widget_);
}

void DragWindow::removeIcon(IconObject* o) 
{
    Icon i = DragFindIconByData(widget_,o);
    if(i != 0) DragDeleteIcon(widget_,i);

}

void DragWindow::sortByName()
{
	DragSortIcons(widget_,DragSortByName);

}

void DragWindow::sortByClass()
{
	DragSortIcons(widget_, DragSortByClass);

}

void DragWindow::toggleSize()
{
	DragUseSmallIcons(widget_,!DragAreSmallIconsUsed(widget_));
}

void DragWindow::refreshIcon(IconObject* o) 
{
 	Icon i = DragFindIconByData(widget_,o);
	if(i != 0) {
		 DragSetIconName(widget_,i,o->name().c_str());
	}
	else {
		cout << "Cannot rename" << endl;
	}

}

class SimpleDropping : public Dropping {
	IconObjectH object_;
	bool copy_;
	virtual IconObject* object(DropSite&) { return object_; } 
	virtual bool copy() { return copy_; }
public:
	 SimpleDropping(IconObject* o,bool c) : object_(o),copy_(c) {}
};

Dropping* DragWindow::dropping(IconObject* o,bool btn2)
{
	return new SimpleDropping(o,btn2 || dropCopy());
}

void DragWindow::selectAll()
{
	DragSelectIcon(widget_,DRAG_ALL_ICONS,True,False);
}

void DragWindow::selectNone()
{
	DragSelectIcon(widget_,DRAG_ALL_ICONS,False,False);
}

void DragWindow::flyOverCB(Widget,XtPointer data)
{
#if 0
	DragCallbackStruct* cb = (DragCallbackStruct*)data;
	IconObject* o = objectOf(cb->icon);
	cout << "flyOverCB " << o << " " << cb->x << " - " << cb->y << endl;

	IconObject* p = objectAt(cb->x,cb->y);
	if(p) cout << *o << " is over " << *p << endl;
#endif
}

IconObject* DragWindow::objectAt(int x,int y)
{
	Icon n = DragFindIconByPosition(widget_,x,y);
	if(n) return objectOf(n);
	return 0;
}
