//===============================================
//  File:  futmex.cpp
//  By:    Roger Z. Rios Mercado
//  Date:  SP/94
//  Revised: JL/96
//===============================================


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>

#include "llist.hpp"
#include "teamClass.hpp"

#define MAX_STRING        30     // Maximum number of characters for
                                 // a team name

//
//   Function prototypes.
//
void read_data(char*, int&, int&, team**&);
int data_checking(int&, team**&);
void print_stats(FILE*, int, container&, container&, container&,
		 container&, container**);
int cmp_overall(void*, void*);
int cmp_home(void*, void*);
int cmp_away(void*, void*);
int cmp_hell(void*, void*);
void print_short(FILE*, void*);
void print_hell(FILE*, void*);
void print_home(FILE*, void*);
void print_away(FILE*, void*);
void print_long(FILE*, void*);
void magic_number(const int, container&);
int cmp_mn(void*, void*);

int main(int argc, char** argv) {

     char* prog_name = NULL;     // Program name
     char* fname = NULL;         // Input file name
     char* out_fname = NULL;     // Output file name
     FILE* out = NULL;           // Output file ptr

     container* overall = NULL;   // Linked list storing teams (overall)
     container* home = NULL;      // Linked list storing teams (home)
     container* away = NULL;      // Linked list storing teams (visitor)
     container* hell = NULL;      // Linked list storing teams (relegation)
     container** groups = NULL;   //Array of linked lists (per group)

     team** team_array = NULL;
     int n_teams;                 // Number of teams.
     int n_groups;                // Number of groups.
     int check_flag;              // 0 = OK

     //
     //   Skip program name.
     //
     prog_name = *argv;
     argc--;  argv++;

     if (argc == 0) {
	  printf("Sorry, too few arguments.\n");
	  printf("Correct syntax:\n\n");
	  printf("%s input_fname \n", prog_name);
	  exit(1);
     } else if (argc > 2) {
	  printf("Sorry, too many arguments.\n");
	  exit(1);
     }

     fname = *argv;

     //
     //   If only one argument, print to stdout, otherwise
     //   print to the given file.
     //
     if (argc == 1) {
	  out = stdout;
     } else {
	  out_fname = *++argv;
	  if ((out = fopen(out_fname, "wt")) == NULL) {
	       printf("Can't open %s for output\n", out_fname);
	       exit(1);
	  }
     }

     //
     //   Read data.
     //
     read_data(fname, n_teams, n_groups, team_array);

     //
     //   Check data: total wins = total losses,
     //   and total gf = total ga
     //   If no match (flag = 1) signal in output file.
     //
     if ((check_flag = data_checking(n_teams, team_array)) != 0)
	  fprintf(out, "WARNING: BAD MATCHING\n");


     //
     //   Allocate space for linked lists.
     //
     overall = new llist(cmp_overall, "Overall");
     assert (overall);
     home = new llist(cmp_home, "Home");
     assert (home);
     away = new llist(cmp_away, "Away");
     assert (away);
     hell = new llist(cmp_hell, "Hell");
     groups = new container* [n_groups];
     assert (groups);

     for (int i = 0; i < n_groups; i++) {
	  groups[i] = new llist(cmp_overall, "Grupo");
	  assert (groups[i]);
     }

     //
     //  Process every team and insert in appropiate lists.
     //
     for (i = 0; i < n_teams; i++) {
	  overall->insert(team_array[i]);
	  home->insert(team_array[i]);
	  away->insert(team_array[i]);
	  hell->insert(team_array[i]);
	  groups[team_array[i]->group - 1]->insert(team_array[i]);
     }

     //
     //   Get the (relegation) magic numbers.
     //
     magic_number(n_teams, *hell);

     //
     //     Output.
     //
     print_stats(out, n_groups, *overall, *home, *away, *hell, groups);

     //
     //   Close file.
     //
     if (out_fname != NULL)
	  fclose(out);

     //
     //   Restore free memory
     //
     for (i = 0; i < n_groups; i++) {
	  groups[i]->flush_nodes();
	  delete groups[i];
     }

     // In UNIX use rather: delete [] groups
     delete groups;

     hell->flush_nodes();
     delete hell;

     away->flush_nodes();
     delete away;

     home->flush_nodes();
     delete home;

     overall->flush_nodes();
     delete overall;

     for (i = 0; i < n_teams; i++) {
	  delete team_array[i];
     }

     // In UNIX use rather: delete [] team_array
     delete team_array;

     return (1);    //OK
}


