#ifdef LINUX
#include <stdio.h>
#define uint8_t unsigned char
#define int8_t signed char
#define int16_t short
#else
#include <vectrex.h>
#define uint8_t unsigned int
#define int8_t int
#define int16_t long
#endif
//
// probably should have used something like:
//
//    https://bitesofcode.wordpress.com/2016/12/23/landscape-generation-using-midpoint-displacement/
//
// but tried my own hack first to see what it would look like.  At least mine only uses context of
// immediate neighbour, so very suitable for left & right scrolling on Vectrex (for a Lunar Lander
// game, or a Defender clone or maybe Scramble...)
//
// Note: surface wraps so can be infinite.
//
// Needs a good random with an easily-set seed.
////

//#define random Random
#ifdef LINUX
#define WRAP 255
#define EIGHT 8
#define SEVEN 7
#define FOUR 4
#define ONE 1
#define SEED 1234
#else
#define WRAP 255L
#define EIGHT 8L
#define SEVEN 7L
#define FOUR 4L
#define ONE 1L
#define SEED 1234L
#endif

#define BASE 2    /* minimum height */

static uint8_t _x, _a, _b, _c;
static void initRandom8(uint8_t s1, uint8_t s2, uint8_t s3, uint8_t x0) {
  _x = x0; _a = s1; _b = s2; _c = s3; _x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
}

static uint8_t random(void) { // assert returns unsigned value that fits in an int.
  _x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
  return _c;
}

void srandom(int16_t seed) {
  int16_t s1, s2, s3, s4;
  s1 = seed; s2 = s1>>FOUR; s3 = s2>>FOUR; s4 = s3>>FOUR;
  initRandom8((uint8_t)s4,(uint8_t)s3,(uint8_t)s2,(uint8_t)s1);
}

#define set_scale(s) do { VIA_t1_cnt_lo = s; } while (0)

unsigned int surface(long i) {
  int16_t block = 0L;
  uint8_t flat=0, prev_flat=0, height=0, prev_major=0, major=0, next_major=0, minor;
  int16_t global_seed = SEED;
  
  // everything is in 8-unit blocks
  block = ((i-EIGHT)&WRAP) & ~SEVEN;
  srandom(((block<<3)+global_seed)^block);
  prev_major = (random()&15) * 4;
  prev_flat = random()&15; // 1 in 15 is a flat zone, where we override the point to the right, and set it the
                           // same height as this point, providing a flat area to land on, or house fuel depots, etc.
      
  block = ((i+EIGHT)&WRAP) & ~SEVEN;
  srandom(((block<<3)+global_seed)^block);
  next_major = (random()&15) * 4;

  block = i & ~SEVEN;
  srandom(((block<<3)+global_seed)^block);
  major = (random()&15) * 4;
  flat = random()&15; // 1 in 15 is a flat zone
      
  // every flat segment is a landing zone - probability = 1 in 16      
  if (prev_flat == 0) {
    minor = 0;
    major = prev_major;
  } else if (flat == 0) {
    minor = 0;
    next_major = major;
  } else {
    srandom(((i<<3)+global_seed)^i);
    minor = (random()&3);
  }
  height = (uint8_t)(major + ((next_major - major) * (i&7) / 8)); // on the vectrex could skip minor, and draw one line between 8 points.
  return (uint8_t)(height+minor);
}

#ifdef LINUX
void stars(int i) {
  while (i --> 0) fputc('*', stdout);
  fputc('\n', stdout);
}
#endif

int main(void)
{
  //int y;
  int16_t x, offset;
  int8_t contour[WRAP + 1L];

  offset = 0;

  //contour[0] = (int8_t)surface(-128);
  //contour[1] = (int8_t)surface(-127);
  //contour[2] = (int8_t)surface(-126);
  //contour[3] = (int8_t)surface(-125);

  for (x = 0L; x <= WRAP; x += 1L) {
    contour[x] = (int8_t)surface(x);
#ifdef LINUX
    //stars(BASE+contour[x]);
#endif
  }

  for (;;) {
#ifndef LINUX
    Wait_Recal();
    Reset0Ref(); set_scale(0x10); Intensity_5F();
    Print_Str_d(0, -70, "HELLO WORLD\x80");
#endif
    for (x = 0L; x < 28L; x++) {
#ifndef LINUX
      Reset0Ref(); set_scale(0x80); Intensity_5F();
      Moveto_d((int)(contour[(x+offset)&WRAP]-128), 
               (int)(x*8 - 128)+16);
      Draw_Line_d((int)((unsigned int)contour[x+1]-(unsigned int)contour[x]), 8);
#endif
      stars(BASE+contour[(x+offset)&WRAP]);
    }

    fprintf(stdout, "\n\n\n");
    offset = (offset+1)&WRAP;
  };

  // if return value is <= 0, then a warm reset will be performed,
  // otherwise a cold reset will be performed
  return 0;
}
