/**
 * \file Visual.cpp
 * \date 13.03.2003
 *
 * Graphical user interface using the Fox library
 */

#undef WITH_VISUAL
#ifdef WITH_VISUAL

#include "Player/Visualization/Visual.h"
#include "Common/Main.h"
#include "Common/Tasks.h"

Visual::Visual()
	: Module("Visual", "Visualisation module", "v0.1.000")
{
	if(Main::GetInstance()->RegisterModule(this, VISUALIZATION_TASK))
	{
		//TXTN("Visual: Register OK");
	}
	else
	{
		ERRN("Visual: Register failed\n\tAborting system");
		Main::GetInstance()->ShutDown();
	}
}

int Visual::Init(int argc, char** argv, int)
{
#ifdef GOALIE
	application = new FXApp("StjupitDox Goalie", "StjupitDox RoboCup Team");
#else // GOALIE
	application = new FXApp("StjupitDox Player", "StjupitDox RoboCup Team");
#endif // GOALIE
	application->init(argc, argv);

	frame = new VisualFrame(application);
	application->create();

	return INIT_OK;
}

void Visual::Run(int)
{
	frame->forceRefresh();
	while(application->peekEvent()) application->runOneEvent();
}


FXDEFMAP(VisualFrame) VisualFrameMap[] =
{
	// Message_Type, ID, Message_Handler
	FXMAPFUNC(SEL_COMMAND, VisualFrame::ID_QUIT, VisualFrame::onQuit),
	FXMAPFUNC(SEL_PAINT, VisualFrame::ID_FIELD, VisualFrame::onPaintField), 
	FXMAPFUNC(SEL_UPDATE, VisualFrame::ID_FIELD, VisualFrame::onUpdateField),
};

FXIMPLEMENT(VisualFrame, FXMainWindow, VisualFrameMap, ARRAYNUMBER(VisualFrameMap))


VisualFrame::VisualFrame(FXApp* application)
	: FXMainWindow(
#ifdef GOALIE
			application, "StjupitDox Goalie", NULL, NULL, 
#else // GOALIE
			application, "StjupitDox Player", NULL, NULL, 
#endif // GOALIE
			DECOR_ALL, 0, 0, 800, 600),
	  fieldBuf(NULL), fieldChanged(true)
{
	FXHorizontalFrame* mainFrame = new FXHorizontalFrame(
		this, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y);

	// Graph on the left
	fieldCtl = new FXCanvas(
		mainFrame, this, ID_FIELD, FRAME_NORMAL | LAYOUT_FILL_X | LAYOUT_FILL_Y);

	// Button pannel on the right
	FXVerticalFrame* buttons = new FXVerticalFrame(
		mainFrame, LAYOUT_FILL_Y | LAYOUT_TOP | LAYOUT_LEFT, 0, 0, 0, 0, 10, 10, 10, 10);

	quit = new FXButton(
		buttons, "&Quit", NULL, this, ID_QUIT, 
		FRAME_THICK | FRAME_RAISED | LAYOUT_FILL_X | LAYOUT_TOP | LAYOUT_LEFT, 
		0, 0, 0, 0, 10, 10, 5, 5);

	// Get kernel pointer
	PlayerKernel* kernel = dynamic_cast<PlayerKernel*>(Main::GetInstance()->GetModule(SYNCHRONIZATION_TASK));
	world = kernel->GetWorld();
}

void VisualFrame::create()
{
	FXMainWindow::create();
	show(PLACEMENT_SCREEN);
}

long VisualFrame::onQuit(FXObject* , FXSelector, void*)
{
	getApp()->stop();
	Main::GetInstance()->ShutDown();

	return 1;
}

long VisualFrame::onUpdateField(FXObject* , FXSelector, void* ptr)
{
	static int prevSimTime = -1;
	if(world->state->GetSimTime() != prevSimTime)
	{
		fieldChanged = true;
		prevSimTime = world->state->GetSimTime();
		
		fieldCtl->update();
	}

	return 1;
}