/////////////////////////////////////////////////
//     Data checking: return 0 if OK
/////////////////////////////////////////////////
int data_checking(int& n, team**& t) {

     int tot_w = 0, tot_l = 0, tot_f = 0, tot_a = 0;
     int tot_hw = 0, tot_hl = 0, tot_aw = 0, tot_al = 0;
     int tot_hf = 0, tot_ha = 0, tot_af = 0, tot_aa = 0;

     for (int i = 0; i < n; i++) {
	tot_w += t[i]->w;
	tot_l += t[i]->l;
	tot_f += t[i]->f;
	tot_a += t[i]->a;
	tot_hw += t[i]->hw;
	tot_hl += t[i]->hl;
	tot_hf += t[i]->hf;
	tot_ha += t[i]->ha;
	tot_aw += t[i]->aw;
	tot_al += t[i]->al;
	tot_af += t[i]->af;
	tot_aa += t[i]->aa;
     }

     if (tot_w == tot_l && tot_f == tot_a && tot_hw == tot_al &&
	 tot_hl == tot_aw && tot_hf == tot_aa && tot_ha == tot_af)
	  return (0);  // OK
     else
	  return (1);  // Bad match

}

/////////////////////////////////////////////////
//     Read input data
/////////////////////////////////////////////////
void read_data(char* fname, int& n, int& ng, team**& t) {

     FILE* in = NULL;             // Input file ptr.
     char team_name[MAX_STRING];  // Team name.
     int ngroup;                  // Group number.
     int g_2, f_2, a_2, pts_2;    // Games, for, against, pts (2 years back).
     int g_1, f_1, a_1, pts_1;    // Games, for, against, pts (1 year back).
     int hw, hd, hl, hf, ha;      // Home wins, draws, loses, for, against.
     int aw, ad, al, af, aa;      // Away wins, draws, loses, for, against.

     if ((in = fopen(fname, "rt")) == NULL) {
	 printf("Can't open input file %s \n", fname);
	 exit(1);
     }

     //
     //   Read number of teams and number of groups.
     //
     fscanf(in, "%d %d", &n, &ng);

     //
     //   Allocate space in array t for n teams.
     //
     t = new team* [n];
     assert(t);

     //
     //   Read every team, create a new team class, store
     //   ptr in array t.
     //
     for (int i = 0; i < n; i++) {
	  //
	  //   Read team name and group.
	  //
	  fscanf(in, "%s %d", team_name, &ngroup);

	  //
	  //   Read team history (past two seasons).
	  //
	  fscanf(in, "%d %d %d %d %d %d %d %d",
		     &g_2, &f_2, &a_2, &pts_2,
		     &g_1, &f_1, &a_1, &pts_1);

	  //
	  //   Read current record.
	  //
	  fscanf(in, "%d %d %d %d %d %d %d %d %d %d",
		     &hw, &hd, &hl, &hf, &ha, &aw, &ad, &al, &af, &aa);

	  //
	  //   Create new team.
	  //
	  t[i] = new team(team_name, ngroup, g_2, f_2, a_2, pts_2,
			  g_1, f_1, a_1, pts_1, hw, hd, hl, hf, ha,
			  aw, ad, al, af, aa);
	  assert(t[i]);

     }     // End main loop.

     fclose (in);
}

