// ================================================================
//  AppShell
//  using a Micro Controller, 8 LED 4 color bar and 4 Button module
//  and Harware Support functions
//

// ----------------  hardware dependent section  ------------------
// constants won't change, unless the hardware changes.
const int AUDIO_OUT = 13;
const int AUDIO_NEG = 12;

// ---------- 8 LED configuration data
const int nleds=8;
const int lites[] = {9, 8, 7, 6, 5, 4, 3, 2};   //the LED pin assignments
#define ON_STATE 0
const int PSUDOVCC = 10;                  // supplies +V to 8 LED module

// ----------  button config. data
const int button[] = {16, 17, 18, 19};    //The four button input pins
#define PRESSED_STATE 0
const int PSUDOGND = 15;                  // used by 4 Key module

const bool debugPrt=false;
// ------------------------------------------------------------
// 4-Button module support

bool btn1 = false;  // reflects the state of button #1
bool btn2 = false;
bool btn3 = false;
bool btn4 = false;
bool Btn=false;   // reflects Buttons collectively
bool ESC = false;  // a psuedo button, true if btn4 is held down for 2 seconds(given ~1ms update freq.)

bool btn1Changed = false; // true if btn1 just changed
bool btn2Changed = false;
bool btn3Changed = false;
bool btn4Changed = false;
bool BtnChanged = false;  // true if any button changes

#define btn1Pressed (btn1Changed && btn1)
#define btn2Pressed (btn2Changed && btn2)
#define btn3Pressed (btn3Changed && btn3)
#define btn4Pressed (btn4Changed && btn4)
#define BtnPressed (BtnChanged && Btn)

// ------------------------------------------------------------
// 8 LED display bar support
unsigned int msCnt;   // pseudo MSecs, incremented every time 'refreshDisp()' is called
// NOTE! in the arrays below, elements [1]-[8] relate to LED 1-8 respectively. ([0] & [9] are for overflow protection)
bool dim[8+2];  // set true which LEDs you want to be dim (~10% on)
bool lit[8+2];  //                 "                  lit (~33% on)
bool brt[8+2];  //                 "                  bright (100%)
int brightOne;  // sets indicated (1-8) LED Bright.  Useful as a cursor or sprite
int litLED;     //         "                 Lit        “

// the setup routine runs once on power up or when you press reset:
//=================================================================
void setup() {
  // ------------ setup LED pins as outputs
  for (int x = 0; x < nleds; x++)
  {
    pinMode(lites[x], OUTPUT);
    digitalWrite(lites[x], (ON_STATE));  // on startup have LEDs momentarially On
  }
//  pinMode(PSUDOVCC, OUTPUT);    // provide +v for LED bar module
//  digitalWrite(PSUDOVCC, HIGH);
  pinMode(PSUDOVCC, INPUT);   // let it be externally driven
  delay(100);

  // ------------ setup the four button module
  for (int x = 0; x < 4; x++)
  {
    pinMode(button[x], INPUT);      // button pins are inputs
    digitalWrite(button[x], HIGH);  // enable internal pullup; buttons start in high position; logic reversed
  }
  pinMode(PSUDOGND, OUTPUT);    // provide Ground (-V) for the button module
  digitalWrite(PSUDOGND, LOW);

  // ------------ setup and init test for the Audio
  pinMode(AUDIO_OUT, OUTPUT);
  digitalWrite(AUDIO_OUT, LOW);
  pinMode(AUDIO_NEG, OUTPUT);
  digitalWrite(AUDIO_NEG, LOW); // -V ~ground for piezo
  beep(50); beep(50); beep(150);  // announce "it is alive"

  clearDisp();  // initialize display LEDs
  scanBtns();   //   "  button statuses
}

// the loop routine runs over and over again forever:
// ==================================================
void loop() {
//  myApp();
//  myApp1();
  myApp2();
}

void myApp() {  // a proposed typical framework .....
  while(true) {
    scanBtns();

    // code to implement your function/activity/game of choice

    refreshDisp();
    delay(1);
  } 
}

void myApp1() {  // a proposed typical framework .....
  while(true) {
    scanBtns();

    // code to implement your function/activity/game of choice
    // ... ... ... ...
    if (btn1Pressed) {boop(75); lit[1]=true;}
    if (btn2Pressed) {beep(25); lit[2]=true;}
    if (btn3Pressed) {beep(150); lit[3]=true;}
    if (btn4Pressed) {clearDisp();}

    refreshDisp();
    delay(1);
  } 
}


