#ifndef __VISIBLE_H__
#define __VISIBLE_H__ 1

#include <math.h>
#include <errno.h>

#include "imptypes.h"  // Note: type _imp_Name undergoing work
#include "internalfile.h"
#include "internal.h"

// The perms that IMP code calls are implemented by 'static inline' calls on code in internal.[hc]

#ifdef NOT_AT_THE_MOMENT
static inline int _imp_NL(void) {
  return '\n';
}
#endif

static inline double _imp_PI(void) {
  return 3.141592653589793238462;
}

static inline _imp_string _imp_SNL(void) {
  _imp_string SNL = {.charno = {1, '\n'} };
  return SNL;
}

// #########################################################################################

// forward references:

static inline _imp_string _imp_TOSTRING(int C);
static inline unsigned char *_imp_CHARNO(_imp_string *str, int i);
static inline unsigned char *_imp_LENGTH(_imp_string *str);

static inline double _imp_FLOAT(double N); // might be wrong. might be for converting an int to a real.

static inline _imp_address _imp_ADDR(void *P);

static inline unsigned char *_imp_BYTE(_imp_address N);
static inline unsigned char *_imp_BYTEINTEGER(_imp_address N);

static inline short *_imp_SHORT(_imp_address N);
static inline short *_imp_SHORTINTEGER(_imp_address N);

static inline int *_imp_INTEGER(_imp_address N);

static inline long long int *_imp_LONG(_imp_address N);
static inline long long int *_imp_LONGINTEGER(_imp_address N);

//static inline long long *_imp_LONGLONG(_imp_address N);
//static inline long long *_imp_LONGLONGINTEGER(_imp_address N);

static inline long long int _imp_LENGTHENI(int I);
static inline int _imp_SHORTENI(long long int I);

static inline float *_imp_REAL(_imp_address N);

static inline double *_imp_LONGREAL(_imp_address N);

static inline _imp_string *_imp_STRING(_imp_address N);

static inline void *_imp_RECORD(_imp_address N);

// Note: the VMS compiler only implemented a subset of types and the
// coding appears somewhat adhoc: /home/imp2023/VMS/imp77-vax/lib/namerts.imp

// These typeof codes will have to be kept in synch with perms.inc, where
// we find this note taken from https://gtoal.com/imp77/reference-manual/Imp77_Core_Environment/core11.html

//! This function returns a code which indicates the type of the
//! object supplied as parameter.  The complete list of code values
//! which may be returned by TYPE OF is as follows:
//!
//! 0 - unknown type
//! 1 - integer
//! 2 - real
//! 3 - string
//! 4 - record
//! 5 - byte integer
//! 6 - short integer
//! 7 - long integer
//! 8 - long real
//! 9 - array
//! 10 - label
//!
//! Note that an actual runtime call will be necessary in the case of
//! %name parameters, but for known variables, the type information can
//! be supplied by the compiler directly.  So, a prim/perm hybrid.

// Note: handling %label in C would prove very difficult.  However the sizeof/typeof calls
// can be handled in the compiler itself for known variables as an optimisation, so should
// handle %label this way even if we don't optimise other cases.  However %name variables
// and parameters are another question, but (pending conformation by testing) I don't believe
// it is possible to store a %label in a %name, whether a %name variable or a %name parameter

#define _imp_type_UNKNOWN      0

#define _imp_type_INTEGER      1
#define _imp_type_REAL         2
#define _imp_type_STRING       3
#define _imp_type_RECORD       4
#define _imp_type_BYTEINTEGER  5
#define _imp_type_SHORTINTEGER 6
#define _imp_type_LONGINTEGER  7
#define _imp_type_LONGREAL     8

#define _imp_type_ARRAY        9   // NOT YET TESTED
#define _imp_type_LABEL        10  // NOT GOING TO BE IMPLEMENTED

#define _imp_type_NAME         16

#define _imp_type_INTEGERNAME      (_imp_type_INTEGER+16)
#define _imp_type_REALNAME         (_imp_type_REAL+16)
#define _imp_type_STRINGNAME       (_imp_type_STRING+16)
#define _imp_type_RECORDNAME       (_imp_type_RECORD+16)
#define _imp_type_BYTEINTEGERNAME  (_imp_type_BYTEINTEGER+16)
#define _imp_type_SHORTINTEGERNAME (_imp_type_SHORTINTEGER+16)
#define _imp_type_LONGINTEGERNAME  (_imp_type_LONGINTEGER+16)
#define _imp_type_LONGREALNAME     (_imp_type_LONGREAL+16)


// I suspect we really want to return the type of the variable that a %NAME points to.
// which means looking inside the private _imp_Name struct that is used to contain the
// %name info, using a runtime function such as:

// extern int                      _imp_typeof(_imp_Name *N);
// extern int                      _imp_sizeof(_imp_Name *N);

