#include <zebra.h>

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

#include "random.c"

#include "thread.h"
#include "vty.h"
#include "log.h"
#include "linklist.h"

#include "spgrid.h"


#define DASH '-'
#define VERY_FAR 100000000

#define DOUBLE_CYCLE   0
#define CYCLE          1
#define PATH           2

#define NO             0
#define YES            1

#define NODE( x, y ) (x*Y + y + 1)

/*
 * Prototypes.
 */
void free_arc(void *);
void help(struct vty *);
void print_arc(struct vty *, struct list *, long, long, long);
void hhelp(struct vty *);
void usage(struct vty *);

const char   *graph_type[] =  {
  "double cycle",
  "cycle",
  "path"
};

struct arc *arc;

char   args[30];

long   X,   /* horizontal size of grid */
       Y;   /* vertical size of grid */

long   x,
       y,
       yy1, yy2, yyp,
       dl, dx, xn, yyn, count,
       *mess;

double n;
long   n0,
       source,
       i,
       i0,
       j,
       dij;

double m;
long   m0,
       mc,
       k;

long   *p,
       p_t,
       l,
       lx;

long   seed,
       seed1,
       seed2;

int    ext=0;

/* initialized by default values */

/* variables for generating one layer */

/* variables for generating spanning graph */
int    c_f = 0, cw_f = 0, cm_f = 0, cl_f = 0;

int    cw = DOUBLE_CYCLE;  /* type of spanning graph */
long   cm = 0,             /* lower bound of the interval */
       cl = 100;           /* upper bound of the interval */

/* variables for generating additional arcs */
int    a_f = 0, ax_f = 0, am_f = 0, al_f = 0;

long   ax = 0,             /* number of additional arcs */
       am = 0,             /* lower bound of the interval */
       al = 100;           /* upper bound of the interval */

/* variables for inter-layer arcs */
int    i_f = 0, ip_f = 0, ix_f = 0, ih_f = 0,
       im_f = 0, il_f = 0, in_f = 0, is_f = 0;

int    ip = NO;       /* to mess or not to mess */
long   ix = 1,        /* number of interlayered arcs in a NODE */
       ih = 1,        /* step between two layeres */
       il = 10000,    /* upper bound of the interval */
       im = 1000;     /* lower bound of the interval */
double in = 1,        /* l *=  in * |x1-x2| */
       is = 0;        /* l *=  is * |x1-x2|^2 */

/* variables for artifical source */
int    s_f = 0, sl_f = 0, sm_f = 0;
long   sl   = VERY_FAR, /* upper bound of artifical arc */
       sm,              /* lower bound of artifical arc */
       s;

/* variables for potentials */
int    p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0;

long   pl,            /* upper bound of the interval */
       pm;            /* lower bound of the interval */
double pn = 0,        /* p +=  ln * (x+1) */
       ps = 0;        /* p +=  ls * (x+1)^2 */

int np;               /* number of parameter parsing now */


void
free_arc   (void *val) {
  free(val);
}

void
print_arc (struct vty *vty, struct list *topology, long i, long j, long length)
{
  struct arc *myarc;

  l = length;
  if ( p_f ) l += ( p[i] - p[j] );
//  vty_out (vty,"a %8ld %8ld %12ld%s", i, j, l ,VTY_NEWLINE);
  myarc = malloc (sizeof(struct arc));
  myarc->from_node = i;
  myarc->to_node = j;
  myarc->distance = l;
  topology->del = free_arc;
  listnode_add (topology, myarc);
}

