/******************************************************************************
 *
 * jabaiface.cpp
 *
 * (c) Leonid (Lodrion) Taycher 2000
 *
 ******************************************************************************/

#include <list>
#include <algorithm>
#include <stdio.h>

#include <qt/qapplication.h>
#include <qt/qwidget.h>
#include <qt/qpainter.h>
#include <qt/qwmatrix.h>

#include <jabaiface.h>
#include <aiiutils.h>

JabaIFace::JabaIFace()
	:
	_maze()
{
}

JabaIFace::~JabaIFace()
{
	for (list<Node>::iterator j = _jabas.begin();
				j != _jabas.end();j++)
	{
		delete *j;
	}
}
	
void JabaIFace::maze(const lcLabyrinth& mz) 
{ 
	_maze = mz; 
	_dmaze.reset();
		
	_jabas = list<Node >();
	
	// Add tower jabas

	_jabas.push_back(new lcJabaTower(&_maze, &_dmaze, aic2Vect(0, 0), 1e9, 0));
	_jabas.push_back(new lcJabaTower(&_maze, &_dmaze, aic2Vect(0, 1), 1e9, 1));
	_jabas.push_back(new lcJabaTower(&_maze, &_dmaze, aic2Vect(1, 0), 1e9, 2));

//	_jabas.push_back(new lcJabaMobile(&_maze, aic2Vect(0.5, 0.6), 1e9));
/*
	for (int i = 0;i < 50;i++)
	{
		aic2Vect coord(0.45 + realRnd() / 10, 0.45 + realRnd() / 10);
		if (!_maze.onmaze(coord))
		{
			_jabas.push_back(new lcJabaMobile(&_maze, coord, 1e6));
		}
	}
*/
}

void JabaIFace::messageCollect(vector<lcJabaEtherMessage>& messages)
{
	messages.resize(0);
	
	for (list<Node>::iterator j = _jabas.begin();
				j != _jabas.end();j++)
	{
		Node& n = *j;
		
		if (n->getPendingMessageQueue().size() != 0)
		{
			const lcJabaEtherMessage& msg = *(n->getPendingMessageQueue()).begin();
			messages.push_back(msg);
			// Do not clean the queue here, as we will need to access it 
			// When computing which messages the jaba hears
		}
	}
	
	sort(messages.begin(), messages.end());
}

void processNode(lcJabaNode& node, vector<lcJabaEtherMessage>& msgs, 
					QPainter& p)
{
	list<lcJabaReceivedMessage> filteredMessages;
	
	{ // Compute the list of the messages which this node hears 
		
		int lastCell = -1;
		bool interference = false;
		
		for (int i = 0;i < msgs.size();i++)
		{
			lcJabaEtherMessage& msg = msgs[i];	// Current message
			
			// The jaba does not hear its own message. This actually deletes all 
			// messages in the same cell, but all the better
			if (node.getPendingMessageQueue().size() > 0 && // Jaba has sent a message
						(msg.cell() == 
						node.getPendingMessageQueue().begin()->cell()))		
				continue;
			
			double strength = lfComputeStrength(
								msg.strength(), msg.realPos(),  node.pos());
			
			if (strength >= 1.0)	// Should be strong enough
			{
				if (msg.cell() != lastCell)
				{
					// If there was the interference on the last message, kill it
					if (interference) 
					{
						filteredMessages.pop_back();
						interference = false;
					}
					

					lastCell = msg.cell();
					filteredMessages.push_back(lcJabaReceivedMessage(msg, strength));
				}
				else
				{
					// and ignore the current message, and set the flag that the last 
					// one should also be killed.
					interference = true;
				}
			}
		}
		node.cleanMessageQueue();
	}
	
	// Draw the node to erase it
	node.draw(p);
	
	// Process the messages
	node.processMessages(filteredMessages);
	
	// Redraw the node
	node.draw(p);
}

void JabaIFace::timerEvent(QTimerEvent *)
{
	// Collect all messages sent out during this timeslice
	vector<lcJabaEtherMessage> msgs;
	messageCollect(msgs);
	
	QPainter p (this);

	// Paint the maze size of the window
	p.setWindow(0, _maze.pictureScale(), 
				_maze.pictureScale(), -_maze.pictureScale());
	
	// Draw on the left side of the window
	p.setViewport(0, 0, width() / 2 - 5, height());
	
			
	// For each node process the messages. For the nodes other then the 
	// towers, do a redraw
	
	for (list<Node>::iterator j = _jabas.begin();
				j != _jabas.end();j++)
	{
		Node& n = *j;
		
		processNode(*n, msgs, p);
	}
	
	// Draw the discovered labyrinth
	// Draw on the right side of the window
	p.setViewport(width() / 2 + 5, 0, width() / 2 - 5, height());
	_dmaze.drawNew(p);
	_dmaze.stepTime();

	p.end();
	qApp->processEvents();	// Release the control...
		
};

void JabaIFace::mouseReleaseEvent(QMouseEvent *m)
{
	if (m->x() < width() / 2 - 5)	
	{
		// Compute the center of the "drop"
		aic2Vect ctr(double(m->x()) / double (width() / 2 - 5), 
								 1.0 - double(m->y()) / double (height()));
		
		// Prepare the painter
		QPainter p (this);

		// Paint the maze size of the window
		p.setWindow(0, _maze.pictureScale(), 
					_maze.pictureScale(), -_maze.pictureScale());

		// Draw on the left side of the window
		p.setViewport(0, 0, width() / 2 - 5, height());

		if (m->button() == LeftButton)
		{
			for (int i = 0;i < 10;i++)
			{
				aic2Vect coord(realRnd() / 10 - 0.05, realRnd() / 10 - 0.05);
				if (!_maze.onmaze(coord + ctr))
				{
					lcJabaMobile *jaba = new lcJabaMobile(&_maze, coord + ctr, 1e7);
				
					// Draw the new jaba
					jaba->draw(p);
					// Add it to the list
					_jabas.push_back(jaba);
				}
			}
		}
		else if (m->button() == RightButton)
		{
			for (list<Node>::iterator j = _jabas.begin();
						j != _jabas.end();j++)
			{
				Node& n = *j;
				
				if ((n->pos() - ctr).r() < 0.1)
				{
					// Remove the node
					n->draw(p);
					_jabas.remove(*(j--));
				}
			}
		}
		
		p.end();
	}
}

void JabaIFace::paintEvent(QPaintEvent *)
{
	QPainter p(this);
	// Paint the maze
	p.setWindow(0, _maze.pictureScale(), 
				_maze.pictureScale(), -_maze.pictureScale());
	
	// Draw on the left side of the window
	p.setViewport(0, 0, width() / 2 - 5, height());

	_maze.draw(p);

	
	// Draw the robots
	
	for (list<Node>::iterator j = _jabas.begin();
				j != _jabas.end();j++)
	{
		(*j)->draw(p);
	}	

	// Draw the discovered maze
	
	// Draw on the right side of the window
	p.setViewport(width() / 2 + 5, 0, width() / 2 - 5, height());
	_dmaze.draw(p);

	p.end();
	
}
