#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <assert.h>

#include "plan.h"

#define SAMEMISSION 2
#define OTHERMISSION 11

struct blurb_s {
  int seen; /* Whether a solution has been found to the constraint. */
  int din;  /* Direction INdex of arrival at constraint (true value). */
  int prestation; /* Previous station (part of previous constraint). */
  int predin; /* Previous DIN (part of previous constraint), -1 if no DIN constraint. */
  int truepredin; /* True value of previous DIN (never -1). */
  int score; /* Best score satisfying this constraint. */
  int moc;  /* Mission or connection arriving at constraint. */
  int ind;  /* Index in mission of connection (of _previous_ station). */
};

/* Solutions to constraints: with, and without direction constraint. */
struct blurb_s fastmap[NBSTATIONS][MAXDIRSIZE];
struct blurb_s shortfastmap[NBSTATIONS];

void
fill_fastmap (int istation)
{
  int iteration;
  int i, j;
  char change;

  for ( i=0 ; i<NBSTATIONS ; i++ )
    {
      shortfastmap[i].seen = 0;
      for ( j=0 ; j<MAXDIRSIZE ; j++ )
	fastmap[i][j].seen = 0;
    }
  shortfastmap[istation].seen = 1;
  shortfastmap[istation].din = 0;
  shortfastmap[istation].prestation = -1;
  shortfastmap[istation].predin = -1;
  shortfastmap[istation].truepredin = -1;
  shortfastmap[istation].score = 0;
  shortfastmap[istation].moc = -1;
  shortfastmap[istation].ind = -1;
  iteration = 0;
  do
    {
      change = 0;
      for ( i=0 ; i<NBSTATIONS ; i++ )
	if ( shortfastmap[i].seen )
	  {
	    for ( j=0 ; j<directions[i].dirsize ; j++ )
	      {
		int m;
		int stat;
		int din;
		int temptscore;

		m = directions[i].dir[j].moc;
		if ( m < NBMISSIONS )
		  {
		    if ( directions[i].dir[j].ind
			 == missions[m].misssize-1 )
		      continue;
		    if ( fastmap[i][j].seen )
		      {
			stat = missions[m].miss[directions[i].dir[j].ind+1].sta;
			din = missions[m].miss[directions[i].dir[j].ind+1].din;
			temptscore = fastmap[i][j].score+SAMEMISSION;
			if ( ! fastmap[stat][din].seen
			     || fastmap[stat][din].score > temptscore )
			  {
			    change = 1;
			    fastmap[stat][din].seen = 1;
			    fastmap[stat][din].din = din;
			    fastmap[stat][din].prestation = i;
			    fastmap[stat][din].predin = j;
			    fastmap[stat][din].truepredin = j;
			    fastmap[stat][din].score = temptscore;
			    fastmap[stat][din].moc = m;
			    fastmap[stat][din].ind = directions[i].dir[j].ind;
			  }
			if ( ! shortfastmap[stat].seen
			     || shortfastmap[stat].score > temptscore )
			  {
			    change = 1;
			    shortfastmap[stat].seen = 1;
			    shortfastmap[stat].din = din;
			    shortfastmap[stat].prestation = i;
			    shortfastmap[stat].predin = j;
			    shortfastmap[stat].truepredin = j;
			    shortfastmap[stat].score = temptscore;
			    shortfastmap[stat].moc = m;
			    shortfastmap[stat].ind = directions[i].dir[j].ind;
			  }
		      }
		    stat = missions[m].miss[directions[i].dir[j].ind+1].sta;
		    din = missions[m].miss[directions[i].dir[j].ind+1].din;
		    temptscore = shortfastmap[i].score+OTHERMISSION;
		    if ( ! fastmap[stat][din].seen
			 || fastmap[stat][din].score > temptscore )
		      {
			change = 1;
			fastmap[stat][din].seen = 1;
			fastmap[stat][din].din = din;
			fastmap[stat][din].prestation = i;
			fastmap[stat][din].predin = -1;
			fastmap[stat][din].truepredin = j;
			fastmap[stat][din].score = temptscore;
			fastmap[stat][din].moc = m;
			fastmap[stat][din].ind = directions[i].dir[j].ind;
		      }
		    if ( ! shortfastmap[stat].seen
			 || shortfastmap[stat].score > temptscore )
		      {
			change = 1;
			shortfastmap[stat].seen = 1;
			shortfastmap[stat].din = din;
			shortfastmap[stat].prestation = i;
			shortfastmap[stat].predin = -1;
			shortfastmap[stat].truepredin = j;
			shortfastmap[stat].score = temptscore;
			shortfastmap[stat].moc = m;
			shortfastmap[stat].ind = directions[i].dir[j].ind;
		      }
		  }
		else
		  {
		    int k;

		    for ( k=0 ; k<connections[m-NBMISSIONS].connsize ; k++ )
		      {
			stat = connections[m-NBMISSIONS].conn[k].sta;
			din = connections[m-NBMISSIONS].conn[k].din;
			temptscore = shortfastmap[i].score;
			if ( ! shortfastmap[stat].seen
			     || shortfastmap[stat].score > temptscore )
			  {
			    change = 1;
			    shortfastmap[stat].seen = 1;
			    shortfastmap[stat].din = din;
			    shortfastmap[stat].prestation = i;
			    shortfastmap[stat].predin = -1;
			    shortfastmap[stat].truepredin = j;
			    shortfastmap[stat].score = temptscore;
			    shortfastmap[stat].moc = m;
			    shortfastmap[stat].ind = directions[i].dir[j].ind;
			  }
		      }
		  }
	      }
	  }
      iteration++;
    }
  while ( change );
}