/* ---- help ---- */
void
help (struct vty *vty) {
//  if ( args[2] == 'h') hhelp (vty);
  vty_out (vty,"grid network generator for shortest paths problem.%s",VTY_NEWLINE);
  vty_out (vty,"Generates problems in extended DIMACS format.%s",VTY_NEWLINE);
  vty_out (vty,"X Y seed [ -cl#i -cm#i -c{c|d|p} -ip -il#i -im#i -p -pl#i -pm#i... ]%s",VTY_NEWLINE);
  vty_out (vty,"#i - integer number%s",VTY_NEWLINE);
  vty_out (vty,"-cl#i - #i is the upper bound on layer arc lengths    (default 100)%s",VTY_NEWLINE);
  vty_out (vty,"-cm#i - #i is the lower bound on layer arc lengths    (default 0)%s",VTY_NEWLINE);
  vty_out (vty,"-c#t  - #t is the type of connecting graph: { c | d | p }%s",VTY_NEWLINE);
  vty_out (vty,"           c - cycle, d - double cycle, p - path      (default d)%s",VTY_NEWLINE);
  vty_out (vty,"-ip   - shuffle inter-layer arcs                     (default NO)%s",VTY_NEWLINE);
  vty_out (vty,"-il#i - #i is the upper bound on inter-layer arc lengths (default 10000)%s",VTY_NEWLINE);
  vty_out (vty,"-im#i - #i is the lower bound on inter-layer arc lengths (default 1000)%s",VTY_NEWLINE);
  vty_out (vty,"-p    - generate potentials%s",VTY_NEWLINE);
  vty_out (vty,"-pl#i - #i is the upper bound on potentials           (default il)%s",VTY_NEWLINE);
  vty_out (vty,"-pm#i - #i is the lower bound on potentials           (default im)%s",VTY_NEWLINE);
  vty_out (vty,"%s",VTY_NEWLINE);
  vty_out (vty,"-hh    - extended help%s",VTY_NEWLINE);
}

/* --------- sophisticated help ------------ */
void
hhelp (struct vty *vty) {
/*
zlog_info (
"\n'%s' - grid network generator for shortest paths problem.\n\
Generates problems in extended DIMACS format.\n\
\n\
   %s  X Y seed [ -cl#i -cm#i -c{c|d|p}\n\
                      -ax#i -al#i -am#i\n\
                      -ip   -il#i -im#i -in#i -is#i -ix#i -ih#i\n\
                      -p    -pl#i -pm#i -pn#f -ps#f\n\
                      -s    -sl#i -sm#i\n\
                    ]\n\
   %s -hh file_name\n\
\n\
                        #i - integer number   #f - real number\n\
\n\
      Parameters of connecting arcs within one layer:\n\
-cl#i - #i is the upper bound on arc lengths          (default 100)\n\
-cm#i - #i is the lower bound on arc lengths          (default 0)\n\
-c#t  - #t is the type of connecting graph: { c | d | p }\n\
           c - cycle, d - double cycle, p - path      (default d)\n\
\n\
      Parameters of additional arcs within one layer:\n\
-ax#i - #i is the number of additional arcs           (default 0)\n\
-al#i - #i is the upper bound on arc lengths          (default 100)\n\
-am#i - #i is the lower bound on arc lengths          (default 0)\n\
\n\
      Interlayerd arc parameters:\n\
-ip    - shuffle inter-layer arcs                         (default NO)\n\
-il#i  - #i is the upper bound on arc lengths          (default 10000)\n\
-im#i  - #i is the lower bound on arc lengths          (default 1000)\n\
-in#f  - multiply l(i, j) by #f * x(j)-x(i)           (default 1)\n\
         if #f=0 - don't multiply\n\
-is#f  - multiply l(i, j) by #f * (x(j)-x(i))^2       (default NO)\n\
-ix#i  - #i - is the number of arcs from a node        (default 1)\n\
-ih#i  - #i - is the step between connected layers     (default 1)\n\
\n\
      Potential parameters:\n\
-p     - generate potentials \n\
-pl#i  - #i is the upper bound on potentials           (default ll)\n\
-pm#i  - #i is the lower bound on potentials           (default lm)\n\
-pn#f  - multiply p(i) by #f * x(i)                    (default NO)\n\
-ps#f  - multiply p(i) by #f * x(i)^2                  (default NO)\n\
\n");
zlog_info (
"     Artificial source parameters:\n\
-s     - generate artificial source with default connecting arc lengths\n\
-sl#i  - #i is the upper bound on art. arc lengths    (default 100000000)\n\
-sm#i  - #i is the lower bound on art. arc lengths    (default sl)\n\"
);*/
}

/* ----- wrong usage ----- */
void
usage (struct vty *vty) {
  vty_out (vty,"usage: X Y seed [-ll#i -lm#i -cl#i -p -pl#i -pm#i ...]%s",VTY_NEWLINE);
  vty_out (vty,"help: -h or -hh%s",VTY_NEWLINE);

  if ( np > 0 )
    zlog_err ("error in parameter # %d\n\n", np );
}


