#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <yafl_rnt.h>
#include "dbx_rnt.h"
#include "dbx_out.h"
#include "dbx_io.h"
#include "dbx_stri.h"

static char stryes[] = "YES";
static char strno[] = "NO";

static char strtrue[] = "TRUE";
static char strfalse[] = "FALSE";    

#define MAX_CHARS 1000
static char buffer[MAX_CHARS+10];

/* send machine-dependent codes for setting any highlight mode */
#ifdef UNIX
void highlight_on YPARAMS0
{
  if (!envi_get_no_highlight())
  {
    sprintf(buffer,"%c[%dm",27,7);
    send_string(buffer);
  }
}
#else
void highlight_on YPARAMS0
{
}
#endif

/* send machine-dependent code for cancelling highlight mode previously set */
#ifdef UNIX
void highlight_off YPARAMS0
{
  if (!envi_get_no_highlight())
  {
    sprintf(buffer,"%c[%dm",27,0);
    send_string(buffer);
  }
}
#else
void highlight_off YPARAMS0
{
}
#endif

/* this module controls formatted output of the command-line interface */
/* most of the informations displayed by the debugger are presented in */
/* tables;these functions permit to the user to define table formats */
/* and ensure that the strings then written to fill the elements are */
/* correctly printed (with report to the next lines if they are too long)*/
/* as necessary to keep the screen clean on a system without windowing */
/* it is also possible to define subtables wich are simply items vertically */
/* aligned in one colum without having to use different lines,as used for */
/* the display of the help command */
#define MAX_COLUMNS 20
/* one character less than the actual size, to prevent unwanted wrapping */
#define RELATIVE_MAX_WIDTH 77
#define MAX_WIDTH 77
#define OPEN_TABLE 1
#define CLOSED_TABLE 0
#define AUTO_TAB 1
#define MANUAL_TAB 0
#define OPEN_SUBTABLE 1
#define CLOSED_SUBTABLE 0

static unsigned relative_width_table[MAX_COLUMNS];
static unsigned width_table[MAX_COLUMNS];
static char *string_table[MAX_COLUMNS];
static unsigned used_columns; /* number of columns in the current table */
static unsigned relative_total_width;
static unsigned total_width; /* sum of the all the columns widths */
static unsigned max_width; 
static unsigned dbx_index; /* current column number in the table */
static int table_state; /* open or closed */
static int table_mode; /* automatic or manual tabulation */
static int subtable_state; /* open or closed */

/* prepares system for printing data in tables;the call to open should be */
/* followed by the definition of the columns */
void open_table YPARAMS0
{ 
  assert(table_state == CLOSED_TABLE);
  table_state = OPEN_TABLE;
  subtable_state = CLOSED_SUBTABLE;
  table_mode = AUTO_TAB;
  used_columns = 0;
  relative_total_width = 0;
  dbx_index = 0;
}

void compute_table YPARAMS0
{
  int i;
  total_width = max_width * relative_total_width / RELATIVE_MAX_WIDTH;
  for (i = 0; i < used_columns; i++)
  {
    width_table[i] = max_width * relative_width_table[i] / RELATIVE_MAX_WIDTH; 
  }
}

/* return to normal operation */
void close_table YPARAMS0
{
  int i;
  assert(table_state == OPEN_TABLE);
  for (i = 0; i < used_columns; i++)
  {
    free(string_table[i]);
    string_table[i] = NULL;
  }
  table_state = CLOSED_TABLE;
}

void dmp YPARAMS0
{
  int i;
  emit_stdout("------------------------------\n");
  for (i = 0;i < used_columns;i++)
  {
    emit_stdout(string_table[i]);
    emit_nl();
  }  
  emit_stdout("------------------------------\n");
}

/* print a line of the table when all the columns have been filled */
/* cutting the strings in pieces as necessary to make them fit in */
/* the column where they have been put */
void dump_table YPARAMS0
{ 
  int i,done;
  unsigned l1,l2;
  char *s,*t;
  assert (dbx_index == used_columns);
  do
  {
    done = 1;
    for (i = 0;i < used_columns;i++)
    {        
      l1 = strlen(string_table[i]);
      l2 = width_table[i];
      if (l2 == 0) continue;
      if (l1 > 0)
      {
        if (l1 > l2)
        {
          done = 0;
          s = strleft(string_table[i],l2);
          t = strright(string_table[i],l1-l2);
          free(string_table[i]);
          string_table[i] = t;
          send_string(s);
          free(s);
        }
        else /* l1 <= l2 */
        {
          s = strblank(l2-l1);
          send_string(string_table[i]);
          send_string(s);
          free(string_table[i]);
          string_table[i] = strnew();
          free(s); 
        }
      }
      else /* l1 = 0 */
      {
        s = strblank(l2);
        send_string(s);
        free(s);
      }
    }  
    s = strblank(max_width - total_width); 
    send_string(s);
    free(s);
/*    if (envi_get_force_cr()) */
      send_string("\n"); /* ??? */
  } while (! done);
  dbx_index = 0;
}

