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

#include "internalfile.h"
#include "internal.h"

char *_imp_current_file = "";
int   _imp_current_line = 0;

char *_imp_Progname = "<imp main program>"; // get from argv[0]

int _imp_global_argc = 0;
char **_imp_global_argv = NULL;

int     _imp_InStream = 0,  _imp_OutStream = 0;
// Need to globally replace _imp_INFILE with _imp_InFile (& ditto OUTFILE)
FILE    *_imp_INFILE,       *_imp_OUTFILE;

_imp_filedata _imp_infile[256];
_imp_filedata _imp_outfile[256];

_imp_string   _imp_promptstr;

void _imp_init_files(void) {
  int i;

  // inpos, outpos, lastchar and nextchar will require a lot of work
  // to implement properly.  Current code is much of a placeholder.
  
  for (i = 0; i < 256; i++) {
    _imp_infile[i].f = NULL;
    _imp_infile[i].fname = _imp_str_literal("<null>");
    _imp_infile[i].streamno = i;
    _imp_infile[i].lastchar = '\n';
    _imp_infile[i].nextchar = -1;
    _imp_infile[i].inpos = 0;
    _imp_outfile[i].f = NULL;
    _imp_outfile[i].fname = _imp_str_literal("<null>");
    _imp_outfile[i].streamno = i;
    _imp_outfile[i].lastchar = '\n';
    _imp_outfile[i].nextchar = -1;
    _imp_outfile[i].outpos = 0;
  }
  _imp_infile[0].f = fopen("/dev/tty", "r");
  _imp_infile[0].fname = _imp_str_literal("<console>");

  _imp_outfile[0].f = fopen("/dev/tty", "w"); // or could use stderr
  _imp_outfile[0].fname = _imp_str_literal("<console>");

  _imp_infile[0].inpos = 0;

  _imp_infile[1].f = stdin;
  _imp_infile[1].fname = _imp_str_literal("<stdin>");

  _imp_outfile[1].f = stdout;
  _imp_outfile[1].fname = _imp_str_literal("<stdout>");

  _imp_outfile[1].outpos = 0;

  // Default currently set to stdin and stdout.
  // This is not the same as selctinput(0) and selectoutput(0) which is /dev/tty
  // which are needed for interactive prompting.  Do an explicit selectinput(0)
  // in your code if it is meant to be an interactive program.

  _imp_INFILE  = _imp_infile[_imp_InStream=1].f;
  _imp_OUTFILE = _imp_outfile[_imp_OutStream=1].f;

  _imp_promptstr = _imp_str_literal("Data: ");
}

void _imp_initialise(int argc, char **argv) {
  _imp_Progname = argv[0];
  _imp_global_argc = argc;
  _imp_global_argv = argv;
  //restart_under_valgrind_if_needed(argc, argv);
  _imp_init_files(); // Initialise Imp I/O streams.
}

int _imp_in_tty(void) {  // Determine if input stream is interactive
  return isatty(fileno(_imp_INFILE));
}

int _imp_out_tty(void) {  // Determine if output stream is interactive
  return isatty(fileno(_imp_OUTFILE));
}

void _imp_issue_prompt(void) {

  // Unfortunately for prompts to work properly, every I/O operation has to
  // keep track of 'lastchar' (and incidentally, also 'outpos')
  // So doing that at the imp library level is not idea, but it'll
  // have to do for now.

  // Note prompting does not work if selected streams are both stream 1,
  // even if that does by coincidence happen to be the console.  At least for now.
  // If I do check the interactivity of stdin and stdout, I should only do it
  // at the point of defining the stream, with a lightweight check at the
  // point of selecting the stream, and a trivial check of that result at
  // the point of outputting a prompt!

  // However, linux has mechanisms for testing whether a file is an interactive
  // stream, so I intend to use that to make prompts more general.
  
  // if (_imp_OutStream == 0
  //     && _imp_outfile[0].lastchar == '\n'
  //     && _imp_InStream == 0
  //     && _imp_infile[0].lastchar == '\n') {

  if (_imp_in_tty() &&
      _imp_out_tty() &&
      _imp_infile[_imp_InStream].lastchar == '\n' &&
      _imp_outfile[_imp_OutStream].outpos == 0) {
    _imp_printstring(_imp_promptstr); fflush(_imp_OUTFILE);

    if (*_imp_length(&_imp_promptstr) > 0) {
      _imp_outfile[_imp_OutStream].lastchar
        = *_imp_charno(&_imp_promptstr, *_imp_length(&_imp_promptstr)-1);
    }
  }
}

void check_instream(const char *fn) {
  if (   _imp_InStream < 0
      || _imp_InStream >= 256
      || _imp_INFILE == NULL
      || _imp_infile[_imp_InStream].f == NULL) {
    fprintf(stderr, "Invalid INSTREAM(%d)", _imp_InStream);
    if ((0 <= _imp_InStream) && (_imp_InStream <= 255)) fprintf(stderr, "=%p",  _imp_infile[_imp_InStream].f);
    fprintf(stderr, " was accessed by %s from %s:%d\n", fn, _imp_current_file, _imp_current_line);
    exit(1); // TO DO: signal.
  }
}

void check_outstream(const char *fn) {
  if (   _imp_OutStream < 0
      || _imp_OutStream >= 256
      || _imp_OUTFILE == NULL
      || _imp_outfile[_imp_OutStream].f == NULL) {
    fprintf(stderr, "Invalid OUTSTREAM(%d)", _imp_OutStream);
    if ((0 <= _imp_OutStream) && (_imp_OutStream <= 255)) fprintf(stderr, "=%p",  _imp_outfile[_imp_OutStream].f);
    fprintf(stderr, " was accessed by %s from %s:%d\n", fn, _imp_current_file, _imp_current_line);
    exit(1); // TO DO: signal.
  }
}
