
// --------------------------------------------------------------------------------

void drawEyebrowLft() {
  // draw left eyebrow as a series of lines, moving down in Y
  // the target values become the new values
  int16_t zY = EbwY;
  for (int zP = 0;zP < 6;zP++) {
    tft.drawLine(eyeBwL[0],eyeBwL[1]+zY,eyeBwL[2],eyeBwL[3]+zY,ILI9341_BLACK); zY++;
  }
  zY = EbwY;
  for (int zP = 0;zP < 6;zP++) {
    tft.drawLine(eyeBtL[0],eyeBtL[1]+zY,eyeBtL[2],eyeBtL[3]+zY,faceCol); zY++;
  }
  eyeBwL[0] = eyeBtL[0]; eyeBwL[1] = eyeBtL[1]; eyeBwL[2] = eyeBtL[2]; eyeBwL[3] = eyeBtL[3];
}

// --------------------------------------------------------------------------------

void drawEyebrowRht() {
  // draw right eyebrow as a series of lines, moving down in Y
  // the target values become the new values
  int zY = EbwY;
  for (int zP = 0;zP < 6;zP++) {
    tft.drawLine(eyeBwR[0],eyeBwR[1]+zY,eyeBwR[2],eyeBwR[3]+zY,ILI9341_BLACK); zY++;
  }
  zY = EbwY;
  for (int zP = 0;zP < 6;zP++) {
    tft.drawLine(eyeBtR[0],eyeBtR[1]+zY,eyeBtR[2],eyeBtR[3]+zY,faceCol); zY++;
  }
  eyeBwR[0] = eyeBtR[0]; eyeBwR[1] = eyeBtR[1]; eyeBwR[2] = eyeBtR[2]; eyeBwR[3] = eyeBtR[3];
}

// --------------------------------------------------------------------------------

void drawFace() {
  // immediately draws the eyes and mouth based on parameters
  drawEyebrowLft(); // draw left eyebrow
  drawEyebrowRht(); // draw right eyebrow

  // draw left eye
  if (Once) {tft.fillRoundRect(sktLftX,sktY,eye_w,eye_h,eye_rad,faceCol);}     // draw the back of the eye
  drawLftEye(); // draw the left eyeball

  // draw right eye
  if (Once) {tft.fillRoundRect(sktRhtX,sktY,eye_w,eye_h,eye_rad,faceCol);}     // draw the back of the eye
  drawRhtEye(); // draw the right eyeball

  drawMouth();    // draw the mouth using target values
}

// --------------------------------------------------------------------------------

void drawLftEye() {
  // draw the left eyeball based on offsets Ox,Oy
  if (!Once) {tft.fillCircle(eyeLft[0],eyeLft[1],eyeLft[2],faceCol);}  // erase the previous eye
  eyeLft[0] = sktLftX+(eye_w/2)+eye_Ox; eyeLft[1] = sktY+(eye_h/2)+eye_Oy;
  if (!SLEEP) {
    // not in sleep mode so draw normal round eyes
    if (RangeEn) {eyeLft[3] = ILI9341_RED;} else {eyeLft[3] = ILI9341_BLUE;} eyeLft[2] = eye_rad;
    tft.fillCircle(eyeLft[0],eyeLft[1],eyeLft[2],eyeLft[3]);  // draw the left eyeball
    tft.fillCircle(eyeLft[0]+(eye_Ox/2),eyeLft[1]+(eye_Oy/2),eyeLft[2]/3,ILI9341_WHITE);  // draw the left pupil
  } else {
    // in SLEEP mode so draw flat closed eyes
    tft.fillRoundRect(eyeLft[0] - eyeLft[2],eyeLft[1],eyeLft[2] + eyeLft[2],eyeLft[2]/4,4,tft.color565(64,64,64));
  }
}

// --------------------------------------------------------------------------------

void drawMouth() {
  // draws a series of filled rectangles, as teeth, using the target Y offsets, +ve
  // for up, -ve for down, and teeth heights
  uint16_t zW = 16;
  uint16_t zX = ctr_X - ((zW + 4) * 4);
  for (int zT = 0;zT < 8;zT++) {
    if ((mthVert[zT] != mthVtgt[zT]) || (mthHght[zT] != mthHtgt[zT])){
      tft.fillRect(zX,mouthY+mthVert[zT],zW,mthHght[zT],ILI9341_BLACK); // remove the previous tooth
      tft.fillRect(zX,mouthY+mthVtgt[zT],zW,mthHtgt[zT],faceCol);       // add the new tooth
      mthHght[zT] = mthHtgt[zT]; mthVert[zT] = mthVtgt[zT];             // store new values
    }
    zX+= (zW + 4);
  }
}