// NOTE THAT THE *NAME TYPES BELOW WILL PROBABLY NOT WORK.
#define _imp_TYPEOF(var)                                                                      \
   (__builtin_types_compatible_p(typeof(*var), unsigned char) ?    _imp_type_BYTEINTEGER :      \
     (__builtin_types_compatible_p(typeof(*var), short int) ?        _imp_type_SHORTINTEGER :     \
       (__builtin_types_compatible_p(typeof(*var), int) ?              _imp_type_INTEGER :          \
         (__builtin_types_compatible_p(typeof(*var), long long int) ?    _imp_type_LONGINTEGER :      \
           (__builtin_types_compatible_p(typeof(*var), float) ?            _imp_type_REAL :             \
             (__builtin_types_compatible_p(typeof(*var), double) ?           _imp_type_LONGREAL :         \
               (__builtin_types_compatible_p(typeof(*var), _imp_string) ?      _imp_type_STRING :           \
                 (__builtin_types_compatible_p(typeof(*var), void) ?             _imp_type_NAME :             \
                   (__builtin_types_compatible_p(typeof(*var), unsigned char *) ?  _imp_type_BYTEINTEGERNAME :  \
                     (__builtin_types_compatible_p(typeof(*var), short int *) ?      _imp_type_SHORTINTEGERNAME : \
                       (__builtin_types_compatible_p(typeof(*var), int *) ?            _imp_type_INTEGERNAME :      \
                         (__builtin_types_compatible_p(typeof(*var), long long int *) ?  _imp_type_LONGINTEGERNAME :  \
                           (__builtin_types_compatible_p(typeof(*var), float *) ?          _imp_type_REALNAME :         \
                             (__builtin_types_compatible_p(typeof(*var), double *) ?         _imp_type_LONGREALNAME :     \
                               (__builtin_types_compatible_p(typeof(*var), _imp_string *) ?    _imp_type_STRINGNAME :       \
                                 (__builtin_types_compatible_p(typeof(*var), _imp_Name *) ?      _imp_type_NAME :             \
                                   (__builtin_types_compatible_p(typeof(*var), void *) ?           _imp_type_RECORDNAME :       \
                                     sizeof(*var) > 8 ? _imp_type_RECORD : _imp_type_UNKNOWN                                      \
                                   ) \
                                 ) \
                               ) \
                             ) \
                           ) \
                         ) \
                       ) \
                     ) \
                   ) \
                 ) \
               ) \
             ) \
           ) \
         ) \
       ) \
     ) \
   )

#define _imp_SIZEOF(var)                                                                                                 \
 (__builtin_types_compatible_p(typeof(*var), unsigned char)  ?   sizeof(char) :                                          \
   (__builtin_types_compatible_p(typeof(*var), short int)  ?       sizeof(short) :                                       \
     (__builtin_types_compatible_p(typeof(*var), int)  ?             sizeof(int) :                                       \
       (__builtin_types_compatible_p(typeof(*var), long long int)  ?   sizeof(long long int) :                           \
         (__builtin_types_compatible_p(typeof(*var), float)  ?           sizeof(float) :                                 \
           (__builtin_types_compatible_p(typeof(*var), double)  ?          sizeof(double) :                              \
             (__builtin_types_compatible_p(typeof(*var), unsigned char *)  ? sizeof(void *) :                            \
               (__builtin_types_compatible_p(typeof(*var), short int *)  ?     sizeof(void *) :                          \
                 (__builtin_types_compatible_p(typeof(*var), int *)  ?           sizeof(void *) :                        \
                   (__builtin_types_compatible_p(typeof(*var), long long int *)  ? sizeof(void *) :                      \
                     (__builtin_types_compatible_p(typeof(*var), float *)  ?         sizeof(void *) :                    \
                       (__builtin_types_compatible_p(typeof(*var), double *)  ?        sizeof(void *) :                  \
                         (__builtin_types_compatible_p(typeof(*var), _imp_string *)  ?   sizeof(void *) :                \
                           sizeof(*var)                                                                                  \
                         ) \
                       ) \
                     ) \
                   ) \
                 ) \
               ) \
             ) \
           ) \
         ) \
       ) \
     ) \
   ) \
 )

//                             (__builtin_types_compatible_p(typeof(*var), _imp_Name)  ?        _imp_sizeof(var) : \ ...
//                               (__builtin_types_compatible_p(typeof(*var), _imp_Name *)  ?      _imp_sizeof(*var)        \ ...
//                               ) \ ...
//                             ) \ ...


static inline double                   _imp_ARCSIN(double ANGLE);
static inline double                   _imp_ARCCOS(double ANGLE);
static inline double                   _imp_ARCTAN(double X, double Y);
static inline double                   _imp_ARCTAN1(double ANGLE);
static inline double                   _imp_SIN(double ANGLE);
static inline double                   _imp_COS(double ANGLE);
static inline double                   _imp_TAN(double ANGLE);
static inline double                   _imp_COT(double ANGLE);
static inline double                   _imp_FRACTION(double R);
static inline double                   _imp_FRACPT(double R);
static inline int                      _imp_IMOD(int I);

#ifdef NEVER

// This is why perms need to be output in C using private internal names such as _imp_imod(N) ...

// %integerfn IMOD(%integer n)
//  %result = |N|
// %end

// If a user program did the above, it would cause an infinite recusion!

#endif