long VisualFrame::onPaintField(FXObject* , FXSelector, void* ptr)
{
	FXEvent* event = (FXEvent*) ptr;

	// Initialize buffer
	static FXint oldW = 0;
	static FXint oldH = 0;
	
	if(fieldCtl->getWidth() != oldW || fieldCtl->getHeight() != oldH)
	{
		oldW = fieldCtl->getWidth(); oldH = fieldCtl->getHeight();
		
		if(fieldBuf != NULL) delete fieldBuf;
		fieldBuf = new FXImage(
			getApp(), NULL, 0, fieldCtl->getWidth(), fieldCtl->getHeight());
		fieldBuf->create();

		// Recalculate ratio
		float ratio = ((float) fieldCtl->getWidth()) / ((float) fieldCtl->getHeight());
		if(ratio > 60.f / 40.f)
		{
			transWidth = (FXint) (((float) fieldCtl->getHeight()) * 60.f / 40.f);
			transHeight = fieldCtl->getHeight();
		}
		else
		{
			transWidth = fieldCtl->getWidth();
			transHeight = (FXint) (((float) fieldCtl->getWidth()) * 40.f / 60.f);
		}

		fieldChanged = true;
	}

	if(fieldChanged)
	{
		// Get device context for drawing
		FXDCWindow* dc = new FXDCWindow(fieldBuf, event);

		// Draw background
		dc->setForeground(getApp()->getBaseColor());
		dc->fillRectangle(event->rect.x, event->rect.y, event->rect.w, event->rect.h);

		// Draw data
		drawPlayerData(dc);

		delete dc;
	}
	
	// Draw buffer to screen
	FXDCWindow windc(fieldCtl, event);
	windc.drawImage(fieldBuf, 0, 0);
		
	return 1;
}

FXint VisualFrame::transX(float x)
{
	return (FXint) (
		(fieldCtl->getWidth() - transWidth) / 2.f + 
		(x + 60.f) * transWidth / (2.f * 60.f)
						);
}

FXint VisualFrame::transY(float y)
{
	return (FXint) (
		(fieldCtl->getHeight() - transHeight) / 2.f + 
		(y + 40.f) * transHeight / (2.f * 40.f)
						);
}

#define FIELD_COLOR FXRGB(0x00, 0xaa, 0x00)
#define FLAG_COLOR FXRGB(0x00, 0x00, 0x00)
#define ME_COLOR FXRGB(0xff, 0x00, 0x00)
#define FRIEND_COLOR FXRGB(0xff, 0xff, 0x00)
#define ENEMY_COLOR FXRGB(0x00, 0xff, 0xff)
#define BALL_COLOR FXRGB(0x00, 0x00, 0xff)

#define FLAG_RADIUS 2
#define PLAYER_RADIUS 4
#define BALL_RADIUS 2

void VisualFrame::drawPlayerData(FXDC* dc)
{
	int i;
	int count;

	// Playground
	dc->setForeground(FIELD_COLOR);
	dc->setBackground(FIELD_COLOR);

	dc->fillRectangle(
		transX(-60.f), transY(-40.f), 
		transX(60.f) - transX(-60.f), transY(40.f) - transY(-40.f));

	// Flags
	dc->setForeground(FLAG_COLOR);
	dc->setBackground(FLAG_COLOR);

	for(i = 1; i < FLAG_MAX; i++)
	{
		dc->fillArc(
			transX(world->params->flags[i].GetX()) - FLAG_RADIUS, 
			transY(world->params->flags[i].GetY()) - FLAG_RADIUS,
			2 * FLAG_RADIUS, 2 * FLAG_RADIUS, 0, 64 * 360);
	}

	// Ball
	dc->setForeground(BALL_COLOR);
	dc->setBackground(BALL_COLOR);

	if(world->ball->IsValid()) dc->fillArc(
		transX(world->ball->pos.GetX()) - BALL_RADIUS, 
		transY(world->ball->pos.GetY()) - BALL_RADIUS,
		2 * BALL_RADIUS, 2 * BALL_RADIUS, 0, 64 * 360);

	// Me
	dc->setForeground(ME_COLOR);
	dc->setBackground(ME_COLOR);

	dc->fillArc(
		transX(world->me->pos.GetX()) - PLAYER_RADIUS, 
		transY(world->me->pos.GetY()) - PLAYER_RADIUS,
		2 * PLAYER_RADIUS, 2 * PLAYER_RADIUS, 0, 64 * 360);

	// Friends
	dc->setForeground(FRIEND_COLOR);
	dc->setBackground(FRIEND_COLOR);

	count = world->friends.GetSize();
	for(i = 0; i < count; i++)
	{
		dc->fillArc(
			transX(world->friends[i].pos.GetX()) - PLAYER_RADIUS, 
			transY(world->friends[i].pos.GetY()) - PLAYER_RADIUS,
			2 * PLAYER_RADIUS, 2 * PLAYER_RADIUS, 0, 64 * 360);
	}

	// Enemies
	dc->setForeground(ENEMY_COLOR);
	dc->setBackground(ENEMY_COLOR);

	count = world->enemies.GetSize();
	for(i = 0; i < count; i++)
	{
		dc->fillArc(
			transX(world->enemies[i].pos.GetX()) - PLAYER_RADIUS, 
			transY(world->enemies[i].pos.GetY()) - PLAYER_RADIUS,
			2 * PLAYER_RADIUS, 2 * PLAYER_RADIUS, 0, 64 * 360);
	}
}

VisualFrame::~VisualFrame()
{
	if(fieldBuf != NULL) delete fieldBuf;
}

#endif // WITH_VISUAL