/*
   File:        inout.c
   Authors:     Andrew W. Moore
   Created:     Wed Jun  5 17:31:14 EDT 2002
   Description: Input/Output Routines

   Copyright 2002, Andrew W. Moore
*/

#include "inout.h"
#include "utils.h"

char *mk_in_string(inout *io)
{
  char *result = NULL;
  if ( io->tokens == NULL ||
       io->next_toknum >= string_array_size(io->tokens) )
  {
    if ( io->tokens != NULL )
      free_string_array(io->tokens);
    io->tokens = mk_string_array_from_line(io->s);
    io->next_toknum = 0;
    if ( io->tokens == NULL )
      my_errorf("Unexpected EOF when reading token number %d from file %s",
		io->next_toknum,io->filename);
  }

  result = mk_copy_string(string_array_ref(io->tokens,io->next_toknum));
  io->next_toknum += 1;

  io->num_tokens_so_far += 1;
  if ( io->num_tokens_so_far >= io->next_report )
  {
    printf("So far read %d token%s from %s\n",
	   io->num_tokens_so_far,(io->num_tokens_so_far==1)?"":"s",
	   io->filename);
    io->next_report *= 2;
  }

  return result;
}

void out_string(FILE *s,char *string)
{
  fprintf(s,"%s\n",string);
}

string_array *mk_in_string_array_no_annotate(inout *io)
{
  string_array *sa = NULL;
  int size;
  
  io_check(io,"size");
  size = in_int(io);
  
  if ( size < 0 )
    my_error("string_array_size should be >= 0");
  else
  {
    int i;
    sa = mk_string_array(size);
    for ( i = 0 ; i < size ; i++ )
    {
      char *s = mk_in_string(io);
      string_array_set(sa,i,s);
      free_string(s);
    }
  }

  return sa;
}

string_array *mk_in_string_array(inout *io)
{
  string_array *sa;
  io_check_begin(io,"string_array");
  sa = mk_in_string_array_no_annotate(io);
  io_check_end(io,"string_array");
  return sa;
}

sa_array *mk_in_sa_array( inout *io)
{
  int size, i;
  string_array *sa;
  sa_array *saa;

  io_check_begin( io, "sa_array");

  io_check(io,"size");
  size = in_int(io);
  saa = mk_sa_array( size);

  for (i=0; i<size; ++i) {
    sa = mk_in_string_array( io);
    sa_array_set( saa, i, sa);
    free_string_array( sa);
  }

  io_check_end( io, "sa_array");
  return saa;
}

void out_string_array_no_annotate(FILE *s,string_array *sa)
{
  int size = string_array_size(sa);
  int i;
  fprintf(s,"size %d\n",size);

  for ( i = 0 ; i < size ; i++ )
    fprintf(s,"%s\n",string_array_ref(sa,i));
}

void out_string_array(FILE *s,string_array *sa)
{
  io_begin(s,"string_array");
  out_string_array_no_annotate(s,sa);
  io_end(s,"string_array");
}

void out_sa_array( FILE *s, sa_array *saa)
{
  int i, size;

  io_begin( s, "sa_array");

  size = sa_array_size( saa);
  fprintf(s,"size %d\n",size);
  for (i=0; i<size; ++i) out_string_array( s, sa_array_ref( saa, i));

  io_end( s, "sa_array");

  return;
}

dyv *mk_in_dyv(inout *io)
{
  string_array *sa;
  dyv *x;
  io_check_begin(io,"dyv");
  sa = mk_in_string_array_no_annotate(io);
  x = mk_dyv_from_string_array(sa,NULL);
  if ( x == NULL )
  {
    fprintf_string_array(stdout,"sa",sa,"\n");
    printf("When I converted this into a DYV I got a NULL\n");
  }

  free_string_array(sa);
  io_check_end(io,"dyv");
  return x;
}

ivec *mk_in_ivec(inout *io)
{
  ivec *x;
  dyv *y;
  io_check_begin(io,"ivec");
  y = mk_in_dyv(io);
  x = mk_ivec_from_dyv(y);
  free_dyv(y);
  io_check_end(io,"ivec");
  return x;
}

void out_dyv(FILE *s,dyv *x)
{
  string_array *sa = mk_string_array_from_dyv(x);
  io_begin(s,"dyv");
  out_string_array_no_annotate(s,sa);
  io_end(s,"dyv");
  free_string_array(sa);
}

void out_ivec(FILE *s,ivec *x)
{
  dyv *dv = mk_dyv_from_ivec(x);
  io_begin(s,"ivec");
  out_dyv(s,dv);
  io_end(s,"ivec");
  free_dyv(dv);
}

dyv_array *mk_in_dyv_array(inout *io)
{
  dyv_array *nu = mk_empty_dyv_array();
  int size;
  int i;

  io_check_begin(io,"dyv_array");
  io_check(io,"size");
  size = in_int(io);
  my_assert(size >= 0);

  for ( i = 0 ; i < size ; i++ )
  {
    dyv *x = mk_in_dyv(io);
    add_to_dyv_array(nu,x);
    free_dyv(x);
  }

  io_check_end(io,"dyv_array");

  return nu;
}

void out_dyv_array(FILE *s,dyv_array *a)
{
  int size = dyv_array_size(a);
  int i;
  io_begin(s,"dyv_array");
  fprintf(s,"size %d\n",size);
  for ( i = 0 ; i < size ; i++ )
    out_dyv(s,dyv_array_ref(a,i));
  io_end(s,"dyv_array");
}