/* Perms from the earliest Imp compiler (allimpc1):
SELECTINPUT SELECTOUTPUT NEWLINE SPACE SKIPSYMBOL RUNOUT NEWLINES
SPACES NEXTSYMBOL PRINTSYMBOL READSYMBOL READ WRITE NEWPAGE ADDR
ARCSIN INT INTPT FRACPT PRINT PRINTFL REAL INTEGER MOD ARCCOS SQRT
LOG SIN COS TAN EXP MAPRECORD BYTEINTEGER SHORTINTEGER RADIUS
ARCTAN PARITY PRINTSTRING NL LONGREAL PRINTCH READCH STRING

Unimplemented ones are:
runout (paper tape, can ignore!)
maprecord (probably old name for RECORD())
radius (sqrt(a^2+b^2)
parity

So... *might* want to implement RADIUS() and PARITY()???

A relatively portable library where we can get non-machine-dependent real handling
can be found in https://gtoal.com/imp77/reference-manual/Library_sources/apm_lib/
- could be the one to use for print/printfl/rtos ..?

*/

static inline double                   _imp_MOD(double R);
static inline int                      _imp_INT(double R);
static inline int                      _imp_INTPT(double R);
static inline long int                 _imp_LINT(double R);
static inline long int                 _imp_LINTPT(double R);
static inline int                      _imp_TRUNC(double R);
static inline int                      _imp_ROUND(double R);
static inline long long int            _imp_IEXP(int NUM, int POWER);
static inline double                   _imp_REXP(double NUM, double POWER);
static inline double                   _imp_EXP(double NUM);
static inline double                   _imp_LOG(double X);
static inline int                      _imp_REM(int P, int Q);
static inline double                   _imp_SQRT(double NUM);
static inline double                   _imp_RADIUS(double X, double Y);
static inline int                      _imp_ISQRT(int NUM);
static inline int                      _imp_MULDIV(int A, int B, int C);
static inline void                     _imp_PROMPT(_imp_string S);
static inline void                     _imp_READSYMBOL(int *P);
static inline void                     _imp_READCH(int *P);
static inline int                      _imp_NEXTSYMBOL(void);
static inline int                      _imp_NEXTCH(void);
static inline void                     _imp_SKIPSYMBOL(void);
static inline void                     _imp_PRINTSYMBOL(char SYM);
static inline void                     _imp_PRINTCH(char SYM);
static inline void                     _imp_PRINTSTRING(_imp_string S);

// experimenting with different ways to implement longinteger:
//static inline void                     _imp_WRITE(int V, int P);

  // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}

static inline void                     _imp_READITEM(_imp_string *S);
static inline void                     _imp_READSTRING(_imp_string *S);
static inline void                     _imp_READTEXT(_imp_string *S, int DELIM);
static inline _imp_string              _imp_NEXTITEM(void);
static inline void                     _imp_READLINE(_imp_string *S);
static inline int                      _imp_INSTREAM(void);
static inline int                      _imp_OUTSTREAM(void);
static inline int                      _imp_INPUTSTREAM(void);
static inline int                      _imp_OUTPUTSTREAM(void);
static inline _imp_string              _imp_INPUTNAME(void);
static inline _imp_string              _imp_OUTPUTNAME(void);
static inline _imp_string              _imp_INFILENAME(void);
static inline _imp_string              _imp_OUTFILENAME(void);
static inline void                     _imp_SELECTINPUT(int N);
static inline void                     _imp_SELECTOUTPUT(int N);
static inline void                     _imp_OPENINPUT(int N, _imp_string FD);
static inline void                     _imp_OPENOUTPUT(int N, _imp_string FD);
static inline void                     _imp_OPENBINARYINPUT(int N, _imp_string FD);
static inline void                     _imp_OPENBINARYOUTPUT(int N, _imp_string FD);
static inline void                     _imp_DEFINEINPUT(int I, _imp_string SPEC);
static inline void                     _imp_DEFINEOUTPUT(int I, _imp_string SPEC);
static inline void                     _imp_ABANDONINPUT(void);
static inline void                     _imp_ABANDONOUTPUT(void);
static inline void                     _imp_CLOSESTREAM(int stream);
static inline void                     _imp_CLOSEINPUT(void);
static inline void                     _imp_CLOSEOUTPUT(void);
static inline void                     _imp_RESETINPUT(void);
static inline void                     _imp_RESETOUTPUT(void);
static inline void                     _imp_COMPLETEINPUT(void);
static inline void                     _imp_COMPLETEOUTPUT(void);
static inline void                     _imp_POSITIONINPUT(int P);
static inline void                     _imp_POSITIONOUTPUT(int P);
static inline void                     _imp_SETINPUT(int P);
static inline void                     _imp_SETOUTPUT(int P);
static inline void                     _imp_INPUTPOSITION(void);
static inline void                     _imp_OUTPUTPOSITION(void);
static inline int                      _imp_ENDOFINPUT(void);
static inline int                      _imp_INPOS(void);
static inline int                      _imp_OUTPOS(void);
static inline void                     _imp_SPACE(void);
static inline void                     _imp_SPACES(int N);
static inline void                     _imp_NEWPAGE(void);
static inline void                     _imp_NEWLINE(void);
static inline void                     _imp_NEWLINES(int N);
static inline void                     _imp_PRINT(double R, int BEFORE, int AFTER);        // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
static inline void                     _imp_PRINTFLOATING(double R, int A, int B);
static inline void                     _imp_PRINTFL(double R, int PLACES);
static inline void                     _imp_PRINTFHEX(double R);
static inline _imp_string              _imp_SUBSTRING(_imp_string *S, int FROM, int TO);
static inline _imp_string              _imp_FROMSTRING(_imp_string *S, int FROM, int TO);
static inline _imp_string              _imp_TRIM(_imp_string S, int MAX);
static inline _imp_string              _imp_TIME(void);
static inline _imp_string              _imp_DATE(void);
#ifdef WANT_EMAS_STYLE
static inline float                    _imp_CPUTIME(void);  // Result is seconds
#else
static inline int                      _imp_CPUTIME(void);  // Result is milliseconds
#endif
static inline _imp_string              _imp_CLIPARAM(void);

