// period time for game engine


#define TIMEPER 300
unsigned long gameTime;

int screenX = 1, screenY = 1;

int sign = 1;

// http://www.braun-home.net/michael/info/misc/VT100_commands.htm
// x,y in upper left corner
// x increasing down
// y increasing right

#define sp(x) Serial.print(x)
#define spln(x) Serial.println(x)
#define sw(x) Serial.write(x)
#define esc() sw(0x1b)
#define spEsc(x) esc(), sp(x)

void setCursor(int x, int y) {
  sw(0x1b);
  sp("[");
  sp(x);
  sp(";");
  sp(y);
  sp("H");
}

void eraseCh(int x, int y) {
  setCursor(x, y);
  sp(" ");
}

void saveCursor() {
  // ESC 7
  // restore ESC 8
  //esc();
  //sp("7");
  spEsc("7");  // tx <esc> karakter efterfulgt af et syvtal
}

void restoreCursor() {
  // ESC 7
  // restore ESC 8
  //esc();
  //sp("8");
  spEsc("8");
}

void cursorOff() {
  //Esc [ ? 25 l
  esc();
  sp("[?25l");
}

void cursorOn() {
  //Esc [ ? 25 l
  esc();
  sp("[?25h");
}

void clrScreen() {
  /*
  *  ESC [ K          erase to end of line (inclusive)
 *  ESC [ 0 K         erase to end of line (inclusive)
 *  ESC [ 1 K         erase to beginning of line (inclusive)
 *  ESC [ 2 K         erase entire line (cursor doesn't move)
 *  ESC [ J           erase to end of screen (inclusive)
 *  ESC [ 0 J         erase to end of screen (inclusive)
 *  ESC [ 1 J         erase to beginning of screen (inclusive)
 *  ESC [ 2 J         erase entire screen (cursor doesn't move)
*/
  esc();
  sp("[2J");
}

void screenWonB(boolean yes) {
  /*
*  ESC [ ? 5 h       black characters on white screen mode
 *  ESC [ ? 5 l       white characters on black screen mode
*/
  spEsc("[?5");
  if (yes)
    sp("l");
  else
    sp("h");
}


void eraseToEOL() {
  /*
  Erasing:

 *  ESC [ K           erase to end of line (inclusive)
 *  ESC [ 0 K         erase to end of line (inclusive)
 *  ESC [ 1 K         erase to beginning of line (inclusive)
 *  ESC [ 2 K         erase entire line (cursor doesn't move)
 *  ESC [ J           erase to end of screen (inclusive)
 *  ESC [ 0 J         erase to end of screen (inclusive)
 *  ESC [ 1 J         erase to beginning of screen (inclusive)
 *  ESC [ 2 J         erase entire screen (cursor doesn't move)
*/
  spEsc("[K");  // ?
}

void eraseLine() {
  spEsc("[2K");
}


void drawBorderOfScreen(int x, int y) {

  screenX = x;
  screenY = y;
  setCursor(1, 1);
  for (int i = 1; i <= y; i++)
    sp("*");
  setCursor(x, 1);
  for (int i = 1; i <= y; i++)
    sp("*");

  for (int i = 1; i <= x; i++) {
    setCursor(i, 1);
    sp("*");
    setCursor(i, y);
    sp("*");
  }
}

// -------oOo--------
// gameengine
int sz = 20;
int counterX = 2;
int counterY = 2;
int state = 0;  // 0 ->, 1 \/  2 <-  3
char dot = 'A';

void doGameEngine() {

  switch (state) {
    case 0:  // right
      if (counterY < sz) {
        setCursor(counterX, counterY);
        sp(" ");
        counterY++;
        setCursor(counterX, counterY);
        sp(dot);
      } else {
        dot++;  // B
        state = 1;
        counterX = 2;
        counterY = sz;
      }
      break;
    case 1:  // down
      if (counterX < sz) {
        setCursor(counterX, counterY);
        sp(" ");
        counterX++;
        setCursor(counterX, counterY);
        sp(dot);
      } else {
        dot++;
        state = 2;
        counterX = sz;
        counterY = sz;
      }
      break;
    case 2:  //left
      if (counterY > 2) {
        setCursor(counterX, counterY);
        sp(" ");
        counterY--;
        setCursor(counterX, counterY);
        sp(dot);
      } else {
        dot++;
        state = 3;
        counterX = sz;
        counterY = 2;
      }
      break;
    case 3:  //up
      if (counterX > 2) {
        setCursor(counterX, counterY);
        sp(" ");
        counterX--;
        setCursor(counterX, counterY);
        sp(dot);
      } else {
        dot = 'A';
        state = 0;
        counterX = 2;
        counterY = 2;
      }
      break;


    default:;
  }
}

void handleUser() {
  int k;
  // basicly cehck if keyboard input and process it
  // NB NB in gtkTerm and putty no <enter> is needed
  // keys are transmitted at once

  while (Serial.available()) {
    k = Serial.read();  // one byte 0..255
    switch (k) {
      case '+':
        sign = 1;
        break;
      case '-':
        sign = -1;
        break;
      case 'c':

        break;
      default:;
    }
  }
}

/* -------oOo------- */
void setup() {

  Serial.begin(115200);
  Serial.println(__DATE__);
  Serial.println(__FILE__);
  delay(200);
  clrScreen();
  screenWonB(1);
  setCursor(1, 1);
  cursorOff();

  gameTime = millis() + TIMEPER;
  drawBorderOfScreen(21, 21);
  setCursor(1, 1);
  sp("12345678901234567890");
  for (int i = 1; i <= 21; i++) {
    setCursor(i, 1);
    sp(i % 10);
  }
}

unsigned long nextTime;

void loop() {
  nextTime = millis();
  if (gameTime <= nextTime) {
    doGameEngine();
    gameTime += TIMEPER;
  }
  handleUser();
}

// end of file  /JDN
