/* impl.c.mpsliban: HARLEQUIN MEMORY POOL SYSTEM LIBRARY INTERFACE (DYLAN)
 *
 *
 * PURPOSE
 *
 * .purpose: The purpose of this code is
 *   1. to connect the MPS Library Interface to the Dylan runtime
 *      with no dependency on the C runtime.
 */

#include "mpslib.h"
#include <windows.h>
#include <stdio.h>
#include <time.h>

/* First some simple stream buffer management */

#define BUFFER_SIZE 200
#define BUFFER_LIMIT (BUFFER_SIZE + 1)

typedef struct message_buffer_s  *message_buffer_t;

typedef struct message_buffer_s {
  int   buf_index;
  char  buf_data [BUFFER_LIMIT];
} message_buffer_s;


static message_buffer_s err_buf_struct = {0, ""};
static message_buffer_s out_buf_struct = {0, ""};

static message_buffer_t err_buf = &err_buf_struct;
static message_buffer_t out_buf = &out_buf_struct;

extern BOOL dylan_streamQ;
extern char *dylan_buffer;
extern int  dylan_buffer_pos;
extern int  dylan_buffer_size;

int buffer_add (message_buffer_t buf, char c)
{
  int res = 0;
  if (dylan_streamQ) {
    if (dylan_buffer_pos < dylan_buffer_size) {
      dylan_buffer[dylan_buffer_pos++] = c;
    } else {
      /* HACK -- delete last line in dylan buffer on overflow */
      int pos = dylan_buffer_pos - 1;

      if (dylan_buffer[pos] != '\n') {
        while (dylan_buffer[--pos] != '\n');
        while (pos < dylan_buffer_pos) {
          dylan_buffer[++pos] = ' ';
        }
        dylan_buffer[pos - 1] = '\n';
      }
    }
  } else {
    buf->buf_data[buf->buf_index++] = c;
    if ((c == '\n') || (c == '\0') || (buf->buf_index >= BUFFER_SIZE)) {
      res = buf->buf_index;
    }
  }
  return(res);
}

char *buffer_contents (message_buffer_t buf)
{
  buf->buf_data[buf->buf_index] = '\0';
  return(buf->buf_data);
}

void buffer_reset (message_buffer_t buf)
{
  buf->buf_index = 0;
}


static HANDLE current_log_file = 0;


HANDLE ensure_log_file (void)
{
  if (current_log_file == 0) {
    current_log_file = CreateFile("dylan-runtime.log",
                                  GENERIC_WRITE,
                                  FILE_SHARE_READ,
                                  0,
                                  OPEN_ALWAYS,
                                  FILE_ATTRIBUTE_NORMAL,
                                  0);
  }
  return(current_log_file);
}

void ensure_log_file_closed (void)
{
  if (current_log_file != 0) {
    if (CloseHandle(current_log_file)) {
      current_log_file = 0;
    }
  }
}


void plinth_flush_string_to_file (char *string, int length)
{
  int written;
  HANDLE log_file = ensure_log_file();
  if (log_file != 0) {
    WriteFile(log_file, string, length, &written, 0);
  }
}

void plinth_flush_string_to_debugger (char *string, int length)
{
  OutputDebugString(string);
}

void plinth_flush_string (char *string, int length)
{
  plinth_flush_string_to_debugger(string, length);
  plinth_flush_string_to_file(string, length);
}

int plinth_putc (message_buffer_t buf, int c)
{
  int num_to_flush = buffer_add(buf, (char)c);
  if (num_to_flush > 0) {
    plinth_flush_string(buffer_contents(buf), num_to_flush);
    buffer_reset(buf);
  }
  return(c);
}

/* Now the plinth implementation itself */


void mps_lib_abort(void)
{
  ensure_log_file_closed();
  DebugBreak();
}

int mps_lib_get_EOF(void)
{
  return 0;
}

mps_lib_FILE *mps_lib_get_stderr(void)
{
  return (mps_lib_FILE *)err_buf;
}

mps_lib_FILE *mps_lib_get_stdout(void)
{
  return (mps_lib_FILE *)out_buf;
}

int mps_lib_fputc(int c, mps_lib_FILE *stream)
{
  return plinth_putc((message_buffer_t)stream, c);
}

int mps_lib_fputs(const char *s, mps_lib_FILE *stream)
{
  int i = 0;
  char c;
  while (c = s[i++]) {
    plinth_putc((message_buffer_t)stream, c);
  }
  return 1;
}

int mps_lib_fputs_(const char *s, int end, mps_lib_FILE *stream)
{
  int i = 0;
  char c;
  while ((i < end) && (c = s[i++])) {
    plinth_putc((message_buffer_t)stream, c);
  }
  return 1;
}

void mps_lib_assert_fail(const char *message)
{
  mps_lib_FILE *err = mps_lib_get_stderr();
  mps_lib_fputs("\nMPS ASSERTION FAILURE: ", err);
  mps_lib_fputs(message, err);
  mps_lib_fputs("\n", err);
  mps_lib_abort();
}

mps_clock_t mps_clock(void)
{
  return (unsigned long)clock();
}

mps_clock_t mps_clocks_per_sec(void)
{
  return (unsigned long)CLOCKS_PER_SEC;
}


unsigned long mps_lib_telemetry_control(void)
{
  return 0;
}

void *mps_lib_memset( void *dest, int c, size_t count )
{
  return memset(dest, c, count);
}


void *mps_lib_memcpy( void *dest, const void *src, size_t count )
{
  return memcpy(dest, src, count);
}


int mps_lib_memcmp(const void *s1, const void *s2, size_t n)
{
  return memcmp(s1, s2, n);
}