// --------------------------------------------------------------------------------

void drawRhtEye() {
  // draw the right eyeball from passed parameters
  if (!Once) {tft.fillCircle(eyeRht[0],eyeRht[1],eyeRht[2],faceCol);}  // erase the previous eye
  eyeRht[0] = sktRhtX+(eye_w/2)+eye_Ox; eyeRht[1] = sktY+(eye_h/2)+eye_Oy;
  if (!SLEEP) {
    // not in sleep mode so draw normal round eyes
    if (RangeEn) {eyeRht[3] = ILI9341_RED;} else {eyeRht[3] = ILI9341_BLUE;} eyeRht[2] = eye_rad;
    tft.fillCircle(eyeRht[0],eyeRht[1],eyeRht[2],eyeRht[3]);  // draw the right eyeball
    tft.fillCircle(eyeRht[0]+(eye_Ox/2),eyeRht[1]+(eye_Oy/2),eyeRht[2]/3,ILI9341_WHITE);  // draw the right pupil
  } else {
    // in SLEEP mode so draw flat closed eyes
    tft.fillRoundRect(eyeRht[0] - eyeRht[2],eyeRht[1],eyeRht[2] + eyeRht[2],eyeRht[2]/4,4,tft.color565(64,64,64));
  }
}

// --------------------------------------------------------------------------------

void initFace() {
  // called to set face constants and fill out the arrays
  EbwY = 20;      // centre Y for eyebrows
  eye_h = 80;     // height of an eye socket
  eye_Ox = 0;     // eyeball X offset from socket centre
  eye_Oy = 0;     // eyeball Y offset from socket centre
  eye_rad = 24;   // eye radius
  eye_s2 = 20;    // eye separation / 2
  eye_w = 80;     // width of an eye socket
  mouthY = 180;   // vertical position of mouth
  sktY = 60;      // top of eye socket

  eye_s2 = 9;     // eye separation /2
  faceCol = tft.color565(255,255,0); // face drawing colour is yellow
  Once = true;    // if true don't refresh eyes
  sktLftX = ctr_X - eye_w - eye_s2; // eye socket left X
  sktRhtX = ctr_X + eye_s2;         // eye socket right X

  // create the default target arrays
  setBrows(2);
}

// --------------------------------------------------------------------------------

void setBrows(uint16_t zB) {
  // sets a specific eyebrow style
  switch(zB) {
    case 0: // slope down centre max
      seteyeBtL(sktLftX,-12,sktLftX + eye_w,  6); seteyeBtR(sktRhtX,  6,sktRhtX + eye_w,-12); break;
    case 1: // slope down centre mid
      seteyeBtL(sktLftX, -8,sktLftX + eye_w,  6); seteyeBtR(sktRhtX,  6,sktRhtX + eye_w, -8); break;
    case 2: // slow down centre min
      seteyeBtL(sktLftX, -4,sktLftX + eye_w,  6); seteyeBtR(sktRhtX,  6,sktRhtX + eye_w, -4); break;
    case 3: // flat centre
      seteyeBtL(sktLftX,  0,sktLftX + eye_w,  0); seteyeBtR(sktRhtX,  0,sktRhtX + eye_w,  0); break;
    case 4: // slope up centre min
      seteyeBtL(sktLftX, -4,sktLftX + eye_w,-14); seteyeBtR(sktRhtX,-14,sktRhtX + eye_w, -4); break;
    case 5: // slow up centre mid
      seteyeBtL(sktLftX, -4,sktLftX + eye_w,-18); seteyeBtR(sktRhtX,-18,sktRhtX + eye_w, -4); break;
    case 6: // slow up centre max
      seteyeBtL(sktLftX, -4,sktLftX + eye_w,-24); seteyeBtR(sktRhtX,-24,sktRhtX + eye_w, -4); break;
  }
}

// --------------------------------------------------------------------------------

void setBrowsRnd() {
  // select random eyebrows
  setBrows(random(7));
}

// --------------------------------------------------------------------------------