/* parsing  parameters */
/* checks the validity of incoming parameters */
int
spgrid_check_params ( struct vty *vty, int argc, const char **argv)
{
/* initialized by default values */
  ext=0;

/* variables for generating one layer */

/* variables for generating spanning graph */
  c_f = 0;
  cw_f = 0;
  cm_f = 0;
  cl_f = 0;

  cw = PATH;  /* type of spanning graph */
  cm = 0;             /* lower bound of the interval */
  cl = 63;           /* upper bound of the interval */

/* variables for generating additional arcs */
  a_f = 0;
  ax_f = 0;
  am_f = 0;
  al_f = 0;

  ax = 0;             /* number of additional arcs */
  am = 0;             /* lower bound of the interval */
  al = 63;           /* upper bound of the interval */

/* variables for inter-layer arcs */
  i_f = 0;
  ip_f = 0;
  ix_f = 0;
  ih_f = 0;
  im_f = 0;
  il_f = 0;
  in_f = 0;
  is_f = 0;

  ip = NO;       /* to mess or not to mess */
  ix = 1;        /* number of interlayered arcs in a NODE */
  ih = 1;        /* step between two layeres */
  il = 63; //was 10000;    /* upper bound of the interval */
  im = 0;  //was 1000;     /* lower bound of the interval */
  in = 1;        /* l *=  in * |x1-x2| */
  is = 0;        /* l *=  is * |x1-x2|^2 */

/* variables for artifical source */
  s_f = 0;
  sl_f = 0;
  sm_f = 0;
  sl   = VERY_FAR; /* upper bound of artifical arc */

/* variables for potentials */
  p_f = 0;
  pl_f = 0;
  pm_f = 0;
  pn_f = 0;
  ps_f = 0;

  pn = 0;        /* p +=  ln * (x+1) */
  ps = 0;        /* p +=  ls * (x+1)^2 */


  if ( argc < 1 ) {
    usage (vty);
    return 1;
  }

  np = 0;

  strcpy ( args, argv[0] );

  if ((args[0] == DASH) && (args[1] == 'h'))
    help (vty);

  if ( argc < 3 ) {
    usage (vty);
    return 1;
  }

  /* first parameter - horizontal size */
  np = 1;
  if ( ( X = atoi ( argv[0] ) )  <  1  ) {
    usage (vty);
    return 1;
  }

  /* second parameter - vertical size */
  np = 2;
  if ( ( Y = atoi ( argv[1] ) )  <  1  ) {
    usage (vty);
    return 1;
  }

  /* third parameter - seed */
  np=3;
  if ( ( seed = atoi ( argv[2] ) )  <=  0  ) {
    usage (vty);
    return 1;
  }

  /* other parameters */
  for ( np = 3; np < argc; np ++ ) {
    strcpy ( args, argv[np] );
    if ( args[0] != DASH )  {
      usage (vty);
      return 1;
    }

    switch ( args[1] ) {
      case 'c' : /* spanning graph in one layer */
        c_f = 1;
        switch ( args[2] ) {
          case 'l': /* upper bound of the interval */
            cl_f = 1;
            cl  =  atol ( &args[3] );
            break;
          case 'm': /* lower bound */
            cm_f = 1;
            cm  = atol ( &args[3] );
            break;
          case 'c': /* type - cycle */
            cw_f = 1;
            cw   = CYCLE;
            break;
          case 'd': /* type - double cycle */
            cw_f = 1;
            cw   = DOUBLE_CYCLE;
            break;
          case 'p': /* type - path */
            cw_f = 1;
            cw   = PATH;
            break;

          default:  /* unknown switch  value */
            usage (vty);
            return 1;
          }
        break;

      case 'a' : /* additional arcs in one layer */
         a_f = 1;
        switch ( args[2] )
          {
          case 'l': /* upper bound of the interval */
            al_f = 1;
            al  =  atol ( &args[3] );
            break;
          case 'm': /* lower bound */
            am_f = 1;
            am  = atol ( &args[3] );
            break;
          case 'x': /* number of additional arcs */
            ax_f = 1;
            ax   = atol ( &args[3] );
            if ( ax < 0 )
             {
               usage (vty);
               return 1;
             }
            break;

          default:  /* unknown switch  value */
            {
              usage (vty);
              return 1;
            }
          }
        break;


      case 'i' : /* interlayered arcs */
        i_f = 1;

        switch ( args[2] )
          {
          case 'l': /* upper bound */
            il_f = 1;
            il  =  atol ( &args[3] );
            break;
          case 'm': /* lower bound */
            im_f = 1;
            im  = atol ( &args[3] );
            break;
          case 'n': /* additional length: l *= in*|i1-i2| */
            in_f = 1;
            in  = atof ( &args[3] );
            break;
          case 's': /* additional length: l *= is*|i1-i2|^2 */
            is_f = 1;
            is  = atof ( &args[3] );
            break;
          case 'p': /* mess interlayered arcs */
            ip_f = 1;
            ip = YES;
            break;
          case 'x': /* number of interlayered arcs */
            ix_f = 1;
            ix  = atof ( &args[3] );
            if ( ix < 1 ) {
              usage (vty);
              return 1;
            }
            break;
          case 'h': /* step between two layeres */
            ih_f = 1;
            ih  = atof ( &args[3] );
            if ( ih < 1 ) {
               usage (vty);
               return 1;
             }
            break;
          default:  /* unknown switch  value */
            usage (vty);
            return 1;
          }
        break;

      case 's' : /* additional source */
        s_f = 1;
        if ( strlen ( args ) > 2 )
        {
        switch ( args[2] )
          {
          case 'l': /* upper bound of art. arc */
            sl_f = 1;
            sl  =  atol ( &args[3] );
            break;
          case 'm': /* lower bound of art. arc */
            sm_f = 1;
            sm  =  atol ( &args[3] );
            break;
          default:  /* unknown switch  value */
            usage (vty);
            return 1;
          }
         }
        break;

      case 'p' : /* potentials */
        p_f = 1;
        if ( strlen ( args ) > 2 )
        {
        switch ( args[2] )
          {
          case 'l': /* upper bound */
            pl_f = 1;
            pl  =  atol ( &args[3] );
            break;
          case 'm': /* lower bound */
            pm_f = 1;
            pm  = atol ( &args[3] );
            break;
          case 'n': /* additional: p *= pn*(x+1) */
            pn_f = 1;
            pn  = atof ( &args[3] );
            break;
          case 's': /* additional: p = ps* (x+1)^2 */
            ps_f = 1;
            ps  = atof ( &args[3] );
            break;
          default:  /* unknown switch  value */
            usage (vty);
            return 1;
          }
        }
        break;

      default: /* unknoun case */
        usage (vty);
        return 1;
      }
  }


  return 0;
}


