#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "geneticTest.h"

#define DEBUG 0
#define MAX_CITIES 100 
#define LINE_BUFFER_SIZE 1000
#define MAX_FIELDS_PER_RECORD 5
#define POPULATION_MAX_SIZE 200

struct genome *population[POPULATION_MAX_SIZE+1];
int populationSize = 5;



/*=========================== cityList =======================*/
/* cityList is a list of all the cities in the graph.         */
/* As the graph is being created, cityCount is updated so that*/
/* it equals the number of cities currently in the graph.     */
/* All cities are stored in cityList[0] through               */
/* cityList[cityCount-1].                                     */
/* The cities are not sorted in cityList.                     */
/* New cities are added at the end of the list.               */
struct node *cityList[MAX_CITIES];
int cityCount = 0;
struct node *startCity, *endCity;
/*============================================================*/




/*========================================================================*/
/* newFlight(struct node* destination, int cost)                          */
/*   malloc memory for a new halfEdge.                                    */
/*   The pointer returned is always placed at the end of the chain of     */
/*   the of halfEdges from a city.                                        */
/*========================================================================*/
struct halfEdge* newFlight(struct node* destination, int cost)
{ struct halfEdge *e = malloc(sizeof(struct halfEdge));
  e->city = destination;
  e->cost = cost;
  e->next = NULL;
  return e;
}




/*========================================================================*/
/* newCity(char* name)                                                    */
/*    malloc memory for a new city and also malloc memory within that     */
/*    city for a copy of name.                                            */
/*========================================================================*/
struct node* newCity(char* name)
{ struct node *n = malloc(sizeof(struct node));
  n->name = strdup(name); //This makes a call to malloc and must be freed.
  n->next = NULL;
  n->edgeCount = 0;
  cityList[cityCount] = n;
  cityCount++;
  return n;
}



/*========================================================================*/
/* malloc memory for every genome in the population +1 for the child.     */
/* Always create the child by overwriting the pointers and values in      */
/* population[populationSize] (this is the least fit dude).              */
/* This saves having to free and re malloc space for childern.            */
/*========================================================================*/
void mallocGenoms()
{ int i;
  for (i=0; i<=populationSize; i++)
  { population[i] = malloc(sizeof(struct genome));
  }
  
}


/*========================================================================*/
/* Hard coded example of how to represent a genome: BCEADA                */
/*   This, of course, is not random.                                      */
/*========================================================================*/
void setGenome(struct genome* g)
{ g->gene[0] = cityList[0]; //B
  g->gene[1] = cityList[2]; //C
  g->gene[2] = cityList[3]; //E
  g->gene[3] = cityList[1]; //A
  g->gene[4] = cityList[4]; //D
  g->gene[5] = cityList[1]; //A

  g->length = 6;
  g->cost = 42;

}

/*========================================================================*/
/* printGenome(struct genome* g)                                          */
/*========================================================================*/
void printGenome(struct genome* g)
{ int i;
  int length = g->length;
  printf("cost=$%6d: { ", g->cost);
  for (i=0; i<length; i++)
  { printf("[%s] ",g->gene[i]->name); 
  }
  printf("}\n");
}


/*========================================================================*/
/* getCity(char* name)                                                    */
/*    Searches cityList for a city with name equal to name.               */
/*    If there is not yet a city with that name, then this function       */
/*    creates a new city.                                                 */
/*    This search is case sensitive.                                      */
/*========================================================================*/
struct node* getCity(char* name)
{ int i;
  for (i=0; i<cityCount; i++)
  { if (strcmp(name, cityList[i]->name) == 0)
    return cityList[i];
  }
  return newCity(name);
}





/*========================================================================*/
/* removeLeadingBlanks(char *s)                                           */
/*    Returns a pointer to the first non-blank character in s.            */
/*    This is used to remove any blanks at the start of a city name       */
/*    whenever a city name is read form the input file.                   */
/*========================================================================*/
char* removeLeadingBlanks(char *s)  
{ while (*s == ' ')
  { s++; 
  }
  return s;
}