/* add a column of the given width to the table */
void set_column YPARAMS2 (unsigned, relative_width)
{
  assert(used_columns < MAX_COLUMNS);
  assert(relative_total_width + relative_width <= RELATIVE_MAX_WIDTH);
  relative_width_table[used_columns] = relative_width;
  string_table[used_columns] = strnew();
  used_columns++;  
  relative_total_width += relative_width;
}

/* put a string in the current column and jump to the next column */
/* if in automatic tabulation mode */
static void set_string YPARAMS2 (char *,  s)
{          
  char *t;
  assertp(s);
  if (dbx_index < used_columns)
  {
    t = strjoin(string_table[dbx_index],s);
    free(string_table[dbx_index]);  
    string_table[dbx_index] = t;
    if (table_mode == AUTO_TAB)
    {
      dbx_index++;    
    }
  }
  else
  {
    /* assert(0); */
  }  
}

/* print as many padding characters as necessary to complete the line in */
/* the current column so that next write will begin on a different line */
void next_item YPARAMS0 
{
  unsigned l,l1,l2;
  char *s;
  assert(dbx_index < used_columns);
  l1 = strlen(string_table[dbx_index]);
  l2 = width_table[dbx_index];
  l = l1 % l2;
  s = strblank(l2-l);
  set_string(s);
  free(s); 
}

/* the default mode of the table is automatic tabulation;each call to a */
/* write function causes writing in a different column;it is possible to */
/* temporarily stop this mode so that a same column will be filled by */
/* successive writes,so the user doesn't have to concatenate strings */
/* himself */
void stop_tab YPARAMS0
{
  assert(table_mode == AUTO_TAB);
  table_mode = MANUAL_TAB;
}
             
/* restore automatic tabulation mode and jump to next column */
void restart_tab YPARAMS0
{
  assert(table_mode == MANUAL_TAB);
  table_mode = AUTO_TAB;
  dbx_index++;
}

/* the subtable mode permits to print many items in the same column and */
/* line of the table;in effect each write is automatically followed by */
/* a write of blank characters to ensure that the next write will begin */
/* on the next line of the screen;the subtable mode operates by stopping */
/* tabulation;so we cannot use the stop and restart tabulation functions */
/* ourselves when in subtable mode */
void open_subtable YPARAMS0
{
  assert(table_state == OPEN_TABLE);
  assert(subtable_state == CLOSED_SUBTABLE);
  subtable_state = OPEN_SUBTABLE;
  stop_tab();
}

/* return to normal table operation */
void close_subtable YPARAMS0
{
  assert(table_state == OPEN_TABLE);
  assert(subtable_state == OPEN_SUBTABLE);
  subtable_state = CLOSED_SUBTABLE;
  restart_tab();
}
                                
/* when using table mode the writing must be delayed until an entire line */
/* of the table has been filled because if strings are too long to fit in */
/* their column they will be printed on successive lines */
static void put_string YPARAMS0
{
  if (table_state == OPEN_TABLE)
  {
    set_string(strclone(buffer));
    if (subtable_state == OPEN_SUBTABLE)
    {
      next_item();
    }
  }
  else /* table_state == CLOSED_TABLE */
  {                      
    send_string(buffer);
  }
}


/* converts various data types into strings which can be processed by */
/* formatting functions */
void writereal YPARAMS2(double,      d)
{
  sprintf(buffer,"%lf",d);
  put_string();
}

void writestring YPARAMS2(char *,   s)
{ 
  sprintf(buffer,"%s",s);
  put_string();
}

void writeint YPARAMS2(int,      i)
{
  sprintf(buffer,"%d",i);
  put_string();
}

void writehex YPARAMS2(unsigned,     u)
{
  sprintf(buffer,"%x",u);
  put_string();
}

void writechar YPARAMS2(char,      c)
{
  sprintf(buffer,"%c",c);
  put_string();
}

void writebool YPARAMS2(int,      i)
{
  if (i)
    writestring(strtrue);
  else
    writestring(strfalse);
}

void writeyn YPARAMS2(int,      i)
{
  if (i)
    writestring(stryes);
  else
    writestring(strno);
}


/* in table mode a call to writeln signals the passage to the next line of */
/* the table ;it will cause the printing of the current line */  		      		       		       
void writeln YPARAMS0
{
  if (table_state == CLOSED_TABLE)
  {
    sprintf(buffer,"\n");
    send_string(buffer);
  }
  else /* table_state == OPEN_TABLE */
  {
    dump_table();
  }
}

void writeline YPARAMS2(char *,  s)
{
  writestring(s);
  writeln();
}

int readstring YPARAMS2(char *,  s)
{
  char *t;
  t = receive_string();
  if (t)
  {
    sprintf(s,"%s",t);
    return 1;
  }
  else
    return 0;
}

void init_dbx_out YPARAMS0
{
  int i;
  char *p;
  p = getenv("YWIDTH");
  if (p)
  {
    max_width = atoi(p);
  }
  else
  {
    max_width = MAX_WIDTH;
  }
  for (i = 0;i < MAX_COLUMNS;i++)
  {
    string_table[i] = NULL;
  }
}


void term_dbx_out YPARAMS0
{
}