static inline int                      _imp_EVENT_(void);
static inline int                      _imp_SUBEVENT(void);
static inline int                      _imp_EVENTINFO(void);
static inline _imp_string              _imp_EVENTMESSAGE(void);
static inline _imp_string              _imp_ITOS(int I, int P);
static inline int                      _imp_STOI(_imp_string S);
static inline _imp_string              _imp_SSFMESSAGE(void);

// #########################################################################################

// Actual procedures:

static inline int _imp_ARGC(void) {
  return _imp_global_argc;
}

static inline _imp_string _imp_ARGV(int n) {
  return _imp_c2istr(_imp_global_argv[n]);
}


static inline _imp_string _imp_TOSTRING(int C) {
  _imp_string tmp;
  *_imp_LENGTH(&tmp) = 1; *_imp_CHARNO(&tmp, 1) = (C&255);
  return tmp; // Return whole string on the stack, not just a pointer.
}

static inline unsigned char *_imp_CHARNO(_imp_string *str, int i) {
  // TO DO: check 0 <= i <= LENGTH(str)  (worry about sizeof str later)
  return &str->charno[i];
}

static inline unsigned char *_imp_LENGTH(_imp_string *str) { return &str->length; }

static inline double _imp_FLOAT(double N) {
  return N; // if FLOAT() is passed an integer, C will promote it to double.
            // %real in imp is evaluated as double everywhere except when
            // assigned to a 4-byte memory location.
}

static inline _imp_address _imp_ADDR(void *P) {
  return (_imp_address)P;
}

//%record(*)%map NEW(%name v)  *** This is a tricky one and may need some work.
//#define _imp_NEW(var) ((typeof(var))malloc(sizeof(*(var))))
#define _imp_NEW(var) malloc(_imp_SIZEOF(var))
//%routine DISPOSE(%name v)
#define _imp_DISPOSE(var) free(var)

// 8 bit
static inline unsigned char *_imp_BYTE(_imp_address N) {
  return (unsigned char *)N;
}

// 8 bit
static inline unsigned char *_imp_BYTEINTEGER(_imp_address N) {
  return (unsigned char *)N;
}

// 16 bit
static inline short int *_imp_SHORT(_imp_address N) {
  return (short int *)N;
}

// 16 bit
static inline short int *_imp_SHORTINTEGER(_imp_address N) {
  return (short int *)N;
}

// 32 bit
static inline int *_imp_INTEGER(_imp_address N) {
  // really a C 'long int' but a lot of hassle to change this everywhere
  //
  return (int *)N;
}

// 64 bit
static inline long long int *_imp_LONG(_imp_address N) {
  return (long long int *)N;
}

// 64 bit
static inline long long int *_imp_LONGINTEGER(_imp_address N) {
  return (long long int *)N;
}

// 128 bit Doesn't exist in Imp77 yet.
//static inline long long *_imp_LONGLONG(_imp_address N) {
//  return (long long *)N;
//}

// 128 bit Doesn't exist in Imp77 yet.
//static inline long long *_imp_LONGLONGINTEGER(_imp_address N) {
//  return (long long *)N;
//}

// 64 bit
static inline long long int _imp_LENGTHENI(int I) { return (long long int) I; }
static inline int _imp_SHORTENI(long long int I) { return (int) I; }

// 32 bit
static inline float *_imp_REAL(_imp_address N) {
  return (float *)N;
}

// 64 bit
static inline double *_imp_LONGREAL(_imp_address N) {
  return (double *)N;
}

// 128 bit
static inline long double *LONGLONGREAL(_imp_address N) {
  return (long double *)N;
}

static inline long double _imp_LENGTHENR(double R) { return (long double) R; }
static inline double _imp_SHORTHENR(long double R) { return (double) R; }


static inline _imp_string *_imp_STRING(_imp_address N) {
  return (_imp_string *)N;
}

static inline void *_imp_RECORD(_imp_address N) {
  return (void *)N; // Needs compiler support with casting at the point of call...
}

/*
   Most math operations have multiple forms depending on the parameter/result type:

   float powf(float x, float y);
   double pow(double x, double y);
   long double powl(long double x, long double y);
 */

