#include "common/fusebox.h"
#include "common/packet.h"
#include "common/utils.h"
#include "common/lattice.h"
#include "common/act_type.h"
#include "common/actors/simple_room.h"
#include "common/actors/player.h"
#include "common/actors/system.h"
#include "common/version.h"

Fusebox fb(5);
Lattice l(40,40);

void send_lattice(int connection)
{
    Atom *a;
    Electron *e;
    
    Packet_info_vect::write_to(fb[connection], ACTOR_SYSTEM,
			       0, 0, l.get_w(), l.get_h());

    for (int i = 0; i < l.get_w(); i++)
    {
	for (int j = 0; j < l.get_h(); j++)
	{
	    for (int list = 0; list < LIST_LAST; list++)
	    {
		a = l.get(i,j);

		if (a)
		{
		    a->get(list).reset();
		    
		    while ((e = (Electron *)a->get(list).get_next()))
		    {
			warning("sending atom (%d, %d)", i, j);
			Packet_command::write_to(fb[connection],
						 e->actor_type,
						 e->actor_id,
						 COMMAND_FROM_TO,
						 -1, -1, i, j);
		    }
		}
	    }
	}
    }
}

Packet const *get_packet(int con)
{
    Packet const *p;
    
    // this loop stops the game until another packet arrives.
    // NOT TO BE USED FOR MULTIPLE PLAYERS!
    while (1)
    {
	p = read_packet_static(fb[con]);
	if (p)
	{
	    if (p->err)
	    {
		return 0;
	    }
	    else
		return p;
	}

	usleep(10000);
    }

    return p;
}

int net_shake(int new_connection)
{
    Packet const *p;

    warning("there are %d plugs", fb.get_nr_plugs());
    
    if (new_connection == fb.get_nr_plugs() - 1)
    {
	// the last connection was used up: pull the plug
	warning("we're full");
	
	Packet_info_num::write_to(fb[new_connection],
				  ACTOR_SYSTEM,
				  SYS_MESSAGE,
				  System::STC_I_AM_FULL_GET_LOST,
				  0);
	return 0;
    }
    else
    {
	warning("sending version number");
	
	Packet_info_num::write_to(fb[new_connection],
				  ACTOR_SYSTEM,
				  SYS_VAR,
				  System::VERSION_INT,
				  COMM_VERSION_INT);
    }

    // this loop stops the game until another packet arrives.
    // NOT TO BE USED FOR MULTIPLE PLAYERS!
    p = get_packet(new_connection);

    if (!Packet_info_num::expect(p, ACTOR_SYSTEM, SYS_VAR,
				 System::VERSION_INT, COMM_VERSION_INT))
    {
	warning("Error reading the network version number.");
	return 0;
    }

    warning("sending the number of players");
    // send the number of players
    Packet_info_num::write_to(fb[new_connection],
			      ACTOR_SYSTEM,
			      0,
			      System::NUM_PLAYERS,
			      42);    

    // we're getting married, or are we?
    p = get_packet(new_connection);

    // see if the client likes the game
    // if we don't get SYS_YES, we assume they didn't like it.
    if (!Packet_info_num::expect(p, ACTOR_SYSTEM, SYS_MESSAGE,
				System::CTS_INTERESTED_YN, SYS_YES))
    {
	warning("Client on socket %d didn't like the game. Bye!",
		new_connection);
	return 0;
    }
    
    return 1;
}

int game_loop()
{
    int	new_connection;

    Packet const *p;

    new_connection = fb.check_new_connection();

    if (new_connection >= 0)
    {
	if (net_shake(new_connection))
	{
	    warning("handshake succesfull!");
	}
	else
	{
	    warning("handshake NOT succesfull!");
	    fb.close_connection(new_connection);
	}
    }
    
    for (int i = 0; i < fb.get_nr_plugs(); i++)
    {
	if (fb[i])
	{
	    p = read_packet_static(fb[i]);
	    
	    if (p)
	    {
		if (p->err)
		{
		    warning("pulling plug %d from the socket", i);
		    fb.close_connection(i);
		}
		else
		{
		    print_packet(p);
		    if (p->get_type() == TYPE_INFO_STRING)
		    {
			if (!strcmp(((Packet_info_string *)p)->get_string(), "quit"))
			{
			    // pull the plug
			    warning("quit requested: pulling plug %d", i);
			    fb.close_connection(i);
			}
			if (!strcmp(((Packet_info_string *)p)->get_string(), "shutdown"))
			{
			    // shutdown the server
			    warning("shutdown requested by plug %d", i);
			    return 0;
			}
		    }
		}
	    }
	}
	
	usleep(10000);
    }

    return 1;
}

void add_room(int x, int y, int actor_id)
{
    Atom *a = new Atom;
    Simple_room *sr = new Simple_room(actor_id);
    
    a->add(sr);
    l.set(a, x, y);
}

int init_lattice()
{
    // initialize the lattice
    for (int i = 0; i < 38; i++)
    {
	add_room(i + 1, 1, i);
    }
	
    for (int i = 0; i < 38; i++)
    {
	add_room(i + 1, 38, 38 + i);
    }

    for (int i = 0; i < 36; i++)
    {
	add_room(1, i + 2, 76 + i);
    }
    
    for (int i = 0; i < 36; i++)
    {	
	add_room(38, i + 2, 112 + i);
    }

    add_room(10,20,200);
    add_room(10,19,201);
    add_room(10,18,202);
    add_room(10,17,203);
    add_room(10,16,204);
    add_room(11,17,205);
    add_room(12,18,206);
    add_room(13,17,207);
    add_room(14,16,208);
    add_room(14,17,209);
    add_room(14,18,210);
    add_room(14,19,211);
    add_room(14,20,212);

    return 1;
}    


int main(int argc, char **argv)
{
    progname = argv[0];

    if (!init_lattice())
	return 0;

    while (game_loop());
    
    return 0;
}
