 void motorRightEncoderInterruptHandler();
 void updateClient();
 
-
 /////////////////////
 // Pin Definitions //
 /////////////////////

  Serial.println(AP_Name + " " + WiFi.softAPmacAddress());
  Serial.print(F("\r\n  Your password is "));
  Serial.println(password);
  Serial.println(F("\r\n  Type FHTbot.com into your browser after you connect. \r\n"));
  SPIFFS.begin();
  delay(200);
  #ifndef Diag
    Serial.end();                   // disable serial interface
  #endif
  pingSetup();
  strip.Begin();
  strip.Show();
  smile();
  // setup motors and encoders
  attachInterrupt(motorLeftEncoder, motorLeftEncoderInterruptHandler , CHANGE);
  attachInterrupt(motorRightEncoder, motorRightEncoderInterruptHandler , CHANGE);
  motorControllerTicker.attach_ms(motors.updateFrequency, updateMotors);  // attach motor update timer
  tempTicker.attach_ms(200,updTemp);                                      // attach temperature sample timer
 #ifndef Diag
    pinMode(leftBumper, INPUT_PULLUP);
    pinMode(rightBumper, INPUT_PULLUP);
	// test for startup auto mode
	if (digitalRead(leftBumper) == LOW){// test for startup auto mode
		autoMode = true;
		motors.playCharge();
		autoModeNextUpdate = millis()+300;
		autoModeNextEvent = millis() + random(10000,30000);
	};
    attachInterrupt(leftBumper, leftBumperHitFunction , FALLING);
    attachInterrupt(rightBumper, rightBumperHitFunction , FALLING);
 #endif
}
void leftBumperHitFunction(){
  if (!leftBumperHit){
    lBH.once_ms(500,leftBumperReset);
    //setColor(RgbColor(255,0,0));
  }
  leftBumperHit = true;
}
void leftBumperReset(){
  leftBumperHit = false;
}
void rightBumperHitFunction(){
  if (!rightBumperHit){
    rBH.once_ms(500,rightBumperReset);
    //setColor(RgbColor(255,0,0));
  }
  rightBumperHit = true;
}
void rightBumperReset(){
  rightBumperHit = false;
}

void motorLeftEncoderInterruptHandler(){
  motors.takeStep(0);
}
void motorRightEncoderInterruptHandler(){
 motors.takeStep(1);
}

void updateMotors(){
  motors.update();
}

void sendFile(String path){
// get content type
if(path.endsWith("/")){ path += "index.html";}
String dataType = getContentType(path);

// check if theres a .gz'd version and send that instead
String gzPath = path + ".gz";
File theBuffer;
if (SPIFFS.exists(gzPath)){             // test to see if there is a .gz version of the file
  theBuffer = SPIFFS.open(gzPath, "r");
  path = gzPath;                        // got it, use this path
}else{                                  // not here so load the standard file
  theBuffer = SPIFFS.open(path, "r");
  if (!theBuffer){                      // this one doesn't exist either, abort.
    //Serial.println(path + "Does Not Exist");
    theBuffer.close();
    String notFound = F("HTTP/1.1 404 Not Found\r\nConnection: Close\r\ncontent-length: 0\r\n\r\n");
    serverClients[currentClient].write( notFound.c_str(),notFound.length() );
    //serverClients[currentClient].write( closeConnectionHeader.c_str(),closeConnectionHeader.length() );
    yield();
    return; // failed to read file
  }
}

// make header
String s = F("HTTP/1.1 200 OK\r\ncache-control: max-age = 3600\r\ncontent-length: ");
    s += theBuffer.size();
    s += F("\r\ncontent-type: ");
    s += dataType;
    s += F("\r\nconnection: close"); // last one added X-Content-Type-Options: nosniff\r\n
  if (path.endsWith(".gz")){
    s += F("\r\nContent-Encoding: gzip\r\n\r\n");
  }else{
    s += F("\r\n\r\n");
  }
     // send the file
  if( !serverClients[currentClient].write(s.c_str(),s.length()) ){
    // failed to send
    theBuffer.close();
    return;
  }
  int bufferLength = theBuffer.size();
  if ( serverClients[currentClient].write(theBuffer,2920) <  bufferLength){
    // failed to send all file
  }
  yield();
  theBuffer.close();
}