/*========================================================================*/
/* isWhiteSpace(char c)                                                   */
/*    returns 1 if c is white space character, otherwise returns 0.       */
/*========================================================================*/
int isWhiteSpace(char c)
{ if(c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f') return 1;
  return 0;
}


/*========================================================================*/
/* removeWhiteSpaceFromEnd(char *s)                                       */
/*    Replaces all white spacecharacters at the end of s with '\0'.       */
/*========================================================================*/
void removeWhiteSpaceFromEnd(char *s)  
{ char* end = (s + strlen(s)) - 1;
  while (isWhiteSpace(*end))
  { *end = 0; 
    end--;
    if (end==s) return;
  }
}

/*========================================================================*/
/* parseLineToArray(char *array[], char* line)                            */
/*    Given a character array with fields delimited by ',', this function */
/*    sets values of an array of string pointers to reates an array like  */
/*    argv[] in main(int argc, char *argv[]), so that the first character */
/*    in each field of line is pointed to by each element of argvs.       */
/*    All leading and trailing blanks are removed.                        */
/*========================================================================*/
int parseLineToArray(char *array[], char* line)
{ 
  array[0] = line;
  int parmIdx=1;
  int foundLetter = 0;

  while (*line)
  { if (*line == ',') 
    { *line = '\0';
      array[parmIdx] = line+1;

      parmIdx++;
      if (parmIdx >= MAX_FIELDS_PER_RECORD)
      { printf("***ERROR*** parseLineIntoArray(%s): too many parms\n");
        exit(0);
      }
    }
    line++;
  }
  array[parmIdx] = NULL;

 
  int i;
  for (i=0; i<parmIdx; i++)
  { 
    array[i] = removeLeadingBlanks(array[i]);
    removeWhiteSpaceFromEnd(array[i]);
    printf("[%s] ", array[i]);
  }
  printf("\n");
  return parmIdx;
}




/*========================================================================*/
/* addFlight(struct node *city1, struct node *city2, int cost)            */
/*========================================================================*/
void addFlight(struct node *city1, struct node *city2, int cost)
{ city1->edgeCount++;
  if (city1->next == NULL)
  { city1->next = newFlight(city2, cost);
  }
  else
  { struct halfEdge *flight = city1->next;
    while (flight->next) 
    { flight = flight->next;
    }
    flight->next = newFlight(city2, cost);
  }
}


/*========================================================================*/
/* addRoundTrip(char *edge[])                                             */
/*   Each record from the input file is parsed with parseLineToArray().   */
/*   Thus, edge[0] is the first city                                      */
/*         edge[1] is the second city                                     */
/*         edge[2] is the cost to fly from city1 to city2.                */
/*         edge[3] is the cost to fly from city2 to city1.                */
/*========================================================================*/
void addRoundTrip(char *edge[])
{ struct node *city1 = getCity(edge[0]);
  struct node *city2 = getCity(edge[1]);
  int cost12 = atoi(edge[2]);
  int cost21 = atoi(edge[3]);
  addFlight(city1, city2, cost12);
  addFlight(city2, city1, cost21);
}



/*========================================================================*/
/* addFlight(struct node *city1, struct node *city2, int cost)            */
/*========================================================================*/
void printGraph()
{ printf("============== printGraph ================\n");
  int i;
  for (i=0; i<cityCount; i++)
  { printf("%2d) %s =>[%d]: ", i, cityList[i]->name, cityList[i]->edgeCount);
    struct halfEdge *flight = cityList[i]->next;
    while (flight)
    { printf("%s(%d) ==> ", flight->city->name, flight->cost);
      flight = flight->next;
    } 
    printf("\n");
  }

}




//==============================================================================
// main 
//==============================================================================
int main(int argc, char *argv[])
{

  /********* Unit Test for removeLeadingBlanks() **********/ 
  char str1[14] = {"     foo    \n"}; 
  char* result = str1;
  result = removeLeadingBlanks(result);   
  assert(strcmp(result, "foo    \n")==0);

  result = removeLeadingBlanks(result);  
  assert(strcmp(result, "foo    \n")==0);
  /*******************************************************/
 
  


  
  /***** Unit Test for removeWhiteSpaceFromEnd() ****/
  removeWhiteSpaceFromEnd(result);   
  assert(strcmp(result, "foo")==0);

  removeWhiteSpaceFromEnd(str1);   
  assert(strcmp(result, "foo")==0);
  /*******************************************************/





  /********** Unit Test for parseLineToArray *************/
  char str2[32] = {"  City1 , City2, 5404     ,3210"};
  char* testLine = str2;
  char* testParms[MAX_FIELDS_PER_RECORD];
  int n1 = parseLineToArray(testParms, testLine);
  assert(strcmp(testParms[0],"City1")==0);
  assert(strcmp(testParms[1],"City2")==0);
  assert(strcmp(testParms[2],"5404")==0);
  assert(strcmp(testParms[3],"3210")==0);
  assert(n1 == 4);
  /*******************************************************/




  /********** Unit Test for parseLineToArray *************/
  char str3[13] = {"City1,City2\n"};
  testLine = str3;
  n1 = parseLineToArray(testParms, testLine);
  assert(strcmp(testParms[0],"City1")==0);
  assert(strcmp(testParms[1],"City2")==0);
  assert(n1 == 2);
  /*******************************************************/

  printf("Passed Unit tests\n\n\n");

  if (argc != 2)
  { printf("**** ERROR ****  expected the filename of a graph\n");
    exit(0);
  }
  

  //Open the input graph file.
  char* fileName = argv[1];
  FILE *inFile = fopen(fileName, "r");
  char lineBuffer[LINE_BUFFER_SIZE];
  char *parmList[MAX_FIELDS_PER_RECORD];


  //The first line of the graph file is special and only contains the 
  //name of the start and end cities.
  fgets(lineBuffer, LINE_BUFFER_SIZE, inFile);
  
  int parmCount = parseLineToArray(parmList, lineBuffer);
  if (parmCount != 2) 
  { printf("***ERROR*** First line of graph file must be: city1,city2\n");
    exit(0);
  }
  
  startCity = getCity(parmList[0]);
  endCity   = getCity(parmList[1]);
  

  //Loop through each line of the graph file and build the graph.
  int i=0;
  while (fgets(lineBuffer, LINE_BUFFER_SIZE, inFile))
  {
    parmCount = parseLineToArray(parmList, lineBuffer); 
    if (parmCount != 4) 
    { printf("***ERROR*** graph file line: city1,city2,cost12,cost21.\n");
      exit(0);
    }

    addRoundTrip(parmList);
  }


  printGraph();

  mallocGenoms();
  for (i=0; i<populationSize; i++)
  { //This should set the genomes to random, valid tours.
    setGenome(population[i]);
  }

  //for (i=0; i<1000000; i++)
  //{ sortPopulation();
  //  int parent1Idx = selection();
  //  int parent2Idx = selection();
  //  struct genome* parent1 = population[parent1Idx];
  //  struct genome* parent2 = population[parent2Idx];
  //  struct genome* child = population[populationSize];
  //  crossover(parent1, parent2, child)  //this sometimes includes mutation.
  //  if (i % 1000 == 0)
  //  { printf("generation %d:\n", i);
  //    printf("    %3d:", parent1Idx); printGenome(parent1);
  //    printf("    %3d:", parent2Idx); printGenome(parent2);
  //    printf("        "); printGenome(child);
  //  }   
  //}

  printf("\n\n============ Final Population ============\n");
  for (i=0; i<populationSize; i++)
  { printGenome(population[i]);
  }
}

