/***************************** 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 <Assertions.hpp>

#include "XDevice.h"
#include "GraphicsEngine.h"
#include "SubPage.h"

// class XDeviceFactory - builds X Devices
class XDeviceFactory : public DeviceFactory
{
	virtual Device* Build( const MvRequest& deviceRequest ) 
		{ return new XDevice( deviceRequest ); }

public:
	XDeviceFactory( const Cached& name) : DeviceFactory( name ) {}
};

// Instance of XDevice which is loaded in the "factory"
static XDeviceFactory x11DeviceFactoryInstance ( "SCREEN" );

#if 0
/////// Event handlers  and translations //////////////////////////

// For setting up translations in the subpages. The action function
// needs access to the SubPage object, therefore it's done here 
// rather than in OpenGLCanvas that handles other input ( zooming etc).
static char scrollTranslations[] = 
"<Enter>: EnterProc()\n\
<Leave>: LeaveProc()\n\
Ctrl<Btn1Down>,Ctrl<Btn1Up>: ScrollProc(b)\n\
Ctrl<Btn3Down>,Ctrl<Btn3Up>: ScrollProc(f)\n\
<Motion>: MotionProc()";
    
static XtActionsRec scrollActions[] = 
{
	{ "EnterProc" , XDevice::EnterProc  } ,
	{ "LeaveProc" , XDevice::LeaveProc  } ,
	{ "ScrollProc", XDevice::ScrollProc } ,
	{ "MotionProc", XDevice::MotionProc } 
};
static XtTranslations  parsed_xlations;

// Event handler called before a subpage is mapped 
// ( for scrolling/animation).
// This redraws the contents before it's shown, getting rid of
// the flickers that would otherwise be there. Eventmask is
// MapNotify
void XDevice::SubPageMappedHandler ( Widget, XtPointer clientData, 
				     XEvent *event, Boolean * )
{
	OpenGLCanvas* self = (OpenGLCanvas*) clientData;

	if ( event->type == MapNotify )
		// Redraw the window if it's getting mapped.
		self->ExposeProc();
}

// Event handler called when a subpage visibility status changes.
// When an icon is dragged over a page, it gets no Expose, 
// so it does not refresh properly.
// This handler makes sure it redraws itself in these cases, 
// so we don't leave traces of the dragged icon.
// Eventmask is VisibilityNotify.
void XDevice::RedrawCanvasHandler ( Widget, XtPointer clientData, 
				    XVisibilityEvent *event, Boolean * )
{
	OpenGLCanvas* self = (OpenGLCanvas*)clientData;

	if ( event->type  == VisibilityNotify && 
	     event->state != VisibilityFullyObscured )
	{
		// Redraw the window if visibility status changing
//F		self->ExposeProc(); //It seems that it is no longer needed
	}
}

// Action proc for scrolling using keybd/mouse. Should possibly
// be taken away, or integrated with the Scrollvisitor stuff.
void XDevice::ScrollProc ( Widget w, XEvent *,
			   String *params, Cardinal * )
{
	SubPageWidget* subPageWidget;
  
	XtVaGetValues ( w, XmNuserData, &subPageWidget, NULL );
  
	PresentableWidget& pageWidget = subPageWidget->Parent();
	Presentable& page = pageWidget.Owner();

	switch(params[0][0])
	{
	case 'f':
		page.GotoNext();
		break;

	case 'b':
		page.GotoPrevious();
		break;
	}
}

// Ensure that Canvas is redrawn when pointer enters window
// Prevents background to be shown when draging any object over canvas
void XDevice::EnterProc ( Widget w, XEvent *,
			  String *, Cardinal * )
{
	PresentableWidget* presentableWidget;
	XtVaGetValues ( w, XmNuserData, &presentableWidget, NULL );

	Canvas& canvas = presentableWidget->GetCanvas();
	canvas.ExposeProc ();

	// Set Scroll button (if it is possible)
//	presentableWidget->SetScrollButton (true);
}

// Removes Coordinates window when leaving a Window
void XDevice::LeaveProc ( Widget w, XEvent *,
			  String *, Cardinal * )
{
	PresentableWidget* presentableWidget;
	XtVaGetValues ( w, XmNuserData, &presentableWidget, NULL );

	Canvas& canvas = presentableWidget->GetCanvas();
	canvas.OnLeave ();

	// Unset scroll button
//	presentableWidget->SetScrollButton ( false );
}

// Captures pointer motion inside window
void XDevice::MotionProc ( Widget widget, XEvent* event,
			   String*, Cardinal*  )
{
	XmAnyCallbackStruct cb_struct;

	cb_struct.reason = 0;
	cb_struct.event = event;

	XtCallCallbacks( widget, GLwNinputCallback, (XtPointer) &cb_struct );
}
#endif
///////////////////////////////////////////////////////////////

XDevice::XDevice ( const MvRequest& deviceRequest ):
	Device ( deviceRequest )
{
	// Empty
}

XDevice::~XDevice()
{
	// Empty
}

// -- Implementation of the "Visitor" pattern
// -- Each type of node associated to widgets (superpage, page and subpage)
//    is visited by the "XDevice" object, which performs the appropriate action
//    of creating the widgets associated with each presentable
void
XDevice::Visit ( SuperPage& )
{
cout << "XDevice::Visit(Superpage) -> EMPTY CODE" << endl;
#if 0
	// Create a new superpage widget (includes a main window)
	// and save this information as the superpage's device data
	SuperPageWidget* spw =  new SuperPageWidget ( superpage );
	ensure ( spw != 0);

	superpage.SetDeviceData ( spw );

	// Save superpage widget
	Widget wid = spw->GetWidget();
	PlotModApp::Instance().SetMagPlusWidget(wid);
#endif
}

void
XDevice::Visit ( Page& )
{
cout << "XDevice::Visit(Page) -> EMPTY CODE" << endl;
#if 0
	// Find out the widget associated to the superpage
	const Presentable* parent = page.Parent();
	PresentableWidget* parentWidget = (PresentableWidget*) parent->GetDeviceData(); 
	ensure ( parentWidget != 0 );

	// Create a new page widget (a form) and
	// save this information as the page's device data
	PageWidget * pw = new PageWidget ( page, parentWidget );
	ensure ( pw != 0 );

	page.SetDeviceData ( pw );
#endif
}

void
XDevice::Visit ( SubPage& subpage )
{
#if 0
	// Retrieval of the parent of the widget associated to the subpage
	// (this is a safe cast since "DeviceData" is a completely opaque class)
	const Presentable* parent = subpage.Parent();
	PresentableWidget* parentWidget = (PresentableWidget*) parent->GetDeviceData(); 
	ensure ( parent != 0 );

	// Creation of a new subpage widget 
	SubPageWidget* spw = new SubPageWidget ( subpage, parentWidget ) ;
	ensure ( spw != 0 );

	// Save superpage widget
//	Widget wid = spw->GetWidget();
//	PlotModApp::Instance().SetMagPlusWidget(wid);

//	if ( subpage.IsVisible() )
//		spw->Manage();
#endif

#if 1 //FAMI
	// Creation a new canvas - there is one canvas per subpage.
	if ( subpage.CheckCanvas() )
		return;  // Canvas already created

	GraphicsEngine& ge = subpage.GetGraphicsEngine();
//U	XCanvas* canvas = ge.MakeXCanvas ( *this, spw, 
	Canvas* canvas = ge.MakeCanvas ( *this, subpage.GetLocation(), subpage.GetMySize()  , subpage.Id()          );
	ensure ( canvas != 0 );

//U	subpage.SetDeviceData ( spw );
	subpage.SetCanvas ( canvas );
//U	spw->SetCanvas ( canvas );
//FAMI	SetTranslations( (*canvas), spw );

//FAMI	subpage.SetVisibility ( true );
//U	spw->Manage();
#endif

#if 0 //FAMI
	// Set up event handler to redraw the window when it's
	// mapped. Passes the canvas as client data.
	// This means that the expose proc will be called twice,
	// once before the window is mapped, and once when it
	// gets the expose event. It's quick anyway and the
	// event handler is needed to make smooth scrolling/
	// animation, because the glwdrawingarea doesn't preserve
	// it's contents when it's unmapped/mapped.
	XtAddEventHandler ( *spw, StructureNotifyMask  ,
			    False, SubPageMappedHandler,
			    (XtPointer) canvas           );

	// Another event handler for when things are dragged over 
	// the glwdrawingarea, because it doesn't refresh properly.
	XtAddEventHandler ( *canvas, VisibilityChangeMask,
			    False, 
			    (XtEventHandler) RedrawCanvasHandler, 
			    (XtPointer) canvas                   );
#endif
}

#if 0
void
XDevice::Visit ( MagnifyPage& magPage )
{
	cout << "ERROR: SHOULD NOT COME HERE!!!!!!!!!" << endl;
#if 0 //D
	// Create a page widget which holds the canvas
	MagnifyWidget* mw = new MagnifyWidget ( magPage );

	magPage.SetDeviceData ( mw );

	// Creation a new canvas
	GraphicsEngine& ge = magPage.GetGraphicsEngine();
	XCanvas* xcanvas = ge.MakeXCanvas ( *this, mw,
					    magPage.GetLocation(),	 
					    magPage.GetMySize()  ,
					    magPage.Id() );
	xcanvas->IsMagnifyWindow ( true );

	magPage.SetCanvas ( xcanvas );

	mw->SetCanvas ( xcanvas );
#endif
}

void
XDevice::Visit ( EditMapPage& editPage )
{
	// Create Main Widget
	EditMapWidget* emw = new EditMapWidget ( editPage );

	editPage.SetDeviceData ( emw );

	// Creation a new canvas
	GraphicsEngine& ge = editPage.GetGraphicsEngine();
	XCanvas* xcanvas = ge.MakeXCanvas ( *this, emw,
					    editPage.GetLocation(),	 
					    editPage.GetMySize()  ,
					    editPage.Id() );

	editPage.SetCanvas ( xcanvas );

	SetTranslations( (*xcanvas), emw );
}

void XDevice::SetTranslations (Widget w, const PresentableWidget* subPageWidget )
{
	parsed_xlations = XtParseTranslationTable ( scrollTranslations ); 
	XtAppAddActions ( XtWidgetToApplicationContext (w), scrollActions,
			  XtNumber (scrollActions) );
	XtOverrideTranslations ( w, parsed_xlations );

	XtVaSetValues ( w, XmNuserData, (XtPointer) subPageWidget, NULL );
}
#endif