void
show_fastmap (void)
{
  int i;
  int maxscore;
  int barscore;
  int minscore;

  maxscore = -1;
  for ( i=0 ; i<NBSTATIONS ; i++ )
    {
      if ( shortfastmap[i].seen && shortfastmap[i].score > maxscore )
	maxscore = shortfastmap[i].score;
    }
  barscore = -1;
  while ( barscore < maxscore )
    {
      char fr;

      minscore = maxscore;
      for ( i=0 ; i<NBSTATIONS ; i++ )
	{
	  if ( shortfastmap[i].seen && shortfastmap[i].score < minscore
	       && shortfastmap[i].score > barscore )
	    minscore = shortfastmap[i].score;
	}
      printf ("%d: ", minscore);
      fr = 1;
      for ( i=0 ; i<NBSTATIONS ; i++ )
	{
	  if ( shortfastmap[i].seen && shortfastmap[i].score == minscore )
	    {
	      if ( ! fr )
		printf (", ");
	      printf ("%s", stations[i].name);
	      fr = 0;
	    }
	}
      printf ("\n");
      barscore = minscore;
    }
}

struct step_s {
  int prestation;
  int predin;
  int moc;
  int ind;
  int poststation;
  int postdin;
};

void
make_fastest_walk (int fstation, int maxsize, struct step_s *walk, int *size)
{
  int i;
  int station, din, prestation, predin;

  i = 0;
  station = fstation;
  din = -1;
  while ( 1 )
    {
      if ( din >= 0 )
	{
	  prestation = fastmap[station][din].prestation;
	  predin = fastmap[station][din].predin;
	}
      else
	{
	  prestation = shortfastmap[station].prestation;
	  predin = shortfastmap[station].predin;
	}
      if ( prestation < 0 )
	break;
      station = prestation;
      din = predin;
      i++;
    }
  *size = i;
  station = fstation;
  din = -1;
  for ( i-- ; i>=0 ; i-- )
    {
      if ( din >= 0 )
	{
	  prestation = fastmap[station][din].prestation;
	  predin = fastmap[station][din].predin;
	}
      else
	{
	  prestation = shortfastmap[station].prestation;
	  predin = shortfastmap[station].predin;
	}
      if ( i<maxsize )
	{
	  walk[i].prestation = prestation;
	  if ( din >= 0 )
	    {
	      walk[i].predin = fastmap[station][din].truepredin;
	      walk[i].moc = fastmap[station][din].moc;
	      walk[i].ind = fastmap[station][din].ind;
	      walk[i].postdin = fastmap[station][din].din;
	    }
	  else
	    {
	      walk[i].predin = shortfastmap[station].truepredin;
	      walk[i].moc = shortfastmap[station].moc;
	      walk[i].ind = shortfastmap[station].ind;
	      walk[i].postdin = shortfastmap[station].din;
	    }
	  walk[i].poststation = station;
	}
      station = prestation;
      din = predin;
    }
}