static inline double                   _imp_ARCSIN(double ANGLE) { return asin(ANGLE); }
static inline double                   _imp_ARCCOS(double ANGLE) { return acos(ANGLE); }
static inline double                   _imp_ARCTAN(double X, double Y) { return atan2(X, Y); }
static inline double                   _imp_ARCTAN1(double ANGLE) { return atan(ANGLE); }

static inline double                   _imp_SIN(double ANGLE) { return sin(ANGLE); }
static inline double                   _imp_COS(double ANGLE) { return cos(ANGLE); }
static inline double                   _imp_TAN(double ANGLE) { return tan(ANGLE); }
static inline double                   _imp_COT(double ANGLE) {
  double sin_val = sin(ANGLE);
    
  // Check if denominator is close to zero
  if (fabs(sin_val) < 1e-15) { 
    // TO DO: _imp_signal(?,, ?, ?, "cot angle is a multiple of Pi");
    return INFINITY; 
  }
    
  return cos(ANGLE) / sin_val;
}

static inline double                   _imp_FRACTION(double R) { return R-_imp_TRUNC(R); }
static inline double                   _imp_FRACPT(double R) { return R-_imp_INTPT(R); }

static inline int                      _imp_IMOD(int I) { /*extern int abs(int j);*/ return abs(I); }
static inline double                   _imp_MOD(double R) { /*extern double fabs(double x);*/ return fabs(R); }

static inline int                      _imp_INT(double R) { return _imp_INTPT(R+0.5); }
static inline int                      _imp_INTPT(double R) { /*extern double floor(double x);*/ /*assert(floor(R) <= R);*/ return (int)floor(R); } // rounds *towards minus infinity*

static inline long int                 _imp_LINT(double R) { return _imp_LINTPT(R+0.5); }
static inline long int                 _imp_LINTPT(double R) { /*extern double floor(double x);*/ /*assert(floor(R) <= R);*/ return (long int)floor(R); }

static inline int                      _imp_TRUNC(double R) { /*extern double trunc(double x);*/ return (int)trunc(R); } // rounds *towards 0*
static inline int                      _imp_ROUND(double R) { return _imp_TRUNC(R < 0.0 ? R-0.5 : R+0.5); }

static inline long long int            _imp_IEXP(int NUM, int POWER) {
                                         int tot = 1, mul = POWER;
                                         while (mul-- > 0) tot *= NUM;
                                         return tot; // return (long)exp(NUM, POWER);
                                       }

static inline double                   _imp_REXP(double NUM, double POWER) {
                                         /*extern double pow(double x, double y);*/
                                         return POWER==2 ? NUM*NUM : pow(NUM,POWER);
                                       }

static inline double                   _imp_EXP(double NUM) {
                                         return exp(NUM);
                                       }

static inline double                   _imp_LOG(double X) { return log(X); }
static inline int                      _imp_REM(int P, int Q) { return P % Q; }
static inline double                   _imp_SQRT(double NUM) { /*extern double sqrt(double x);*/ return sqrt(NUM); }
static inline double                   _imp_RADIUS(double X, double Y) { return sqrt(X*X+Y*Y); }
static inline int                      _imp_ISQRT(int NUM) { /*extern double sqrt(double x);*/ return (int)sqrt(NUM); }
static inline int                      _imp_MULDIV(int A, int B, int C) { return (int) (((long long)A * (long long)B) / (long long)C); }

static inline void                     _imp_PROMPT(_imp_string S) { check_instream(__PRETTY_FUNCTION__); _imp_promptstr = S; }
static inline void                     _imp_READSYMBOL(int *P) { _imp_readsymbol(P); }
static inline void                     _imp_READCH(int *P) { _imp_readch(P); }
static inline int                      _imp_NEXTSYMBOL(void) { return _imp_nextsymbol(); }
static inline int                      _imp_NEXTCH(void) { return _imp_nextch(); }
static inline void                     _imp_SKIPSYMBOL(void) { _imp_skipsymbol(); }
static inline void                     _imp_PRINTSYMBOL(char SYM) { _imp_printsymbol(SYM); }
static inline void                     _imp_PRINTCH(char SYM) { _imp_printch(SYM); }
static inline void                     _imp_PRINTSTRING(_imp_string S) { _imp_printstring(S); }

// experimenting with different ways to implement longinteger:
#define _imp_WRITE(V, P) do { \
                              if (sizeof(V) == sizeof(long long int)) _imp_writelong(V, P); else _imp_writeint(V, P); \
                            } while (0)
//static inline void                     _imp_WRITE(int V, int P) { _imp_write(V, P); } // TO DO: longinteger

// IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}


static inline void                     _imp_READITEM(_imp_string *S) { _imp_readitem(S);  }
static inline void                     _imp_READSTRING(_imp_string *S) { _imp_readstring(S); }
static inline void                     _imp_READTEXT(_imp_string *S, int DELIM) { _imp_readtext(S, DELIM); }
static inline _imp_string              _imp_NEXTITEM(void) { return _imp_nextitem(); }
static inline void                     _imp_READLINE(_imp_string *S) { _imp_readline(S); }