void seteyeBtL(int16_t zX0,int16_t zY0,int16_t zX1,int16_t zY1) {
  // puts the values into the array
  eyeBtL[0] = zX0; eyeBtL[1] = EbwY + zY0; eyeBtL[2] = zX1; eyeBtL[3] = EbwY + zY1;
}

// --------------------------------------------------------------------------------

void seteyeBtR(int16_t zX0,int16_t zY0,int16_t zX1,int16_t zY1) {
  // puts the values into the array
  eyeBtR[0] = zX0; eyeBtR[1] = EbwY + zY0; eyeBtR[2] = zX1; eyeBtR[3] = EbwY + zY1;
}

// --------------------------------------------------------------------------------

void setMouth(uint16_t zM) {
  // sets a specific mouth styles 0 - 9
  switch (zM) {
    case 0: // flat mouth, common teeth
      setMthVtgt(  0,  0,  0,  0,  0,  0,  0,  0);
      setMthHtgt( 20, 20, 20, 20, 20, 20, 20, 20); break;
    case 1: // flat mouth tapered ends up
      setMthVtgt( -8, -4, -1,  0,  0, -1, -4, -8);
      setMthHtgt(  5, 12, 18, 20, 20, 18, 12,  5); break;
    case 2:  // frowning max
      setMthVtgt(+20, +4, -4,-10,-10, -4,+4,+20);
      setMthHtgt(  5, 10, 15, 15, 15, 15, 10,  5); break;
    case 3:  // frowning min
      setMthVtgt(+10, +2, -2, -5, -5, -2,+2,+10);
      setMthHtgt(  5, 10, 15, 15, 15, 15, 10,  5); break;
    case 4: // smiling max
      setMthVtgt(-14,  2, 12, 16, 16, 12,  2,-14);
      setMthHtgt(  5, 10, 20, 20, 20, 20, 10,  5); break;
    case 5: // smiling min
      setMthVtgt( -7,  1,  6,  8,  8,  6,  1, -7);
      setMthHtgt(  5, 10, 20, 20, 20, 20, 10,  5); break;
    case 6: // turned down left max
      setMthVtgt(+30,+14, +4,  0,  0,  0,  0,  0);
      setMthHtgt(  5, 10, 15, 14, 12, 10,  8,  6); break;
    case 7: // turned down left min
      setMthVtgt(+15, +7, +2,  0,  0,  0,  0,  0);
      setMthHtgt(  5, 10, 15, 14, 12, 10,  8,  6); break;
    case 8: // turned down right max
      setMthVtgt(  0,  0,  0,  0,  0, +4,+14,+30);
      setMthHtgt(  6,  8, 10, 12, 14, 15, 10,  5); break;
    case 9: // turned down right min
      setMthVtgt(  0,  0,  0,  0,  0, +2, +7,+15);
      setMthHtgt(  6,  8, 10, 12, 14, 15, 10,  5); break;
    case 10: // flat mouth tapered ends down
      setMthVtgt(  8,  4,  1,  0,  0,  1,  4,  8);
      setMthHtgt(  5, 12, 18, 20, 20, 18, 12,  5); break;
  }
}

// --------------------------------------------------------------------------------

void setMouthRnd() {
  // selects a random mouth styles
  setMouth(random(6));
}

// --------------------------------------------------------------------------------

void setMthHtgt(int16_t z0,int16_t z1,int16_t z2,int16_t z3,int16_t z4,int16_t z5,int16_t z6,int16_t z7) {
  // set the target values of the mouth teeth heights
  mthHtgt[0] = z0; mthHtgt[1] = z1; mthHtgt[2] = z2; mthHtgt[3] = z3;
  mthHtgt[4] = z4; mthHtgt[5] = z5; mthHtgt[6] = z6; mthHtgt[7] = z7;
}

// --------------------------------------------------------------------------------

void setMthVtgt(int16_t z0,int16_t z1,int16_t z2,int16_t z3,int16_t z4,int16_t z5,int16_t z6,int16_t z7) {
  // set the target values of the mouth vertical offset array
  mthVtgt[0] = z0; mthVtgt[1] = z1; mthVtgt[2] = z2; mthVtgt[3] = z3;
  mthVtgt[4] = z4; mthVtgt[5] = z5; mthVtgt[6] = z6; mthVtgt[7] = z7;
}

// --------------------------------------------------------------------------------