void
print_walk (int size, const struct step_s *walk)
{
  int i;
  int lastline;

  lastline = -1;
  for ( i=0 ; i<size ; i++ )
    {
      if ( walk[i].moc < NBMISSIONS
	   && missions[walk[i].moc].line != lastline )
	{
	  if ( lastline >= 0 )
	    printf ("\n");
	  printf ("%s: %s, %s",
		  lines[missions[walk[i].moc].line],
		  stations[walk[i].prestation].name,
		  stations[walk[i].poststation].name);
	  lastline = missions[walk[i].moc].line;
	}
      else if ( walk[i].moc < NBMISSIONS )
	{
	  printf (", %s", stations[walk[i].poststation].name);
	}
    }
  printf ("\n");
}

int
score_walk (int size, const struct step_s *walk)
{
  int score;
  int i;

  score = 0;
  for ( i=0 ; i<size ; i++ )
    {
      if ( i>0 )
	{
	  assert (walk[i].prestation == walk[i-1].poststation);
	  if ( walk[i].moc < NBMISSIONS )
	    {
	      if ( walk[i].predin == walk[i-1].postdin )
		score += SAMEMISSION;
	      else
		score += OTHERMISSION;
	    }
	}
      else
	{
	  if ( walk[i].moc < NBMISSIONS )
	    score += OTHERMISSION;
	}
      assert (walk[i].predin < directions[walk[i].prestation].dirsize);
      assert (walk[i].moc == directions[walk[i].prestation].dir[walk[i].predin].moc);
      assert (walk[i].ind == directions[walk[i].prestation].dir[walk[i].predin].ind);
      assert (walk[i].moc == directions[walk[i].poststation].dir[walk[i].postdin].moc);
      if ( walk[i].moc < NBMISSIONS )
	{
	  assert (walk[i].ind < missions[walk[i].moc].misssize);
	  assert (walk[i].prestation == missions[walk[i].moc].miss[walk[i].ind].sta);
	  assert (walk[i].predin == missions[walk[i].moc].miss[walk[i].ind].din);
	  assert (walk[i].poststation == missions[walk[i].moc].miss[walk[i].ind+1].sta);
	  assert (walk[i].postdin == missions[walk[i].moc].miss[walk[i].ind+1].din);
	  assert (walk[i].ind+1 == directions[walk[i].poststation].dir[walk[i].postdin].ind);
	}
      else
	{
	  assert (walk[i].ind < connections[walk[i].moc-NBMISSIONS].connsize);
	  assert (walk[i].prestation == connections[walk[i].moc-NBMISSIONS].conn[walk[i].ind].sta);
	  assert (walk[i].predin == connections[walk[i].moc-NBMISSIONS].conn[walk[i].ind].din);
	}
    }
  return score;
}

#define WALKSIZE 1000

int walk_size;
struct step_s walk[WALKSIZE];

int
main (void)
{
  int istation, fstation;

  srandom (time (NULL));
  do
    {
#if 0
      istation = 80;
      fstation = 180;
#else
      istation = random()%NBSTATIONS; 
      fstation = random()%NBSTATIONS;
#endif
      fill_fastmap (istation);
      make_fastest_walk (fstation, WALKSIZE, walk, &walk_size);
      printf ("IStation %s (%d) - FStation %s (%d)\n",
	      stations[istation].name, istation, stations[fstation].name, fstation);
      printf ("SCORE=%d\n", shortfastmap[fstation].score);
      assert (shortfastmap[fstation].score == score_walk (walk_size, walk));
      print_walk (walk_size, walk);
#if 0
      show_fastmap ();
#endif
    }
  while ( 0 );
  return 0;
}