static inline void                     _imp_SETSTREAMS(void) {
  /* To do - not a serious implementation yet. */
  // Just enough to get layout.imp from the Dec10 running!
  _imp_OPENOUTPUT(2, _imp_str_literal("/dev/stdout"));
}

static inline int                      _imp_INSTREAM(void) { check_instream(__PRETTY_FUNCTION__); return _imp_InStream; }
static inline int                      _imp_OUTSTREAM(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_OutStream; }
static inline int                      _imp_OUTDEV(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_OutStream; }
static inline int                      _imp_INPUTSTREAM(void) { check_instream(__PRETTY_FUNCTION__); return _imp_InStream; }
static inline int                      _imp_OUTPUTSTREAM(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_OutStream; }
static inline _imp_string              _imp_INPUTNAME(void) { check_instream(__PRETTY_FUNCTION__); return _imp_infile[_imp_InStream].fname; }
static inline _imp_string              _imp_OUTPUTNAME(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_outfile[_imp_OutStream].fname; }
static inline _imp_string              _imp_INFILENAME(void) { check_instream(__PRETTY_FUNCTION__); return _imp_infile[_imp_InStream].fname; }
static inline _imp_string              _imp_OUTFILENAME(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_outfile[_imp_OutStream].fname; }
static inline void                     _imp_SELECTINPUT(int N) { if (N < 0) return; _imp_InStream = N; _imp_InFile = _imp_infile[N].f; check_instream(__PRETTY_FUNCTION__); }
static inline void                     _imp_SELECTOUTPUT(int N) { if (N < 0) return; _imp_OutStream = N; _imp_OutFile = _imp_outfile[N].f; check_outstream(__PRETTY_FUNCTION__); }
static inline void                     _imp_OPENINPUT(int N, _imp_string FD) { _imp_openinput(N, FD); }
static inline void                     _imp_OPENOUTPUT(int N, _imp_string FD) { _imp_openoutput(N, FD); }
static inline void                     _imp_OPENBINARYINPUT(int N, _imp_string FD) { _imp_openinput(N, FD); }
static inline void                     _imp_OPENBINARYOUTPUT(int N, _imp_string FD) { _imp_openoutput(N, FD); }
static inline void                     _imp_DEFINEINPUT(int I, _imp_string SPEC) { _imp_OPENINPUT(I, SPEC); }
static inline void                     _imp_DEFINEOUTPUT(int I, _imp_string SPEC) { _imp_OPENOUTPUT(I, SPEC); }
static inline void                     _imp_ABANDONINPUT(void) { _imp_abandoninput(); }
static inline void                     _imp_ABANDONOUTPUT(void) { _imp_abandonoutput(); }
static inline void                     _imp_CLOSESTREAM(int stream) { _imp_closestream(stream); } /* EMAS */
static inline void                     _imp_CLOSEINPUT(void) { _imp_closeinput(); }
static inline void                     _imp_CLOSEOUTPUT(void) { _imp_closeoutput(); }
static inline void                     _imp_RESETINPUT(void) { check_instream(__PRETTY_FUNCTION__); fseek(_imp_infile[_imp_InStream].f, 0L, SEEK_SET); }
static inline void                     _imp_RESETOUTPUT(void) { check_outstream(__PRETTY_FUNCTION__); }
static inline void                     _imp_COMPLETEINPUT(void) { check_instream(__PRETTY_FUNCTION__); }
static inline void                     _imp_COMPLETEOUTPUT(void) { check_outstream(__PRETTY_FUNCTION__); }

// seek/tell:
static inline void                     _imp_POSITIONINPUT(int P) { _imp_SETINPUT(P); }
static inline void                     _imp_POSITIONOUTPUT(int P) { _imp_SETOUTPUT(P); }
static inline void                     _imp_SETINPUT(int P) { check_instream(__PRETTY_FUNCTION__); fseek (_imp_InFile, P, SEEK_SET); }
static inline void                     _imp_SETOUTPUT(int P) { check_outstream(__PRETTY_FUNCTION__); fseek (_imp_OutFile, P, SEEK_SET); }
static inline void                     _imp_INPUTPOSITION(void) { check_instream(__PRETTY_FUNCTION__); }  // TO DO: is this also 'inpos' or is it the ftell file offset?
static inline void                     _imp_OUTPUTPOSITION(void) { check_outstream(__PRETTY_FUNCTION__); }
static inline int                      _imp_ENDOFINPUT(void) { return 0; /*TO DO*/}

static inline int                      _imp_INPOS(void) { check_instream(__PRETTY_FUNCTION__); return _imp_infile[_imp_InStream].inpos; }
static inline int                      _imp_OUTPOS(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_outfile[_imp_OutStream].outpos; }

