/********************************************************************** <BR>
  This file is part of Crack dot Com's free source code release of
  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
  information about compiling & licensing issues visit this URL</a> 
  <PRE> If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "net/startup.hh"
#include "net/client.hh"
#include "net/server.hh"
#include "net/command.hh"

#include "loaders/load.hh"

#include "mess_id.hh"

#include "time/timedev.hh"

#include "app/app.hh"

#include "resources.hh"

#include "device/device.hh"
#include "device/event.hh"

#include "file/ram_file.hh"

#include "gui/text_input.hh"
#include "gui/text.hh"
#include "gui/button.hh"

#include "network/login.hh"
#include "network/net_find.hh"
#include "network/net_prot.hh"
#include "network/net_addr.hh"
#include "network/net_sock.hh"


i4_event_reaction_class *g1_net_window_class::create_orec(int mess_id)
{
  i4_object_message_event_class *om=new i4_object_message_event_class(this, mess_id);
  return new i4_event_reaction_class(this, om);
}

g1_net_window_class::g1_net_window_class(w16 w, w16 h, 
                                         i4_graphical_style_class *style,
                                         i4_net_protocol *protocol,
                                         char *bg_res,
                                         int poll_delay,
                                         int poll_id)
  : i4_parent_window_class(w,h),
    style(style),
    protocol(protocol),
    poll_delay(poll_delay),
    poll_id(poll_id)
{
  bg=i4_load_image(i4gets(bg_res));

  i4_object_message_event_class poll(this, poll_id);
  poll_event_id=i4_time_dev.request_event(this, &poll, poll_delay);
}
    
void g1_net_window_class::receive_event(i4_event *ev)
{
  if (ev->type()==i4_event::OBJECT_MESSAGE)
  {
    CAST_PTR(oev, i4_object_message_event_class, ev);
    if (oev->object==this)
    {
      if (oev->sub_type==(w32)poll_id)
      {
        poll();

        i4_object_message_event_class poll(this, poll_id);
        poll_event_id=i4_time_dev.request_event(this, &poll, poll_delay);
      }
      else object_message(oev->sub_type);
    }
    else i4_parent_window_class::receive_event(ev);
  }
  else i4_parent_window_class::receive_event(ev);
}


void g1_net_window_class::parent_draw(i4_draw_context_class &context)
{
  if (bg)
  {
    local_image->clear(0, context);
    bg->put_image(local_image, 0, 0, context);
  }
}


g1_net_window_class::~g1_net_window_class()
{
  i4_time_dev.cancel_event(poll_event_id);

  if (bg)
    delete bg;
}

g1_startup_window::g1_startup_window(w16 w, w16 h, 
                                     i4_graphical_style_class *style,
                                     i4_net_protocol *protocol)
  : g1_net_window_class(w,h, style, protocol, "net_bg_image", 500, POLL)
{
  hostname=new i4_text_input_class(style, i4_string_man.get(0), 190, 200, this);
  add_child(g1_resources.net_hostname_x, g1_resources.net_hostname_y, hostname);

  username=new i4_text_input_class(style, *g1_resources.username, 90, 10, this);
  add_child(g1_resources.net_username_x, g1_resources.net_username_y, username);

  i4_text_window_class *start_game_text=new i4_text_window_class(i4gets("start_server"), style);
  i4_button_class *new_game;
  new_game=new i4_button_class(0,start_game_text, style, create_orec(START_SERVER));
  add_child(g1_resources.net_start_x, g1_resources.net_start_y, new_game);

  i4_text_window_class *quit_text=new i4_text_window_class(i4gets("net_main_menu"), style);
  add_child(new_game->x(), new_game->y()+new_game->height()+1, 
            new i4_button_class(0, quit_text, style, create_orec(QUIT_NET_GAME)));

  i4_net_protocol *p=protocol;
  if (p)
    find=p->create_finder_socket(g1_resources.net_find_port, g1_resources.net_find_port);
  else find=0;

  t_buts=0;
  buts=0;
}

void g1_startup_window::free_buts()
{
  for (int i=0; i<t_buts; i++)
  {
    remove_child(buts[i]);
    delete buts[i];
  }
    
  if (buts)
    i4_free(buts);
  buts=0;
  t_buts=0;
}

void g1_startup_window::grab_uname()
{
  delete g1_resources.username;
  i4_query_text_input_class q;
  i4_kernel.send_event(username, &q);
  g1_resources.username=q.copy_of_data;
  q.copy_of_data=0;
}

void g1_startup_window::object_message(int id)
{
  switch (id)
  {
    case START_SERVER :
    {
      grab_uname();

      g1_server=new g1_server_class(g1_resources.net_udp_port, protocol);

      i4_user_message_event_class u(G1_SERVER_MENU);
      i4_kernel.send_event(i4_current_app, &u);
    } break;

    case QUIT_NET_GAME :
    {
      grab_uname();

      i4_user_message_event_class u(G1_MAIN_MENU);
      i4_kernel.send_event(i4_current_app, &u);
    } break;
  }

  if (id>=LAST)
  {
    grab_uname();

    i4_finder_socket::server s;
    find->get_server(id-LAST, s);

    s.addr->set_port(g1_resources.net_udp_port);
    g1_client=new g1_client_class(s.addr, g1_resources.net_udp_port, protocol);

    i4_user_message_event_class u(G1_CLIENT_JOINED_MENU);
    i4_kernel.send_event(i4_current_app, &u);    
  }
}

void g1_startup_window::poll()
{
  if (find)
    if (find->poll())
    {
      free_buts();

      t_buts=find->total_servers();
      buts=(i4_button_class **)I4_MALLOC(sizeof(i4_button_class *)*t_buts,"but array");
  
      int y=g1_resources.net_found_y, x=g1_resources.net_found_x1;

      for (int i=0; i<t_buts; i++)
      {
        i4_finder_socket::server s;
        find->get_server(i, s);

        i4_text_window_class *t=new i4_text_window_class(*s.notification_string, style);
        buts[i]=new i4_button_class(0, t, style, create_orec(LAST + i));
  
        add_child(x,y, buts[i]);
        y+=buts[i]->height()+1;
      }
    }
}

g1_startup_window::~g1_startup_window()
{
  if (find)
    delete find;

  free_buts();
}

g1_server_start_window::g1_server_start_window(w16 w, w16 h, 
                                               i4_graphical_style_class *style,
                                               i4_net_protocol *protocol)
  : g1_net_window_class(w,h, style, protocol, "server_bg_image", 100, POLL)
{
  memset(names,0,sizeof(names));

  i4_text_window_class *start_game_text=new i4_text_window_class(i4gets("start_net_game"), 
                                                                 style);

  i4_object_message_event_class *sng_e=new i4_object_message_event_class(this, START_NET_GAME);
  i4_event_reaction_class *sng_r=new i4_event_reaction_class(this, sng_e);
  i4_button_class *new_game=new i4_button_class(0, start_game_text, style, sng_r);
  add_child(480,20, new_game);

  i4_text_window_class *quit_text=new i4_text_window_class(i4gets("net_main_menu"), style);
  i4_object_message_event_class *q_e=new i4_object_message_event_class(this, QUIT_NET_GAME);
  i4_event_reaction_class *q_r=new i4_event_reaction_class(this, q_e);
  i4_button_class *q=new i4_button_class(0, quit_text, style, q_r);
  add_child(new_game->x(), new_game->y()+new_game->height()+1, q);

  if (protocol)
    note=protocol->create_notifier_socket(g1_resources.net_find_port, *g1_resources.username);
  else note=0;
}


void g1_server_start_window::object_message(int id)
{
  switch (id)
  {
    case START_NET_GAME :
    {
      i4_user_message_event_class u(G1_START_NEW_GAME);
      i4_kernel.send_event(i4_current_app, &u);
    } break;

    case QUIT_NET_GAME :
    {
      if (g1_server)
      {
        delete g1_server;
        g1_server=0;
      }

      i4_user_message_event_class u(G1_MAIN_MENU);
      i4_kernel.send_event(i4_current_app, &u);
    } break;
  }
}

void g1_server_start_window::poll()
{
  if (note)
    note->poll();

  if (g1_server)
  {
    g1_server->poll();
    if (g1_server->list_changed)
    {
      g1_server->list_changed=i4_F;

      int i;
      for (i=0; i<G1_MAX_PLAYERS; i++)
        if (names[i])
        {
          remove_child(names[i]);
          names[i]=0;
        }

      i4_text_window_class *t;
      int x=i4getn("net_joined_x"), y=i4getn("net_joined_y");

      t=new i4_text_window_class(*g1_resources.username, style);
      add_child(x, y, t);
      y+=t->height()+1;
      names[0]=t;

      for (i=1; i<G1_MAX_PLAYERS; i++)
      {
        if (g1_server->clients[i].addr)
        {
          t=new i4_text_window_class(*g1_server->clients[i].username, style);
          add_child(x,y,t);
          y+=t->height()+1;
          names[i]=t;
        }
      }
    }
  }
}

g1_server_start_window::~g1_server_start_window()
{ 
  if (note)
    delete note;
}



g1_client_wait_window::g1_client_wait_window(w16 w, w16 h, 
                                             i4_graphical_style_class *style,
                                             i4_net_protocol *protocol)
  : g1_net_window_class(w,h, style, protocol, "client_wait_image", 100, POLL)
{
  i4_text_window_class *quit_text=new i4_text_window_class(i4gets("net_cancel"), style);
  i4_object_message_event_class *q_e=new i4_object_message_event_class(this, QUIT_NET_GAME);
  i4_event_reaction_class *q_r=new i4_event_reaction_class(this, q_e);
  i4_button_class *q=new i4_button_class(0, quit_text, style, q_r);
  add_child(i4getn("net_cancel_x"), i4getn("net_cancel_y"), q);
  
}

void g1_client_wait_window::object_message(int id)
{
  if (id==QUIT_NET_GAME)
  {
    i4_user_message_event_class u(G1_MAIN_MENU);
    i4_kernel.send_event(i4_current_app, &u);
  }
}

void g1_client_wait_window::poll()
{
  if (!g1_client || !g1_client->poll())
  {    
    i4_user_message_event_class u(G1_MAIN_MENU);
    i4_kernel.send_event(i4_current_app, &u);
  }
}

g1_client_wait_window::~g1_client_wait_window()
{
  if (g1_client)
  {
    delete g1_client;
    g1_client=0;
  }
}
// CLIENT.CPP
/********************************************************************** <BR>
  This file is part of Crack dot Com's free source code release of
  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
  information about compiling & licensing issues visit this URL</a> 
  <PRE> If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/


g1_client_class *g1_client=0;

g1_client_class::g1_client_class(i4_net_address *server_address, 
                                 int use_port,
                                 i4_net_protocol *protocol)
  : server_address(server_address->copy()),
    use_port(use_port)
{
  listen=0;
  send=0;
  map_name=0;

  if (protocol)
  {
    listen=protocol->listen(use_port, I4_PACKETED_DATA);
    send=protocol->connect(server_address, I4_PACKETED_DATA);
  }
}

i4_bool g1_client_class::poll()  // returns false if server is not responding
{
  if (!listen || !send)
    return 0;

  if (state==JOIN_START)
  {
    i4_time_class now;
    if (now.milli_diff(last_responce_time)>5000)  // if it's been 5 secs assume server dead
      return i4_F;

    
    w8 packet[512];
    i4_ram_file_class r(packet, sizeof(packet));

    r.write_8(G1_PK_I_WANNA_JOIN);
    r.write_16(use_port);
    r.write_counted_str(*g1_resources.username);

    if (!send->write(packet, r.tell()))
      return i4_F;

  }

  if (listen->ready_to_read())
  {
    w8 packet[512];
    i4_net_address *a;
    int s=listen->read_from(packet, sizeof(packet), a);
    if (a->equals(server_address))
    {
      i4_ram_file_class r(packet, sizeof(packet));

      if (r.read_8()==G1_PK_YOU_HAVE_JOINED)
      {
        player_num=(w8)r.read_16();
       
        if (map_name)
          delete map_name;
        map_name=r.read_counted_str();
        state=JOIN_START;
        last_responce_time.get();
      }
    }
    delete a;
  }
  return i4_T;
}


g1_client_class::~g1_client_class()
{
  delete server_address;

  if (map_name)
    delete map_name;

  if (listen)
    delete listen;

  if (send)
    delete send;
}
// SERVER.CPP
/********************************************************************** <BR>
  This file is part of Crack dot Com's free source code release of
  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
  information about compiling & licensing issues visit this URL</a> 
  <PRE> If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/


g1_server_class *g1_server=0;

void g1_server_class::client::cleanup()
{
  if (addr)
  {
    delete addr;
    delete username;
  }
  if (send)
    delete send;
  addr=0;
  send=0;
}

g1_server_class::g1_server_class(int use_port, i4_net_protocol *protocol)
  : protocol(protocol)
{  
  map_name=new i4_str(i4gets("tmp_savename"));
  udp_port=0;
  memset(clients, 0, sizeof(clients));
  list_changed=i4_F;

  if (protocol)
  {
    udp_port=protocol->listen(use_port, I4_PACKETED_DATA);
    if (!udp_port)
      i4_warning("could not bind to port, server already running?");
    


  }
}

void start_game() { ; }


void g1_server_class::send_player_joined(int client_num)
{
  w8 packet[512];
  i4_ram_file_class r(packet, sizeof(packet));

  r.write_8(G1_PK_YOU_HAVE_JOINED);
  r.write_16(client_num+1);
  r.write_counted_str(*map_name);

  clients[client_num].send->write(packet, r.tell());
}

void g1_server_class::process_client_packet(w8 *packet,
                                            int packet_length, 
                                            int client_num)
{
  clients[client_num].last_data.get();

  i4_ram_file_class r(packet, packet_length);
  if (r.read_8()==G1_PK_I_WANNA_JOIN)
  {
    r.read_16();  // skip use port

    delete clients[client_num].username;
    clients[client_num].username=r.read_counted_str();
    send_player_joined(client_num);
  }    
}


void g1_server_class::poll()
{
  int i;

  while (udp_port && udp_port->ready_to_read())
  {
    w8 packet[512];
    i4_net_address *a;
    int len=udp_port->read_from(packet, sizeof(packet), a);
    
    // see if this was from one of our clients

    int found=0, free_spot=-1;

    for (i=0; i<G1_MAX_PLAYERS; i++)
    {
      if (clients[i].addr)
      {
        if (clients[i].addr->equals(a))
        {
          process_client_packet(packet, len, i);
          found=1;
        }
      }
      else free_spot=i;
    }

    if (!found && free_spot!=-1)
    {
      i4_ram_file_class r(packet, len);
      if (r.read_8()==G1_PK_I_WANNA_JOIN)
      {
        clients[free_spot].addr=a->copy();
        clients[free_spot].addr->set_port(r.read_16());
        clients[free_spot].username=r.read_counted_str();
        
        clients[free_spot].send=protocol->connect(clients[free_spot].addr, I4_PACKETED_DATA);

        if (clients[free_spot].send)
          send_player_joined(free_spot);
        else
          clients[free_spot].cleanup();

        list_changed=i4_T;
      }    
    }

    delete a;
  }

  if (state==WAITING_FOR_PLAYERS)
  {
    i4_time_class now;
    for (i=0; i<G1_MAX_PLAYERS; i++)
    {     
      if (clients[i].addr && now.milli_diff(clients[i].last_data)>1000)
      {
        clients[i].cleanup();
        list_changed=i4_T;
      }

    }
  }

}


g1_server_class::~g1_server_class()
{
  delete map_name;

  for (int i=0; i<G1_MAX_PLAYERS; i++)
    if (clients[i].addr)
      delete clients[i].addr;
  delete udp_port;
}
