/* 
   This file is part of Practical Distributed Processing
   Copyright (C) 2006-2007 Phillip J. Brooke and Richard F. Paige
*/

#include "ngcommon.h"
#include <arpa/inet.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

char *secret_keyword = NULL;
char *admin_host     = NULL;
char *local_ip       = NULL;
char *name           = NULL;
int   bot            = 0;

pnode *phead = NULL;
snode *shead = NULL;
pthread_mutex_t pmutex; 

void read_command_line(int argc, char *argv[]) {
  int i = 1;

  while (i < argc) {
    if (strlen(argv[i]) == 2 && !strncmp(argv[i], "-s", 2)) {
      /* Secret keyword. */
      i++;
      if (i >= argc) {
	/* We seem to have run out of arguments... */
	printf("Secret keyword value needed!\n");
	exit(EXIT_FAILURE);
      } else {
	secret_keyword = argv[i];
	i++;
      }
    } else if (strlen(argv[i]) == 2 && !strncmp(argv[i], "-a", 2)) {
      /* Admin host. */
      i++;
      if (i >= argc) {
	/* We seem to have run out of arguments... */
	printf("Admin hostname needed!\n");
	exit(EXIT_FAILURE);
      } else {
	admin_host = argv[i];
	i++;
      }
    }  else if (strlen(argv[i]) == 2 && !strncmp(argv[i], "-n", 2)) {
      /* Player name. */
      i++;
      if (i >= argc) {
	/* We seem to have run out of arguments... */
	printf("Name needed!\n");
	exit(EXIT_FAILURE);
      } else {
	name = argv[i];
	i++;
      }
    }  else if (strlen(argv[i]) == 2 && !strncmp(argv[i], "-b", 2)) {
      /* Bot. */
      i++;
      bot = 1;
    } else {
      /* Unmatched keyword. */
      printf("Unknown argument: `%s'.\n", argv[i]);
      exit(EXIT_FAILURE);
    }
  } /* End of while loop. */
}

pnode *match_player(char *name) {
  pnode *pcurr;

  pthread_mutex_lock(&pmutex);
  pcurr = phead;
  while (pcurr) {
    if (!strncmp(name, pcurr->name, strlen(pcurr->name))) {
      /* Got a match.  Return the pointer. */
      pthread_mutex_unlock(&pmutex);
      return pcurr;
    }
    pcurr = pcurr->next;
  }
  pthread_mutex_unlock(&pmutex);
  /* Fallen off the end?  No match?  Use NULL to mean `no match'. */
  return NULL;
}

void delete_player(pnode *pdel) {
  pnode *pcurr;

  pthread_mutex_lock(&pmutex);
  if (pdel) {
    if (phead == pdel) {
      phead = phead->next;
    } else {
      pcurr = phead;
      while (pcurr) {
	if (pcurr->next == pdel) {
	  pcurr->next = pcurr->next->next;
	}
	pcurr = pcurr->next;
      }
    }
    free(pdel->name);
    free(pdel);
  }
  pthread_mutex_unlock(&pmutex);
}

snode *match_shot(char *name) {
  snode *scurr;

  pthread_mutex_lock(&pmutex);
  scurr = shead;
  while (scurr) {
    if (!strncmp(name, scurr->name, strlen(scurr->name))) {
      /* Got a match.  Return the pointer. */
      pthread_mutex_unlock(&pmutex);
      return scurr;
    }
    scurr = scurr->next;
  }
  pthread_mutex_unlock(&pmutex);
  /* Fallen off the end?  No match?  Use NULL to mean `no match'. */
  return NULL;
}

void delete_shot(snode *sdel) {
  snode *scurr;

  pthread_mutex_lock(&pmutex);
  if (sdel) {
    if (shead == sdel) {
      shead = shead->next;
    } else {
      scurr = shead;
      while (scurr) {
	if (scurr->next == sdel) {
	  scurr->next = scurr->next->next;
	}
	scurr = scurr->next;
      }
    }
    free(sdel->name);
    free(sdel);
  }
  pthread_mutex_unlock(&pmutex);
}

