//-------------------------------------------------
//          TestSuite: Stability Shared external 
//-------------------------------------------------

#include "lcd.h"
#include "util.h"
#include "os_core.h"
#include "os_scheduler.h"
#include "os_memory.h"

#include <avr/interrupt.h>
#include <string.h>

#define DELAY 100

#define DRIVER extHeap


unsigned char pos = 16;
//! This program prints the current time in the first line.
PROGRAM(4, AUTOSTART) {
  for (;;) {
    os_enterCriticalSection();
    lcd_line1();
    lcd_writeProgString(PSTR("Time: ")); // +6
    uint16_t const msecs = (os_coarseSystemTime * 33 / 10) % 1000;
    uint16_t const secs = os_coarseSystemTime / 300;
    uint16_t const mins = secs / 100;
    if (mins < 100) {
      lcd_writeDec((secs / 60) % 100); // +2
      lcd_writeChar('m'); // +1
      lcd_writeChar(' '); // +1
      lcd_writeDec(secs % 60); // +2
      lcd_writeChar('.'); // +1
      lcd_writeDec(msecs / 100); // +2
      lcd_writeChar('s'); // +1
      lcd_writeChar(' '); // +1
    } else {
      lcd_writeProgString(PSTR("> 100 min"));
    }
    lcd_goto(2,pos+1);
    os_leaveCriticalSection();
    delayMs(100);
  }
}

static prog_char const spaces16[] = "                ";

void writeChar(char c) {
  os_enterCriticalSection();
  if (++pos > 16) {
    pos = 5;
    lcd_line2();
    lcd_goto(2,pos);
    lcd_writeChar('|');
    pos++;
    lcd_writeProgString(spaces16+5);
  }
  lcd_goto(2,pos);
  lcd_writeChar(c);
  os_leaveCriticalSection();
}

MemAddr sh;
uint16_t sz;

void makeCheck(uint16_t size, MemValue pat) {
  MemAddr const priv = os_malloc(intHeap, size);
  os_sh_read(DRIVER, &sh, 0, (uint8_t*)priv, size);
  MemValue const check = intHeap->driver->read(priv);
  MemAddr p;
  unsigned char tmp;
  for (p = priv; p < priv + size; p++) {
    tmp = intHeap->driver->read(p);
    if (tmp != check)
      os_error("Write was interleaved");
    intHeap->driver->write(p, pat);
  }
  os_sh_write(DRIVER, &sh, 0, (uint8_t*)priv, size);
  os_free(intHeap, priv);
}

void myRealloc(uint16_t newsz) {
  MemAddr newsh = os_sh_malloc(DRIVER, newsz);
  // important, because our programs expect all cells to be the same
  MemAddr i;
  for (i = 0; i < sz; i++) {
    DRIVER->driver->write(newsh+i, 0);
  }
  os_enterCriticalSection();
  sh ^= newsh;
  newsh ^= sh;
  sh ^= newsh;
  sz = newsz;
  os_leaveCriticalSection();
  os_sh_free(DRIVER, &newsh);
}

PROGRAM(1, AUTOSTART) {
  uint8_t counter = 4;
  sz = intHeap->use.size / counter;
  sh = os_sh_malloc(DRIVER, sz);
  MemAddr i;
  for (i = 0; i < sz; i++) {
    DRIVER->driver->write(sh+i, 0xFF);
  }
  while (--counter)
    os_exec(2, 10);
  MemValue start;
  MemValue end;
  uint16_t writeItrs = 0;
  for (;;) {
    cli();
    start = DRIVER->driver->read(sh);
    end = DRIVER->driver->read(sh + sz - 1);
    sei();
    if (start != end) {
      writeItrs++;
      cli();
      lcd_goto(2,1);
      lcd_writeProgString(spaces16+(16-4));
      lcd_goto(2,1);
      lcd_writeDec(writeItrs);
      sei();
      Time const now = getSystemTime(); // granularity: 0.1/8 ms
      while (now == getSystemTime())
        os_yield();
    }
    os_yield();
  }
}

PROGRAM(2, DONTSTART) {
  for (;;) {
    uint8_t k = (TCNT0 % 40);
    while (k--)
      makeCheck(sz, os_getCurrentProc());
    myRealloc(sz);
    writeChar(os_getCurrentProc()+'0');
  }
}