/* generator of layered networks for the shortest paths problem;
   extended DIMACS format for output */
int
gen_spgrid_topology (struct vty *vty, struct list *topology)
{
  /* ----- ajusting parameters ----- */

  /* spanning */
  if ( cl < cm ) { lx = cl; cl = cm; cm = lx; }

  /* additional arcs */
  if ( al < am ) { lx = al; al = am; am = lx; }

  /* interlayered arcs */
  if ( il < im ) { lx = il; il = im; im = lx; }

  /* potential parameters */
  if ( p_f )
    {
     if ( ! pl_f ) pl = il;
     if ( ! pm_f ) pm = im;
     if ( pl < pm ) { lx = pl; pl = pm; pm = lx; }
    }

  /* number of nodes and arcs */

  n = (double)X *(double)Y + 1;

  m  = (double)Y; /* arcs from source */

  switch ( cw )
  {
   case PATH:
    mc = (double)Y - 1;
    break;
   case CYCLE:
    mc = (double)Y;
    break;
   case DOUBLE_CYCLE:
    mc = 2*(double)Y;
  }

  m += (double)X * (double)mc;  /* spanning arcs */
  m += (double)X * (double)ax;  /* additional arcs */

  /* interlayered arcs */
  for ( x = 0; x < X; x ++ )
  {
    dl = ( ( X - x - 1 ) + ( ih - 1 ) ) / ih;
    if ( dl > ix ) dl = ix;
    m += (double)Y * (double)dl;
  }

   /* artifical source parameters */
  if ( s_f ) {
    m += n; n ++ ;
    if ( ! sm_f ) sm = sl;
    if ( sl < sm ) { lx = sl; sl = sm; sm = lx; }
  }

  if ( n >= (double)LONG_MAX || m >= (double)LONG_MAX )
  {
    zlog_err ("Too large problem. It can't be generated\n");
    exit (4);
  }
   else
  {
    n0 = (long)n; m0 = (long)m;
  }

  if ( ip_f )
     mess = (long*) calloc ( Y, sizeof ( long ) );

  /* printing title */
  zlog_info ("Generating topology for ISIS");

  source = ( s_f ) ? n0-1 : n0;

  if ( p_f ) /* generating potentials */ {
    p = (long*) calloc ( n0+1, sizeof (long) );
    seed1 = 2*seed + 1;
    init_rand ( seed1);
    pl = pl - pm + 1;

    for ( x = 0; x < X; x ++ )
      for ( y = 0; y < Y; y ++ ) {
        p_t = pm + nrand ( pl );
        if ( pn_f ) p_t *= (long) ( (1 + x) * pn );
        if ( ps_f ) p_t *= (long) ( (1 + x) * ( (1 + x) * ps ));

        p[ NODE ( x, y ) ] = p_t;
      }
      p[n0] = 0;
      if ( s_f ) p[n0-1] = 0;
    }

  if ( s_f ) /* additional arcs from artifical source */
    {
      seed2 = 3*seed + 1;
      init_rand ( seed2 );
      sl = sl - sm + 1;

      for ( x = X - 1; x >= 0; x -- )
        for ( y = Y - 1; y >= 0; y -- )
        {
          i = NODE ( x, y );
          s = sm + nrand ( sl );
          print_arc (vty, topology,  n0, i, s );
        }

      print_arc (vty, topology,  n0, n0-1, 0 );
    }


  /* ----- generating arcs within layers ----- */

  init_rand ( seed );
  cl = cl - cm + 1;
  al = al - am + 1;

  for ( x = 0; x < X; x ++ )
   {
  /* generating arcs within one layer */
    for ( y = 0; y < Y-1; y ++ )
    {
       /* generating spanning graph */
       i = NODE ( x, y );
       j = NODE ( x, y+1 );
       l = cm + nrand ( cl );
       print_arc (vty, topology,  i, j, l );

       if ( cw == DOUBLE_CYCLE )
         {
           l = cm + nrand ( cl );
           print_arc (vty, topology,  j, i, l );
         }
     }

    if ( cw <= CYCLE )
      {
        i = NODE ( x, Y-1 );
        j = NODE ( x, 0 );
        l = cm + nrand ( cl );
        print_arc (vty, topology,  i, j, l );

        if ( cw == DOUBLE_CYCLE )
          {
  	  l = cm + nrand ( cl );
            print_arc (vty, topology,  j, i, l );
          }
       }

  /* generating additional arcs */

    for ( k = ax; k > 0; k -- )
       {
         yy1 = nrand ( Y );
         do
            yy2 = nrand ( Y );
         while ( yy2 == yy1 );
         i  = NODE ( x, yy1 );
         j  = NODE ( x, yy2 );
         l = am + nrand ( al );
         print_arc (vty, topology,  i, j, l );
       }
   }

  /* ----- generating interlayered arcs ------ */

  il = il - im + 1;

  /* arcs from the source */

    for ( y = 0; y < Y; y ++ )
      {
        l = im + nrand ( il );
        i = NODE ( 0, y );
        print_arc (vty, topology,  source, i, l );
      }

  for ( x = 0; x < X-1; x ++ )
   {
  /* generating arcs from one layer */
     for ( count = 0, xn = x + 1;
           count < ix && xn < X;
           count ++, xn += ih )
      {
        if ( ip_f )
        for ( y = 0; y < Y; y ++ )
  	mess[y] = y;

        for ( y = 0; y < Y; y ++ )
         {
            i = NODE ( x, y );
  	  dx = xn - x;
  	  if ( ip_f )
  	    {
  	      yyp = nrand(Y-y);
  	      yyn = mess[ yyp ];
                mess[ yyp ] = mess[ Y - y - 1 ];
  	    }
  	  else
               yyn =  y;
  	  j = NODE ( xn, yyn );
  	  l = im + nrand ( il );
  	  if ( in != 0 )
              l *= (long) ( in * dx );
            if ( is_f )
              l *= (long) ( ( is * dx ) * dx );
            print_arc (vty, topology,  i, j, l );
  	}
      }
   }
  /* all is done */
  return ext;

return 0;
}