pnode *add_player(char *username, 
	          int xt, int yt, float xi, float yi,
		  int txt, int tyt, float txi, float tyi,
		  char *p_host, int p_port) {
  pnode *            pcurr;
  struct hostent *   h;

  /* Add the player to our list... */
  pcurr = malloc(sizeof(pnode));
  if (p_host) {
    if ((h = gethostbyname(p_host)) == NULL)
      { 
	perror("Could not get host details.");
	exit(EXIT_FAILURE);
      }   
    pcurr->udp.sin_family = AF_INET;
    memcpy(&(pcurr->udp.sin_addr.s_addr), h->h_addr, h->h_length);
  }
  pcurr->udp.sin_port = htons(p_port);
  pcurr->name = strdup(username);
  pcurr->xt = xt;
  pcurr->yt = yt;
  pcurr->xi = xi;
  pcurr->yi = yi;
  pcurr->txt = txt;
  pcurr->tyt = tyt;
  pcurr->txi = txi;
  pcurr->tyi = tyi;
  pthread_mutex_lock(&pmutex);
  pcurr->next = phead;
  phead = pcurr;
  pthread_mutex_unlock(&pmutex);

  return pcurr;
}

snode *add_shot(char *username, 
                int xt, int yt, float xi, float yi,
                int txt, int tyt, float txi, float tyi) {
  snode *scurr;

  scurr = malloc(sizeof(snode));
  scurr->name = strdup(username);
  scurr->xt = xt;
  scurr->yt = yt;
  scurr->xi = xi;
  scurr->yi = yi;
  scurr->txt = txt;
  scurr->tyt = tyt;
  scurr->txi = txi;
  scurr->tyi = tyi;
  pthread_mutex_lock(&pmutex);
  scurr->next = shead;
  shead = scurr;
  pthread_mutex_unlock(&pmutex);

  return scurr;
}

/* Three conversion functions. */
float to_real(int t, float i) {
  return (float) (TILE_LENGTH * t) + i;
}
int to_tile(float r) {
  return (int) floor(r / (float) TILE_LENGTH);
}
float to_inner(float r) {
  return fmodf(r, (float) TILE_LENGTH);
}

float get_dir(float r0, float r1) {
  /* On a 1-d finite axis from 0..(N_TILES * TILE_LENGTH), an object
     is at location r0 and its destination r1.  Which way and how far
     should we travel? */
  float pos, neg;

  pos = fmodf((float) (N_TILES * TILE_LENGTH) + r1 - r0,
	      (float) (N_TILES * TILE_LENGTH));
  neg = fmodf((float) (N_TILES * TILE_LENGTH) + r0 - r1,
	      (float) (N_TILES * TILE_LENGTH));
  if (neg < pos) {
    return -neg;
  } else {
    return pos;
  }
}

float distance(float x, float y) {
  return sqrt((x*x) + (y*y));
}

float distance8(int axt, int ayt, float axi, float ayi,
		int bxt, int byt, float bxi, float byi) {
  float axr, ayr, bxr, byr, dx, dy;

  axr = to_real(axt, axi);
  ayr = to_real(ayt, ayi);
  bxr = to_real(bxt, bxi);
  byr = to_real(byt, byi);
  dx = get_dir(axr, bxr);
  dy = get_dir(ayr, byr);
  return sqrt((dx * dx) + (dy * dy));
}

int local_tile(int axt, int ayt, int bxt, int byt) {
  /* Returns true if a and b are local to each other (i.e., the same
     tile, or nearby. */
  int     xm, ym;

  xm = (N_TILES + axt - bxt) % N_TILES;
  ym = (N_TILES + ayt - byt) % N_TILES;
  return ( (xm==0 || xm==1 || xm==N_TILES-1)
	   && (ym==0 || ym==1 || ym==N_TILES-1) );
}

int check_tile(int *xt, int *yt) {
  int rv = 1;
  if (*xt < 0)           { *xt = 0;         rv = 0; }
  if (*xt >= N_TILES)    { *xt = N_TILES-1; rv = 0; }
  if (*yt < 0)           { *yt = 0;         rv = 0; }
  if (*yt >= N_TILES)    { *yt = N_TILES-1; rv = 0; }
  return rv;
}

int check_inner(float *xi, float *yi) {
  int rv = 1;
  if (*xi < 0.0)                  { *xi = 0.0; rv = 0; }
  if (*xi >= (float) TILE_LENGTH) { *xi = 0.0; rv = 0; }
  if (*yi < 0.0)                  { *yi = 0.0; rv = 0; }
  if (*yi >= (float) TILE_LENGTH) { *yi = 0.0; rv = 0; }
  return rv;
}