//////////////////////////////////////////////////
//  <compare> function for overall standings.
//////////////////////////////////////////////////
int cmp_overall(void* a, void* b) {

     team* p = (team*) a;
     team* q = (team*) b;

     if (p->pts > q->pts)
	 return (-1);
     else if (p->pts < q->pts)
	 return (1);
     else if (p->dif > q->dif)
	 return (-1);
     else if (p->dif < q->dif)
	 return (1);
     else if (p->f > q->f)
	 return (-1);
     else if (p->f < q->f)
	 return (1);
     else
	 return (0);
}

///////////////////////////////////////////////////
//   <compare> function for home standigs.
///////////////////////////////////////////////////
int cmp_home(void* a, void* b) {

     team* p = (team*) a;
     team* q = (team*) b;

     if (p->hpts > q->hpts)
	 return (-1);
     else if (p->hpts < q->hpts)
	 return (1);
     else if (p->hdif > q->hdif)
	 return (-1);
     else if (p->hdif < q->hdif)
	 return (1);
     else if (p->hf > q->hf)
	 return (-1);
     else if (p->hf < q->hf)
	 return (1);
     else
	 return (0);
}

/////////////////////////////////////////////////
//   <compare> function for away standings.
/////////////////////////////////////////////////
int cmp_away(void* a, void* b) {

     team* p = (team*) a;
     team* q = (team*) b;

     if (p->apts > q->apts)
	 return (-1);
     else if (p->apts < q->apts)
	 return (1);
     else if (p->adif > q->adif)
	 return (-1);
     else if (p->adif < q->adif)
	 return (1);
     else if (p->af > q->af)
	 return (-1);
     else if (p->af < q->af)
	 return (1);
     else
	 return (0);
}

/////////////////////////////////////////////////
//   <compare> function for relegation standing.
/////////////////////////////////////////////////
int cmp_hell(void* a, void* b) {

     team* p = (team*) a;
     team* q = (team*) b;

     if (p->ptge > q->ptge)
	 return (-1);
     else if (p->ptge < q->ptge)
	 return (1);
     else if (p->dif_avg > q->dif_avg)
	 return (-1);
     else if (p->dif_avg < q->dif_avg)
	 return (1);
     else if (p->f_avg > q->f_avg)
	 return (-1);
     else if (p->f_avg < q->f_avg)
	 return (1);
     else
	 return (0);
}

/////////////////////////////////////////////////
//   <compare> function for magic number comp.
/////////////////////////////////////////////////
int cmp_mn(void* a, void* b) {

     team* p = (team*) a;
     team* q = (team*) b;

     if (p->best_ptge > q->best_ptge)
	 return (-1);
     else if (p->best_ptge < q->best_ptge)
	 return (1);
     else
	 return (0);
}

///////////////////////////////////////////////////////////
//     Computing relegation magic number.
///////////////////////////////////////////////////////////
void magic_number(const int nt, container& hell) {

     //
     //   Working parameters.
     //
     int NREL = 1;            // Number of relegated teams;
     int ng, remaining_pts;
     double max_ptge = 0.0;
     container* mn = NULL;

     //
     //   Create linked list to store teams by best_ptge.
     //
     mn = new llist (cmp_mn, "MN");
     assert (mn);

     //
     //   Compute number of games (ng)
     //   In the winter season it is 2(nt-1)
     //   In the summer season is (nt-1)
     
     // Use this line for Winter
     ng = 2 * (nt - 1);

     // Use this line for summer
     //ng = 1 * (nt - 1);


     //
     //   For every team in <hell>, compute its best_percentage,
     //   and insert it in <mn>.
     //
     team* t = (team *) (hell.first());

     while(t != NULL) {
	  remaining_pts = PTS_PER_WIN_HELL * (ng - t->g);
	  t->best_ptge =
	  (double) (t->pts_t + remaining_pts) / (double) (t->g_2 + t->g_1 + ng);
	  mn->insert(t);
	  t = (team *) (hell.next());
     }

     //
     // Remove from the list the possible relegated teams
     // and get the common best_ptge maximum.
     //
     for (int i = 0; i < NREL; i++) {
	  t = (team *) (mn->remove(mn->last()));
	  t->mn = 1000;   // Relegated.
	  if (t->best_ptge > max_ptge)
	       max_ptge = t->best_ptge;
     }

     //
     //   Traverse remaining of list and compute magic number.
     //   Remove node from struct <mn>.
     //
     while ((t = (team*) (mn->remove(mn->first()))) != NULL) {
       t->mn = max_ptge * (t->g_2 + t->g_1 + ng) - t->pts_t + 1.00001;
       // If MN is negative, reset it to 0 (it means team is "safe")
       if (t->mn < 0)
            t->mn = 0;
     }

     //
     //   Restore free memory.
     //
     delete (mn);
}