static inline void                     _imp_SPACE(void) { _imp_space(); }
static inline void                     _imp_SPACES(int N) { _imp_spaces(N); }
static inline void                     _imp_NEWPAGE(void) { _imp_newpage(); }
static inline void                     _imp_NEWLINE(void) { _imp_newline(); }
static inline void                     _imp_NEWLINES(int N) { _imp_newlines(N); }
static inline void                     _imp_PRINT(double R, int BEFORE, int AFTER) { _imp_print(R, BEFORE, AFTER); }        // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
static inline void                     _imp_PRINTFLOATING(double R, int A, int B) { _imp_printfloating(R, A, B); }
static inline void                     _imp_PRINTFL(double R, int PLACES) { _imp_printfl(R, PLACES); }
static inline void                     _imp_PRINTFHEX(double R) { _imp_printfhex(R); }

static inline _imp_string              _imp_SUBSTRING(_imp_string *S, int FROM, int TO) { return _imp_substring(S, FROM, TO); }
static inline _imp_string              _imp_FROMSTRING(_imp_string *S, int FROM, int TO) { return _imp_substring(S, FROM, TO); }
static inline _imp_string              _imp_TRIM(_imp_string S, int MAX) { return _imp_trim(S, MAX); }
static inline void                     _imp_TOUPPER(_imp_string *S) { return _imp_toupper(S); }
static inline void                     _imp_TOLOWER(_imp_string *S) { return _imp_tolower(S); }


static inline _imp_string              _imp_TIME(void) { return _imp_time(); }
static inline _imp_string              _imp_DATE(void) { return _imp_date(); }
static inline int                      _imp_CPUTIME(void) { return _imp_cputime(); }

static inline _imp_string              _imp_CLIPARAM(void) { return _imp_cliparam(); }

// FREESTORE
static inline int                      _imp_FREESTORE(void) { return 0x8000; }  // made up value for old Imp15 programs. Could do a real number for modern linux.

static inline _imp_string              _imp_ITOS(int I, int P) { return _imp_itos(I, P); }
static inline int                      _imp_STOI(_imp_string S) { return _imp_stoi(S); }
// there are some other calls like ITOS I think.  (Also check re ITOS2/ITOS)

// not to be confused with the EVENT_MESSAGE field.  This is specifically for native system calls that went wrong.
static inline _imp_string              _imp_SSFMESSAGE(void) { return _imp_c2istr(strerror(errno)); }


//////////////////////////////////////////////////////////////////////////////////////////////


// These are the routines from JDM's imp2026 perms which I've added
// to my perms for his convenience when testing his programs under i2c.

// I'm not promising that these will be supported long-term...

// (The only reason for these const int fn shennanigans is to avoid
//  namespace pollution from %constinteger in the Imp perms...)

// How to implement %permconstinteger via C's inline functions!:
static inline const int _imp_FILESEP(void)     { return '='; }
static inline const int _imp_CMAXSIGFIGS(void) { return 15; }

static inline const int _imp_FROMSTART(void)   { return 0; }
static inline const int _imp_FROMCURRENT(void) { return 1; }
static inline const int _imp_FROMEND(void)     { return 2; }

extern const _imp_string _imp_typedesc[32]; // or move spec to ingternal.h?

static inline _imp_string _imp_TYPENAME(int TYPECODE) {
  return ((((0) >= (TYPECODE))) || (((TYPECODE) > (31))))
         ? _imp_str_literal("unknown")
         : _imp_typedesc[TYPECODE];
}

static inline void _imp_SEEKINPUT(int DISPLACEMENT, int STARTPOINT) {
  check_instream(__PRETTY_FUNCTION__);
  fseek (_imp_InFile, DISPLACEMENT, STARTPOINT);
}
static inline void _imp_SEEKOUTPUT(int DISPLACEMENT, int STARTPOINT) {
  check_outstream(__PRETTY_FUNCTION__);
  fseek (_imp_OutFile, DISPLACEMENT, STARTPOINT);
}

static inline int _imp_TELLINPUT(void) {
  check_instream(__PRETTY_FUNCTION__);
  return ftell(_imp_InFile);
}
static inline int _imp_TELLOUTPUT(void) {
  check_outstream(__PRETTY_FUNCTION__);
  return ftell(_imp_OutFile);
}

static inline int _imp_READBUFFER(void *PTR, int COUNT) {
  int i;
  unsigned char *s;
  s = (unsigned char *)PTR;
  int ch;
  for (i = 0; i < COUNT; i++) {
    ch = _imp_getchar(_imp_InFile);
    if (ch == EOF) {
      //_imp_signal(9, 2, _imp_InStream, strerror(errno));
      //exit(EXIT_FAILURE);
      return i;
    }
    *s++ = ch;
  }
  return COUNT;
}

static inline _imp_string _imp_CURRENTPROMPT(void) { return _imp_promptstr ; }

static inline int _imp_GETMAXINPUTSTREAM(void) { return 255 ; }
static inline int _imp_GETMAXOUTPUTSTREAM(void) {  ; }

static inline int _imp_FILEEND(void) {
  int ch;
  check_instream(__PRETTY_FUNCTION__);
  _imp_infile[_imp_InStream].nextchar = -1;
  if (feof(_imp_InFile)) return 1;
  ch = _imp_getchar(_imp_InFile);
  if (ch == EOF) return 1;
  ungetc(ch, _imp_InFile);
  _imp_infile[_imp_InStream].nextchar = ch;
  return 0;
}

