/* box2stat.c ----- VERSION 1.0, 7/3/1992                  */
/* Compute player stats from a collection of boxscores     */
/* COMPILATION: cc -O box2stat.c -o box2stat               */
/* USAGE: cat boxscore1 ... | box2stat > stats             */
/*    or: box2stat boxscore1 ... > stats                   */
/* NEEDS: roster file to work                              */
/*     M. Campostrini, 1992                                */
/* send comments/fixes/improvements to:                    */
/*      campo@sunthpi3.difi.unipi.it                       */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define LINESIZE 96
#define TEAM_MAX 28
#define PLAYER_MAX 30
#define TRUE 1
#define FALSE 0

#define PERCENT(a,b) ((b)!=0 ? (100*(a)+(b)/2)/(b) : (b))
#define MAX(a,b) (((a)>(b)) ? (a) : (b))

typedef struct st_player {
  char name[24];
  int games, minutes, fga, fgm, threea, threem, fta, ftm, oreb,
      treb, assist, pfaul, steal, turnov, block, techn, eject, pts;
} player;

typedef struct st_team {
  char name[8];
  int player_used, games_analyzed;
  player team_player[PLAYER_MAX];
} team;

team team_array[TEAM_MAX];

void initialize();
void readscore();
team *find_team();
void update_player();
void printout();
int order_player();
int strnlike();
char *myfgets();
char *extract_player_name();
void scan_player();

main( argc, argv )
     int argc;
     char *argv[];
{
  int ifile;
  FILE *scorefile;

/* initialize teams and players from roster file */
  initialize();

/* read and process input files (or standard input) */
  if (argc<2) {
    readscore(stdin);
  }
  else {
    for (ifile=1; ifile<argc; ifile++) {
      fprintf(stderr,"=============== reading %s ================\n",
	      argv[ifile]);
      scorefile = fopen(argv[ifile],"r");
      if (scorefile == NULL) {
	fprintf(stderr,"** WARNING ** unable to read <%s>, skipping it.\n",
		argv[ifile] ); }
      else {
	readscore(scorefile); }
      fclose(scorefile);
    }
  }

/* output stats to stdout */
  printout();
}

/* read roster file, initialize teams and players */
void initialize()
{
  char buffer[LINESIZE];
  FILE *rosterfile;
  int done_team, name_len;
  int team_no, player_no;
  team *my_team;
  player *my_player;

  rosterfile = fopen("/mit/oliver/nbastats/roster", "r");

  if (rosterfile == NULL) {
    fprintf(stderr,"** FATAL ** unable to find roster file\n");
    exit(2);
    }

  done_team = TRUE;
  my_team = team_array;
  my_player = my_team->team_player;

  while (myfgets(buffer, LINESIZE, rosterfile) != NULL ) {
    
    if (done_team) {
/* read in next team and initialize it */
      my_player = my_team->team_player;
      if (strlen(buffer)!=3) {
	fprintf(stderr, "** WARNING ** \"%s\" should be a team!\n",
		buffer); }
      strncpy (my_team->name, buffer, 4);

      my_team->games_analyzed = 0;
      my_team->player_used = 0;
      done_team = FALSE; }
    else {

      if (strlen(buffer)==0) {
/* team done */
      my_team++;
      if (my_team >= team_array + TEAM_MAX) {
	fprintf(stderr,
		"** FATAL ** team_array exhausted, increase TEAM_MAX\n");
	exit(3);
      }
      done_team = TRUE; }

      else {
/* read in next player and initialize it */
	if (islower(buffer[0])) {
	  extract_player_name(my_player->name, buffer);
	  my_player->games = 0;
	  my_player->minutes = 0;
	  my_player->fgm = 0;
	  my_player->fga = 0;
	  my_player->threem = 0;
	  my_player->threea = 0;
	  my_player->ftm = 0;
	  my_player->fta = 0;
	  my_player->oreb = 0;
	  my_player->treb = 0;
	  my_player->assist = 0;
	  my_player->pfaul = 0;
	  my_player->steal = 0;
	  my_player->turnov = 0;
	  my_player->block = 0;
	  my_player->techn = 0;
	  my_player->eject = 0;
	  my_player->pts = 0;

	  my_team->player_used++;
	  my_player++;
	  if (my_team->player_used >= PLAYER_MAX) {
	    fprintf(stderr,
	       "** FATAL ** team_player exhausted, increase PLAYER_MAX\n");
	    exit(4);
	  }
	}
      }
    }

  }
  fclose(rosterfile);
}