void myApp2() {  // a proposed typical framework .....
  while(true) {
    scanBtns();

    if (BtnPressed) {
      clearDisp();
      for (int i=1; i<=nleds; i++) {  // set all LEDs to a given level
        if (btn1Pressed) dim[i]=true;
        if (btn2Pressed) lit[i]=true;
        if (btn3Pressed) brt[i]=true;        
      }
    }
    //if (btn4Pressed) {clearDisp();}   // not needed

    // produce sound while btn1-3 is held
    while (Btn) {
      if (btn1 || btn3) boop(5);
      if (btn2 || btn3) beep(5);
      scanBtns();
      //refreshDisp();
    }

    refreshDisp();
    delay(1);
  } 
}


// ============================= Support Functions =====================

// ------------------------------------------------------------
//        Display Output Processing

// ------------------
void clearDisp() {
    litLED=0;
    brightOne = 0;
    for (int i=0; i<=nleds; i++) {
      dim[i] =false;
      lit[i]=false;
      brt[i]=false;
    }
    refreshDisp();
}

//  ---------------------  Display Update  - Should to be called ~ every One-two Milli-Seconds
void refreshDisp() {
  bool on[13];
  int ledn, Lite;
  unsigned int ledTime;
  int i, n;
  
  msCnt++;
  for (i=1; i<=nleds; i++) on[i]=0; // init working array

  // -------------- determine which Leds its time to illuminate
  ledTime = msCnt;
  if (ledTime%13 == 0) {                // 1/x cadence time to show 'dim'
    for (i=1; i<=nleds; i++) {on[i]=dim[i];}
  } else if (ledTime%3 == 1) {          // 33% of the time process 'lit'
    for (i=1; i<=nleds; i++) on[i]=lit[i];
    on[litLED] = true;
  }

  for (i=1; i<=nleds; i++) on[i]|=brt[i];
  on[brightOne] = true;
  
  // -------------- drive the resulting Leds of interest
  for (i=0; i<nleds; i++) {
    ledn = i+1;
    Lite = lites[i];
    digitalWrite(Lite, (on[ledn]==ON_STATE));
  }
}

// ------------------------------------------------------------
//        Button Input Processing

void scanBtns() {
  static byte btnState = 0, currState, priorBtn;
  static int escCnt=0;
  bool b1,b2,b3,b4;
  byte i,k;

  BtnChanged = btn1Changed = btn2Changed = btn3Changed = btn4Changed = false;
  if (btn4) escCnt++;
  else escCnt=0;
  ESC = (escCnt>1800);  // takes <2secs given Btns check ~ every msec

  b1 = ! digitalRead(button[0]);
  b2 = ! digitalRead(button[1]);
  b3 = ! digitalRead(button[2]);
  b4 = ! digitalRead(button[3]);
  Btn = b1 || b2 || b3 || b4;

//    if (debugPrt)  {Serial.print("Btns:  ");  Serial.print(b1); Serial.print(b2); Serial.print(b3); Serial.print(b4); }
  currState =  (b1)? 1 : ((b2)? 2 : ((b3)? 3 : ((b4)? 4 : 0)));  // logical button # (1-4) (multiple presses not detected)
  
  if (currState != btnState) {  // the 'button state' has changed
    if (debugPrt)  {Serial.print("CurrState Btn State:  ");  Serial.println(currState);}
    priorBtn = btnState;
    btnState = currState;
    BtnChanged=true;

    Btn = b1 || b2 || b3 || b4;
    if(b1 != btn1) {btn1 = b1; btn1Changed = true;}
    if(b2 != btn2) {btn2 = b2; btn2Changed = true;}
    if(b3 != btn3) {btn3 = b3; btn3Changed = true;}
    if(b4 != btn4) {btn4 = b4; btn4Changed = true;}
  }
}

// -----------------------
void refreshWait(int msec) {
  while (msec>0) {
    refreshDisp();
    delay(1);
    msec--;
  }
}
// ------------------------------------------------------------
//        Audio support

void beep(int msecs){   // works for both Buzzers & Speakers
  for (int i=0; i<msecs; i++) {         // loop for so many 'msecs'
    digitalWrite ( AUDIO_OUT, HIGH); 
    delayMicroseconds(500);    
    digitalWrite ( AUDIO_OUT, LOW);
    delayMicroseconds(500);
  }
  //delay(msecs);          // pause for the same # of 'msecs'   
}

void boop(int msecs){   // produces a lower frequency tone then does 'beep()'
  for (int i=0; i<((msecs+3)/4); i++) {         // loop for 'msecs' miliseconds
    digitalWrite ( AUDIO_OUT, HIGH); 
    delay(2);    
    digitalWrite ( AUDIO_OUT, LOW);
    delay(2);
  }
  //delay(msecs/2);          // pause for 1/2 as long   
}