ivec_array *mk_in_ivec_array(inout *io)
{
  ivec_array *nu = mk_empty_ivec_array();
  int size;
  int i;

  io_check_begin(io,"ivec_array");
  io_check(io,"size");
  size = in_int(io);
  my_assert(size >= 0);

  for ( i = 0 ; i < size ; i++ )
  {
    ivec *x = mk_in_ivec(io);
    add_to_ivec_array(nu,x);
    free_ivec(x);
  }

  io_check_end(io,"ivec_array");

  return nu;
}

void out_ivec_array(FILE *s,ivec_array *a)
{
  int size = ivec_array_size(a);
  int i;
  io_begin(s,"ivec_array");
  fprintf(s,"size %d\n",size);
  for ( i = 0 ; i < size ; i++ )
    out_ivec(s,ivec_array_ref(a,i));
  io_end(s,"ivec_array");
}

char *mk_begin_name(char *name)
{
  return mk_printf("<%s>",name);
}

char *mk_end_name(char *name)
{
  return mk_printf("</%s>",name);
}

void io_check(inout *io,char *expected_string)
{
  char *actual_string = mk_in_string(io);
  if ( !eq_string(actual_string,expected_string) )
    my_errorf("I expected token %s but read token %s",
	      expected_string,actual_string);
  free_string(actual_string);
}

void io_check_begin(inout *io,char *name)
{
  char *s = mk_begin_name(name);
  io_check(io,s);
  free_string(s);
}

void io_begin(FILE *s,char *name)
{
  char *st = mk_begin_name(name);
  fprintf(s,"%s\n",st);
  free_string(st);
}

void io_end(FILE *s,char *name)
{
  char *st = mk_end_name(name);
  fprintf(s,"%s\n",st);
  free_string(st);
}

void io_check_end(inout *io,char *name)
{
  char *s = mk_end_name(name);
  io_check(io,s);
  free_string(s);
}

int in_int(inout *io)
{
  char *s = mk_in_string(io);
  int result;
  if ( !is_a_number(s) )
    my_errorf("%s is not an integer\n",s);
  result = atoi(s);
  free_string(s);
  return result;
}

double in_double(inout *io)
{
  char *s = mk_in_string(io);
  double result;
  if ( !is_a_number(s) )
    my_errorf("%s is not a number\n",s);
  result = atof(s);
  free_string(s);
  return result;
}

bool in_bool(inout *io)
{
  char *s = mk_in_string(io);
  bool t = eq_string(s,"true");
  bool f = eq_string(s,"false");

  if ( !t && !f )
    my_errorf("not t and not f?");
  
  free_string(s);

  return t;
}

void out_bool(FILE *s,bool b)
{
  fprintf(s,"%s\n",(b) ? "true" : "false");
}

void out_int(FILE *s,int x)
{
  fprintf(s,"%d\n",x);
}

void out_double(FILE *s,double x)
{
  fprintf(s,"%g\n",x);
}

inout *mk_inout(char *filename)
{
  inout *x = AM_MALLOC(inout);

  x -> filename = mk_copy_string(filename);
  x -> s = safe_fopen(filename,"r");
  x -> tokens = NULL;
  x -> next_toknum = -77;
  x -> num_tokens_so_far = 0;
  x -> next_report = 10000;

  return x;
}

void free_inout(inout *x)
{
  free_string(x->filename);
  fclose(x->s);
  if ( x->tokens != NULL ) free_string_array(x->tokens);

  AM_FREE(x,inout);
}

void fprintf_inout(FILE *s,char *m1,inout *x,char *m2)
{
  char *buff;

  buff = mk_printf("%s -> filename",m1);
  fprintf_string(s,buff,x->filename,m2);
  free_string(buff);

  buff = mk_printf("%s -> tokens",m1);
  fprintf_string_array(s,buff,x->tokens,m2);
  free_string(buff);

  buff = mk_printf("%s -> next_toknum",m1);
  fprintf_int(s,buff,x->next_toknum,m2);
  free_string(buff);

  buff = mk_printf("%s -> num_tokens_so_far",m1);
  fprintf_int(s,buff,x->num_tokens_so_far,m2);
  free_string(buff);

  buff = mk_printf("%s -> next_report",m1);
  fprintf_int(s,buff,x->next_report,m2);
  free_string(buff);

}

void pinout(inout *x)
{
  fprintf_inout(stdout,"inout",x,"\n");
}

inout *mk_copy_inout(inout *old)
{
  my_error("You must not copy an inout");
  return NULL;
}

namer *mk_in_namer(inout *io)
{
  namer *nu;
  string_array *sa;
  io_check_begin(io,"namer");
  sa = mk_in_string_array(io);
  nu = mk_namer_from_string_array(sa,FALSE);
  io_check_end(io,"namer");
  free_string_array(sa);
  return nu;
}

void out_namer(FILE *s,namer *x)
{
  io_begin(s,"namer");

  out_string_array(s,x->index_to_name);

  io_end(s,"namer");
}

void out_dym(FILE *s,dym *x)
{
  int i,j;

  io_begin(s,"dym");
  out_int(s,dym_rows(x));
  out_int(s,dym_cols(x));

  for ( i = 0 ; i < dym_rows(x) ; i++ )
    for ( j = 0 ; j < dym_cols(x) ; j++ )
      out_double(s,dym_ref(x,i,j));

  io_end(s,"dym");
}

dym *mk_in_dym(inout *io)
{
  dym *x;
  int rows;
  int cols;
  int i,j;

  io_check_begin(io,"dym");
  rows = in_int(io);
  cols = in_int(io);

  x = mk_dym(rows,cols);

  for ( i = 0 ; i < rows ; i++ )
    for ( j = 0 ; j < cols ; j++ )
      dym_set(x,i,j,in_double(io));

  io_check_end(io,"dym");

  return x;
}