/* read and process boxscores */
void readscore(scorefile)
FILE *scorefile;
{
  char buffer[LINESIZE], home[4], visit[4], *name;
  team *my_team;
  player *my_player;

/* file must start with GAME line */
  myfgets(buffer, LINESIZE, scorefile);
  if (strncmp(buffer, "GAME,", 5)) {
    fprintf(stderr, "** FATAL ** file begins with:\n%s\n", buffer);
    fprintf(stderr,
	    "** FATAL ** file must begin with GAME line, skipping it\n");
    exit(9);
  }
/* get team names from GAME line*/
  strncpy(visit, buffer+5, 3);
  visit[3] = '\0';
  strncpy(home, buffer+10, 3);
  home[3] = '\0';

/* scan file */
  while (myfgets(buffer, LINESIZE, scorefile) != NULL ) {
/* ignore blank lines */
    if (!buffer[0]) {}
/* GAME resets team names */
    else if (!strncmp(buffer, "GAME,", 5)) {
      strncpy(visit, buffer+5, 3);
      visit[3] = '\0';
      strncpy(home, buffer+10, 3);
      home[3] = '\0'; }
/* VISITOR set team to `visit' */
    else if (!strncmp(buffer, "VISITOR,", 8)) {
      my_team = find_team(visit);
      my_team->games_analyzed++; }
/* HOME set team to `home' */
    else if (!strncmp(buffer, "HOME,", 5)) {
      my_team = find_team(home);
      my_team->games_analyzed++; }
/* END, COMMENT, MISSING ignored */
    else if (!strncmp(buffer, "END,", 4)) {}
    else if (!strncmp(buffer, "COMMENT", 7)) {}
    else if (!strncmp(buffer, "MISSING", 7)) {}
/* line beginning with digit presently ignored */
    else if (isdigit(buffer[0])) {}
/* all else should be player stats */
    else {
      update_player(buffer, my_team);
    }
  }
}

/* find team by name (exact match requested) */
team *find_team(name)
     char *name;
{
  team *my_team;

  for (my_team = team_array;
       strcmp(name,my_team->name) &&
          my_team < team_array + TEAM_MAX;
       my_team++);
  if (my_team == team_array + TEAM_MAX) {
    fprintf(stderr, "** FATAL ** team \"%s\" not found\n", name);
    exit(7);
  }
  return(my_team);
}

/* find player by name, extract and accumulate stats */
void update_player(buffer, my_team)
     char *buffer;
     team *my_team;
{
  player *my_player, *pp, read_player;
  char name[24], buf;
  int i, maxlike;

/* match player name */
  extract_player_name(name, buffer);
  maxlike = 0;
  for (pp=my_team->team_player;
       pp < my_team->team_player + my_team->player_used;
       pp++) {
    i = MAX(strnlike(pp->name, name), strnlike(name, pp->name));
    if (i>maxlike) {
      my_player = pp;
      maxlike = i;
    }
  }
/* name was not close enough, reject */
  if (maxlike < 5) {
    fprintf(stderr,
	    "** WARNING ** rejected match for player \"%s\" on team %s\n",
	    name, my_team->name);
    fprintf(stderr,
	    "** WARNING ** closest match was         \"%s\"\n",
	    my_player->name); }
  else {
/* name was close, warning if not VERY close */
    if (maxlike < strlen(name)-5) {
      fprintf(stderr,
	      "** WARNING ** accepted match for player \"%s\" on team %s\n",
	      name, my_team->name);
      fprintf(stderr,
	      "** WARNING ** closest match was         \"%s\"\n",
	      my_player->name);
    }

/* extract stats */
    scan_player(buffer+strlen(name), &read_player);
    read_player.pts = read_player.ftm +
      read_player.threem + 2*read_player.fgm;
/* accumulate */
    my_player->games++;
    my_player->minutes += read_player.minutes;
    my_player->fga     += read_player.fga;
    my_player->fgm     += read_player.fgm;
    my_player->threea  += read_player.threea;
    my_player->threem  += read_player.threem;
    my_player->fta     += read_player.fta;
    my_player->ftm     += read_player.ftm;
    my_player->oreb    += read_player.oreb;
    my_player->treb    += read_player.treb;
    my_player->assist  += read_player.assist;
    my_player->pfaul   += read_player.pfaul;
    my_player->steal   += read_player.steal;
    my_player->turnov  += read_player.turnov;
    my_player->block   += read_player.block;
    my_player->techn   += read_player.techn;
    my_player->eject   += read_player.eject;
    my_player->pts     += read_player.pts;
  }
}