// TO DO:
static inline void _imp_ALLOWEOFEVENT(void) {  ; }
static inline void _imp_BLOCKEOFEVENT(void) {  ; }

// TO DO:
static inline int _imp_OLDOUTPUTSTREAM(void) { return 0 ; }

static inline int _imp_WRITEBUFFER(void *PTR, int COUNT) {
  int ch, rc, i;
  unsigned char *s = (unsigned char *)PTR;
  for (i = 0; i < COUNT; i++) {
    ch = *s++;
    rc = fputc(ch, _imp_outfile[_imp_OutStream].f);
    if (rc != EOF) {
      if (ch == '\n' || ch == '\r' || ch == '\f') {
        _imp_outfile[_imp_OutStream].outpos = 0;
      } else if (ch == '\b') {
        _imp_outfile[_imp_OutStream].outpos -= 1;
      } else if (ch == '\t') {
        _imp_outfile[_imp_OutStream].outpos = (_imp_outfile[_imp_OutStream].outpos + 8) & (~7)  ;// at least on linux
      } else {
        _imp_outfile[_imp_OutStream].outpos += 1;
      }
      //  _imp_signal(9, 2, _imp_OutStream, strerror(errno));
      //  return i;
    }
  }
  return COUNT;
}

static inline void _imp_FLUSHOUTPUT(void) { fflush(_imp_OutFile); }

static inline void _imp_PRINTREAL(double F, int SF, int PLACES) {_imp_print(F, SF, PLACES);}
static inline void _imp_PRINTFLOAT(double F, int SF) { _imp_printfl(F, SF) ; }

// TO DO: more number formatting but to strings rather than current output file
static inline _imp_string _imp_FTOS(double F, int SF) {
  return _imp_formatf("%*f", SF, F);
}

// Does C's printf handle bases???
extern char *_c_itoa(int value, char *dest, int base);
static inline _imp_string _imp_INT2ASCII(int NN, int BASE, int PLACES) {
  char buffer[256];
  _c_itoa(NN, buffer, BASE);
  return _imp_c2istr(buffer);
}
static inline _imp_string _imp_FORMATNUMBER(int N, int BASE, int PLACES) {
  char buffer[256];
  _c_itoa(N, buffer, BASE);
  return _imp_c2istr(buffer);
}

// User can print "16_" before this if that's what's wanted:
static inline _imp_string _imp_INT2HEX(int NN, int PLACES) {
  return _imp_formatf("%*A", PLACES, NN);
}

// User can print "2_" before these if that's what's wanted:
static inline _imp_string _imp_INT2BIN(int NN) {
  return _imp_formatf("%32b", NN);
}
static inline _imp_string _imp_SHORT2BIN(short NN) {
  return _imp_formatf("%16b", NN);
}

// TO DO: %name & dopevector stuff
static inline int _imp_ARRAYENTRYSIZE(void *N) {
  fprintf(stderr, "* ARRAYENTRYSIZE not yet implemented\n");
  exit(1); // short-term error message. Needs _imp_signal. *TO DO*
}
static inline int _imp_ARRAYENTRYTYPE(void *N) {
  fprintf(stderr, "* ARRAYENTRYTYPE not yet implemented\n");
  exit(1); // short-term error message. Needs _imp_signal. *TO DO*
}
static inline int _imp_ARRAYDIMENSIONS(void *N) {
  fprintf(stderr, "* ARRAYDIMENSIONS not yet implemented\n");
  exit(1); // short-term error message. Needs _imp_signal. *TO DO*
}
static inline int _imp_ARRAYLOWERBOUND(void *N, int INDEX) {
  fprintf(stderr, "* ARRAYLOWERBOUND not yet implemented\n");
  exit(1); // short-term error message. Needs _imp_signal. *TO DO*
}
static inline int _imp_ARRAYUPPERBOUND(void *N, int INDEX) {
  fprintf(stderr, "* ARRAYUPPERBOUND not yet implemented\n");
  exit(1); // short-term error message. Needs _imp_signal. *TO DO*
}

// TO DO:  Is this from envp, akin to argv?
static inline int _imp_GETENVCOUNT(void) {  ; }
static inline _imp_string _imp_GETENVNAME(int INDEX) {  ; }
static inline _imp_string _imp_GETENV(int INDEX) {  ; }
static inline int _imp_GETENVINDEX(_imp_string VARNAME) {  ; }
static inline _imp_string _imp_GETENVASSTRING(_imp_string ENVNAME) {  ; }
static inline int _imp_GETENVASINTEGER(_imp_string VARNAME) {  ; }


static inline int _imp_GETARGCOUNT(void) { return _imp_ARGC() ; }
static inline _imp_string _imp_GETARG(int INDEX) { return _imp_ARGV(INDEX) ; }
static inline double _imp_SINE(double X) { return sin(X) ; }
static inline double _imp_COSINE(double X) { return cos(X) ; }
static inline double _imp_LOG2(double X) { return log2(X) ; }
static inline double _imp_LOG10(double X) { return log10(X) ; }
static inline void _imp_EXIT(int STATUS) { exit(STATUS) ; }

#endif // __VISIBLE_H__
