/* 
 * Copyright (C) 2003 Tim Martin
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>

/* so strptime gets picked up */
#define _XOPEN_SOURCE
#include <time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>

#include "map.h"
#include "connections.h"
#include "protocol.h"
#include "senkenconfig.h"
#include "main.h"
#include "utils.h"
#include "game.h"

time_t CurrentGameTime;

struct timeval lasttime;

/* seconds between moving clock */
struct timeval game_speed;
extern game_t *game;

/* days to move at once */
#define GAME_TIMEQUANTUM 1

/*
 * Initialize network stuff so we can accept connections
 */
static int
init_network(int port, int *sock)
{
  struct sockaddr_in sin;
  char hostname[128]; /* xxx max */
  int sockoptval = 1;

  /* get our hostname */
  gethostname(hostname, sizeof(hostname));

  /* create a socket */
  if ((*sock = socket(AF_INET, SOCK_STREAM, 0)) <0)
  {
      perror("socket");
      exit(1);
  }

  /* let socket reuse this address and port */
  if( setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, 
		 (char*)&sockoptval, sizeof(int)) != 0 ) {
      perror( "dblisten(): setsockopt() returned" );
      exit(1);
  }

  /* create sockaddr_in */
  sin.sin_family=AF_INET;
  sin.sin_port= htons(port);
  sin.sin_addr.s_addr = INADDR_ANY;

  /* bind it */
  if (bind(*sock,(const struct sockaddr *) &sin, sizeof(sin)) < 0 )
  {
      perror("bind");
      exit(1);
  }

  if (listen(*sock, 10) < 0) {
      perror("listen");
      exit(1);
  }
  
  /* set non-blocking */
  /*fcntl(*sock, F_SETFL, O_NONBLOCK);*/

  printf(_("listening on port %i...\n"),port);

  return 0;
}

static void
give_time(void)
{
    struct timeval curtime;
    long diff;
    long speed;

    gettimeofday(&curtime, NULL);

    diff = (curtime.tv_sec - lasttime.tv_sec) * 1000000 +
	   (curtime.tv_usec - lasttime.tv_usec);

    speed = game_speed.tv_sec * 1000000 + game_speed.tv_usec;

    if ((game_speed.tv_sec >= 0) && (diff >= speed)) {
	
	CurrentGameTime += (GAME_TIMEQUANTUM * SECS_IN_DAY);
	lasttime = curtime;
	pass_time(CurrentGameTime, 0);
	connections_send_to_all("* CURTIME %d\r\n", CurrentGameTime);
    }

}

static void
set_time(int mon, int day, int year)
{
    struct tm tm;
    
    memset(&tm, 0, sizeof(struct tm));
    tm.tm_mon = mon;
    tm.tm_mday = day;
    tm.tm_year = year - 1900;
    CurrentGameTime = mktime(&tm);

    gettimeofday(&lasttime, NULL);
}

extern void
set_time_str(char *str)
{
    struct tm tm;

    strptime(str,"%m/%d/%Y", &tm);

    set_time(tm.tm_mon, tm.tm_mday, tm.tm_year + 1900);
}


static int
mainloop(int acceptsock)
{
    fd_set readfds;
    
    while (1) {
	struct timeval timeout;
	int r;

	/* add file descriptors to read list */
	FD_ZERO(&readfds);
	FD_SET(acceptsock, &readfds);
	connections_enumerate(&connection_setfd, &readfds);

	/* need to reset the timeout each time around for linux */
	timeout.tv_sec = 0;
	timeout.tv_usec = 10000;

	/* main select loop */
	r = select(1000, &readfds, NULL, NULL, &timeout);
	
	if (r < 0) {
	    perror("select");
	}

	if ((game) && (game->active)) {
	    give_time();
	}

	if (r == 0) {
	    connections_flush_all();
	}

	/* if there is a new connect to accept */
	if (FD_ISSET(acceptsock, &readfds)) {
	    struct sockaddr_in sin;
	    int sinsize = sizeof(sin);
	    int newsock;

	    newsock = accept(acceptsock, (struct sockaddr *)&sin, &sinsize);
	    if (newsock < 0) {
		perror("accept");
	    }

	    connections_add(newsock);
	}

	connections_enumerate(&connection_checkfd, &readfds);
	
    }
}

int
main(int argc, char *argv[])
{
    int acceptsock;
    int r;

    srand(time(NULL));

    signal(SIGPIPE,SIG_IGN);

    r = init_network(5000, &acceptsock);
    if (r) {
	printf(_("Error initializing network\n"));
	exit(1);
    }

    connections_setlogging("logfile");

    set_time(0, 1, 1995);
    
    mainloop(acceptsock);

    return 0;
}