/* sort players and print stats */
/* this is the routine you will want to modify */
void printout()
{
  team *my_team;
  player *my_player, *pptrs[PLAYER_MAX];
  int i;
  double norm;

  for (my_team = team_array;  my_team <= team_array + TEAM_MAX;  my_team++) {
    if (my_team->name[0] && my_team->games_analyzed) {
      printf("-------------------------- Team: %3s ", my_team->name);
      printf("(%2d matches) ----------------------------\n",
	     my_team->games_analyzed);
      printf("       name    gam min field g. 3 ptrs. ");
      printf("free thr off/tot rb stl lost block  pts\n");

/* sort players by total minutes played */
/* change order_player() to change order */
      for (i=0; i<my_team->player_used; i++) {
	pptrs[i] = my_team->team_player + i; 
      }
      qsort((char *) pptrs, my_team->player_used,
	    sizeof(player *), order_player);

/* loop over sorted players */
      for (i=0; i<my_team->player_used; i++) {
	my_player = pptrs[i];
	if (my_player->games>0) {
	  norm = 1.0/my_player->games;
/* normalize and print stats */
	  printf("%15.15s%3d%3.0f%5.1f%3d%%%5.1f%3d%%%5.1f%3d%%",
		 my_player->name,
		 my_player->games, norm*my_player->minutes,
		 norm*my_player->fgm,
		 PERCENT(my_player->fgm,my_player->fga), 
		 norm*my_player->threem,
		 PERCENT(my_player->threem,my_player->threea), 
		 norm*my_player->ftm,
		 PERCENT(my_player->ftm,my_player->fta));
	  printf("%5.1f%5.1f%5.1f%5.1f%5.1f%6.1f\n",
		 norm*my_player->oreb, norm*my_player->treb,
		 norm*my_player->steal, norm*my_player->turnov,
		 norm*my_player->block, norm*my_player->pts);
	}
      }
    }
  }
}

/* utility routine to sort in total minutes order */
int order_player(pp1, pp2)
     player **pp1, **pp2;
{
  return ((*pp2)->minutes - (*pp1)->minutes);
}

/* utility routine to fond "how close" two strings are */
/* this routine should be improved */
int strnlike( s, t )
     char *s, *t;
{
  char *cp;
  int j;

  j = 0;
  cp = strchr( s, *t );
  while( cp && *cp )
    {
      cp++;
      j++;
      t++;
      cp = strchr( cp, *t );
    }
  return( j );
}

/* like fgets, but delete final NEWLINE */
char *myfgets(s, n, stream)
     char *s;
     int n;
     FILE *stream;
{
  int l;
  char *o;

  o = fgets(s, n, stream);
  if (o==NULL) return NULL;

  l = strlen(s)-1;
  if (s[l]=='\n') s[l] = '\0';
  return s;
}

/* extract player name from the beginning of a line */
char *extract_player_name(name, buffer)
     char *name, *buffer;
{
  char *p1, *p2;
  int commas;

  for (p1=name, p2=buffer, commas=0;
       commas<2 && *p2 && !isspace(*p2) && p1 < name+24;
       p1++, p2++) {
    if (*p2==',') commas++;
    if (commas<2) {
      if (isalpha(*p2)) { *p1 = tolower(*p2); }
      else { *p1 = *p2; }
    }
  }
  *p1 = '\0';

  return name;
}

/* extract player stats from a line (player name already deleted) */
void scan_player(buffer, read_player)
     char *buffer;
     player *read_player;
{
  int *ip;
  char *p;
  
  p = buffer;
  for (ip=&(read_player->minutes); ip<&(read_player->eject); ip++) {
    for (; *p == ','; p++) {}
    if (isdigit(*p)) {
      sscanf(p, "%d", ip); }
    else if (*p=='y') {
      *ip = 1; }
    else {
      *ip = 0; }
    for (; *p != '\0' && *p != ','; p++) {}
  }

  if (read_player->fga < read_player->fgm) {
    fprintf(stderr,"** FATAL ** fga < fgm\n");
    exit(24); }
  if (read_player->threea < read_player->threem) {
    fprintf(stderr,"** FATAL ** threea < threem\n");
    exit(24); }
  if (read_player->fta < read_player->ftm) {
    fprintf(stderr,"** FATAL ** fta < ftm\n");
    exit(24); }
}
