
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>

#include "misc.h"
#include "tracelog.h"
#include "fizmo.h"
#include "streams.h"
#include "stack.h"
#include "zpu.h"
#include "i18n.h"
#include "routine.h"
#include "variable.h"
#include "splint.h"


static struct sigaction fizmo_sigactions;


void opcode_restart(void)
{
  TRACE_LOG("Opcode: RESTART.\n");

  terminate_interpreter = INTERPRETER_QUIT_RESTART;
}


void opcode_verify(void)
{
  uint16_t file_length = (z_mem[0x1a] << 8) | z_mem[0x1b];
  uint16_t header_checksum = (z_mem[0x1c] << 8) | z_mem[0x1d];
  uint16_t calculated_checksum = 0;
  int i, input, scale;

  TRACE_LOG("Opcode: VERIFY.\n");

  if ( (file_length == 0) && (header_checksum == 0) )
    evaluate_branch((uint8_t)1);

  if (ver <= 3)
    scale = 2;
  else if (ver <= 5)
    scale = 4;
  else
    scale = 8;

  if (file_length * scale - 1 >= 0x40)
  {
    if (fseek(
          active_z_story->z_file,
          0x40 + active_z_story->story_file_exec_offset,
          SEEK_SET) != 0)
      i18n_translate_and_exit(
          i18n_fizmo_FATAL_ERROR_READING_STORY_FILE,
          -1);

    i = 0x40;
    while (i < file_length * scale)
    {
      if ((input = fgetc(active_z_story->z_file)) == EOF)
        i18n_translate_and_exit(
            i18n_fizmo_FATAL_ERROR_READING_STORY_FILE,
            -1);

      calculated_checksum += input;
      i++;
    }
  }

  evaluate_branch(
      calculated_checksum == header_checksum ? (uint8_t)1 : (uint8_t)0);
}


void opcode_piracy(void)
{
  TRACE_LOG("Opcode: PIRACY.\n");

  evaluate_branch(1);
}


/*@noreturn@*/ void abort_interpreter(int exit_code,
    /*@null@*/ z_ucs *error_message)
{
#ifdef THROW_SIGFAULT_ON_ERROR
  int x;
#endif

  TRACE_LOG("Aborting interpreter with message \"");
  TRACE_LOG_Z_UCS(error_message);
  TRACE_LOG("\"\n");

  if (active_sound_interface != NULL)
    active_sound_interface->close_sound();

  close_streams(error_message);

  // From the OpenGroup: The value of status may be 0, EXIT_SUCCESS,
  // EXIT_FAILURE, [CX] [Option Start]  or any other value, though only
  // the least significant 8 bits (that is, status & 0377) shall be
  // available to a waiting parent process. [Option End]
  //  -> Thus, we print the exit code seperately.
  fprintf(stderr, "Exit code: %d.\n", exit_code);

  (void)signal(SIGSEGV, SIG_DFL);

#ifdef THROW_SIGFAULT_ON_ERROR
  x = *((int*)NULL);
#endif
  exit(exit_code);
}


static void catch_signal_and_abort(int sig_num)
{
  TRACE_LOG("Caught signal %d.\n", sig_num);

  i18n_translate_and_exit(
      i18n_fizmo_CAUGHT_SIGNAL_P0D_ABORTING_INTERPRETER,
      -1,
      (long)sig_num);
}


void init_signal_handlers(void)
{
  TRACE_LOG("Initialiazing signal handlers.\n");

  sigemptyset(&fizmo_sigactions.sa_mask);
  fizmo_sigactions.sa_flags = 0;
  fizmo_sigactions.sa_handler = &catch_signal_and_abort;

  sigaction(SIGSEGV, &fizmo_sigactions, NULL);
  sigaction(SIGTERM, &fizmo_sigactions, NULL);
  sigaction(SIGINT, &fizmo_sigactions, NULL);
  sigaction(SIGQUIT, &fizmo_sigactions, NULL);
  sigaction(SIGBUS, &fizmo_sigactions, NULL);
  sigaction(SIGILL, &fizmo_sigactions, NULL);
}