String getContentType(String path){ // get content type
String dataType = F("text/html");
String lowerPath = path.substring(path.length()-4,path.length());
lowerPath.toLowerCase();

if(lowerPath.endsWith(".src")) lowerPath = lowerPath.substring(0, path.lastIndexOf("."));
else if(lowerPath.endsWith(".gz")) dataType = F("application/x-gzip");
else if(lowerPath.endsWith(".html")) dataType = F("text/html");
else if(lowerPath.endsWith(".htm")) dataType = F("text/html");
else if(lowerPath.endsWith(".png")) dataType = F("image/png");
else if(lowerPath.endsWith(".js")) dataType = F("application/javascript");
else if(lowerPath.endsWith(".css")) dataType = F("text/css");
else if(lowerPath.endsWith(".gif")) dataType = F("image/gif");
else if(lowerPath.endsWith(".jpg")) dataType = F("image/jpeg");
else if(lowerPath.endsWith(".ico")) dataType = F("image/x-icon");
else if(lowerPath.endsWith(".svg")) dataType = F("image/svg+xml");
else if(lowerPath.endsWith(".mp3")) dataType = F("audio/mpeg");
else if(lowerPath.endsWith(".wav")) dataType = F("audio/wav");
else if(lowerPath.endsWith(".ogg")) dataType = F("audio/ogg");
else if(lowerPath.endsWith(".xml")) dataType = F("text/xml");
else if(lowerPath.endsWith(".pdf")) dataType = F("application/x-pdf");
else if(lowerPath.endsWith(".zip")) dataType = F("application/x-zip");

return dataType;
}

void checkBoredBot(){
    if (millis() > nextBoredBotEvent){       // bored bot event called here
          nextBoredBotEvent = millis() + boredBotTimeout * 0.5; // reset bored bot timer
          // different events to be put here
          int events = 4;
          int pickedEvent = random(1,(events+1));
          switch(pickedEvent){

            case 1:                 // play vroom and bright light
              setColor(RgbColor(80,80,80));
              motors.playVroom();
            break;

            case 2:                 // random colors
            for (int a = 0; a < 50; a++){
              pixelTest();
              delay(20);
            }
            break;

            case 3:                 // blinker rotation
            for (int a = 0; a < 4; a++){
              delay(100);
              updateBlinkers(0, -1);
              delay(100);
              updateBlinkers(60, -1);
              delay(100);
              updateBlinkers(0, 1);
              delay(100);
              updateBlinkers(-60, -1);
            }
            break;

            case 4:                 // smile
              smile();
            break;
          }
  }
}

void updTemp(){
  updateTemperature();
}

void testBumper(){
    #ifndef Diag
    if (!leftBumperHit && rightBumperHit){
          motors.manualDrive(-250,500);
          motors.hardLeftTurn();
    }
    if (!rightBumperHit && leftBumperHit){
          motors.manualDrive(250,500);
          motors.hardRightTurn();                               // in reverse right becomes left
    }
     if (rightBumperHit && leftBumperHit){
          motors.manualDrive(0,500);
    }
     #endif
}

void checkAutoMode(){
	if (autoMode){
		HeartBeatRcvd = true;
		pingOn = true;
		driverAssist = true;
		int dX = -1;
		int dY = -500;
		if (millis() > autoModeNextUpdate){
			updateBlinkers(dX,dY);
			autoModeNextUpdate = millis() + 500;
			if (distance < 450 && distance > 199 && dY < 0){
			setColor(RgbColor(90,105,95));
			motors.hardRightTurn();
			dX = 500;
			dY = -100;
			autoModeNextUpdate = millis() + 2000;
			}
			if (distance < 200){
			setColor(RgbColor(255,0,0));
			dY = 500;
			autoModeNextUpdate = millis() + 1500;
			}
			motors.manualDrive(dX,dY);
		}
		if (millis() > autoModeNextEvent){
			autoModeNextEvent = millis() + random(10000,30000);
			setColor(RgbColor(30,105,35));
			motors.hardRightTurn();
			dX = 500;
			dY = -100;
			motors.manualDrive(dX,dY);
			autoModeNextUpdate = millis() + 3000;
			//String data = "data,L,120,";
			//motors.startCommandSet(data);
		}
	}
}
/**
 * Updates websocket client with distance/temperature information.
 */
void updateClient(){
  
  String s = "/T";
  s += getCurrentTemperature();
  s += ",";
  s += "/D";
  s += distance;
  webSocket.sendTXT(0,s);
  Serial.println("Return Message: " + s);
}


const int leftBumper = D9;
const int rightBumper = D10;

encoderMotorController motors(motorLeftA, motorLeftB, motorRightA, motorRightB,
                              motorLeftEncoder, motorRightEncoder);
Ticker motorControllerTicker;
Ticker tempTicker;
Ticker lBH; // left bumper hit reverse timer
Ticker rBH; // right bumper hit reverse timer
WiFiServer server(80);
WiFiClient client;
WebSocketsServer webSocket = WebSocketsServer(81);