/////////////////////////////////////////////////////////////
//     Print output.
/////////////////////////////////////////////////////////////
void print_stats(FILE* out, int ng, container& o, container& h,
		 container& a, container& hell, container** g) {

     //
     //   Title.
     //
     fprintf(out, "----- Mexican League -----\n\n");

     //
     //     Group standings.
     //
     fprintf(out, " STANDINGS");
     for (int i = 0; i < ng; i++) {
	  fprintf(out, "\n");
	  fprintf(out, " ----------------------------------------------\n");
	  //fprintf(out, " ---------------------------------------------------\n");
	  fprintf(out,
		  "     GROUP %1d                  G    F:A   PTS\n", i + 1);
		  //"     GROUP %1d                  G    F:A   PTS   MN\n", i + 1);
	  fprintf(out, " ----------------------------------------------\n");
	  //fprintf(out, " ---------------------------------------------------\n");
	  g[i]->traverse_op(out, print_short);
	  fprintf(out, " ----------------------------------------------\n");
	  //fprintf(out, " ---------------------------------------------------\n");
     }

     /*
      fprintf(out, "\n"\
	     " MN = Magic number of points needed to clinch a play-\n"\
	     "      off berth.\n"\
	     "      (*) Qualified.\n"\
	     "      (X) Out.\n");
     */

     //
     //     Percentage standing.
     //
     fprintf(out, "\n\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
	//" -------------------------------------------------------------\n");
     fprintf(out, 
	     "    HIGHWAY TO HELL           PTGE      (PTS/GMS)    MN\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
	//" -------------------------------------------------------------\n");
     hell.traverse_op(out, print_hell);
     fprintf(out,
	     " ---------------------------------------------------------\n");
	//" -------------------------------------------------------------\n");
     fprintf(out,
	     "\n The team with the lowest percentage (PTGE) at the end\n");
     fprintf(out,
	     " of the regular season is relegated to Division 1-A.\n\n");
     fprintf(out,
	     "   PTGE = Total of points earned from the 1994-95 through\n");
     fprintf(out,
	     "          the 1996-97 season divided by the total of games\n");
     fprintf(out,
	     "          played during that period.  Only regular season,\n");
     fprintf(out,
	     "          Division 1 matches are considered.  Points are\n");
     fprintf(out,
	     "          based on 2 points per each game won and 1 point\n");
     fprintf(out,
	     "          per each game tied.\n\n");
     fprintf(out,
	     "   MN = Magic number of points needed to avoid relegation.\n");
     /*
     fprintf(out,
	     "        (provided team finishing last does not reach the\n");
     fprintf(out,
	     "        championship match).  Only teams with mathematical\n");
     fprintf(out,
	     "        chances are listed.\n");
     */
     /*
     fprintf(out,
	     "   MN = Magic number of points needed to avoid relegation\n");
     fprintf(out,
	     "        provided team finishing last does not reach the\n");
     fprintf(out,
	     "        championship match.\n\n");
     fprintf(out,
	     "   AMN = Absolute MN, assuming both teams finishing last\n");
     fprintf(out,
	     "         do reach the championship match.  Only teams\n");
     fprintf(out,
	     "         with mathematical chances are listed.\n");
     */

     //
     //     Home stats.
     //
     fprintf(out, "\n\n");
     //fprintf(out, "\n\n TOP FIVE HOME SQUADS\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
     fprintf(out,
	     "    HOME STATS                G    W  D  L    F  A  PTS\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
     h.traverse_op(out, print_home);
     fprintf(out,
	     " ---------------------------------------------------------\n");

     //
     //     Away stats.
     //
     fprintf(out, "\n\n");
     //fprintf(out, "\n\n TOP FIVE AWAY SQUADS\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
     fprintf(out,
	     "    AWAY STATS                G    W  D  L    F  A  PTS\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
     a.traverse_op(out, print_away);
     fprintf(out,
	     " ---------------------------------------------------------\n");

     //
     //     Overall stats.
     //
     fprintf(out, "\n\n");
     //fprintf(out, "\n\n OVERALL STANDINGS\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
     fprintf(out,
	     "    OVERALL STANDING          G    W  D  L    F  A  PTS\n");
     fprintf(out,
	     " ---------------------------------------------------------\n");
     o.traverse_op(out, print_long);
     fprintf(out,
	     " ---------------------------------------------------------\n");

     //
     //   End.
     //
     fprintf(out, "\n\n---- End ----\n");

}


void print_short(FILE* out, void* data) {

     static int place = 0;
     static int group = 0;

     team* t = (team *) data;

     char*& name = t->name;
     int& g = t->g;
     int& f = t->f;
     int& a = t->a;
     int& pts = t->pts;

     if (t->group == group)
	  place++;
     else {
	  place = 1;
	  group = t->group;
     }

     fprintf(out, "    %2d. %-20s", place, t->name);
     fprintf(out, " %2d   %2d-%2d   %2d\n", g, f, a, pts);
}

void print_hell(FILE* out, void* data) {

     static place = 1;
     team* t = (team *) data;

     char*& name = t->name;
     int& t_g = t->g_t;
     int& t_pts = t->pts_t;
     double& ptge = t->ptge;
     int& mn = t->mn;

     fprintf(out, "    %2d. %-20s", place++, name);
     fprintf(out, " %6.5f    (%3d/%3d)", ptge, t_pts, t_g);
     if (mn == 1000)
	  fprintf(out, "     -\n");
     else
	  fprintf(out, "  %4d\n", mn);
}

void print_home(FILE* out, void* data) {

     static place = 1;
     team* t = (team *) data;

     char*& name = t->name;
     int& g = t->hg;
     int& w = t->hw;
     int& d = t->hd;
     int& l = t->hl;
     int& f = t->hf;
     int& a = t->ha;
     int& pts = t->hpts;

     fprintf(out, "    %2d. %-20s", place++, name);
     fprintf(out,
	     " %2d   %2d %2d %2d   %2d %2d   %2d\n", g, w, d, l, f, a, pts);
}

void print_away(FILE* out, void* data) {

     static place = 1;
     team* t = (team *) data;

     char*& name = t->name;
     int& g = t->ag;
     int& w = t->aw;
     int& d = t->ad;
     int& l = t->al;
     int& f = t->af;
     int& a = t->aa;
     int& pts = t->apts;

     fprintf(out, "    %2d. %-20s", place++, name);
     fprintf(out,
	     " %2d   %2d %2d %2d   %2d %2d   %2d\n", g, w, d, l, f, a, pts);
}

void print_long(FILE* out, void* data) {

     static place = 1;
     team* t = (team *) data;

     char*& name = t->name;
     int& g = t->g;
     int& w = t->w;
     int& d = t->d;
     int& l = t->l;
     int& f = t->f;
     int& a = t->a;
     int& pts = t->pts;

     fprintf(out, "    %2d. %-20s", place++, name);
     fprintf(out,
	     " %2d   %2d %2d %2d   %2d %2d   %2d\n", g, w, d, l, f, a, pts);
}


