/************************************************************************************
  Author   : MsH
  Version  : 0.5 (WIP)

  This project was made in purpose of exploring Adafruit libraries
  Feel free to cherrypick my logics if you find them useful

  Made in Wokwi.com
************************************************************************************/
//#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


/************************************************************************************
 * Neopixel
 ***********************************************************************************/
const uint8_t LED_STRIP = 13;
const uint16_t LED_COUNT = 45;

Adafruit_NeoPixel NeoPixel(LED_COUNT, LED_STRIP, NEO_GRB + NEO_KHZ800);

// Pixels close to oled screen are meant to preview colors
Adafruit_NeoPixel ColorPreview(2, 14, NEO_GRB + NEO_KHZ800);

// analogs
const uint8_t
rgb        = 27,
brightness = 26,
white      = 25;

const uint8_t analogCount = 3;
uint8_t analogs[analogCount] = {rgb, brightness, white};

const uint16_t colorValsTotal = 256; // color has 256 different values (0-255)

enum values {
  R = 0, 
  G = 1,
  B = 2, 
  W = 3, 
  RGB = 4, 
  Brightness = 5, 
  lastW = 6,
  lastRGB = 7, 
  lastBrightness = 8 
};

// Color arrays
const uint8_t valCount = 9;
uint16_t vals[valCount]; // for looping purposes
// Color save arrays
uint16_t color1[valCount], color2[valCount], 
         color3[valCount], color4[valCount]; 
// Array of saves
uint16_t colors[4][valCount] = {
  color1[valCount], color2[valCount], 
  color3[valCount], color4[valCount]};

// Led settings
enum options {
  Speed = 0, 
  Intensity = 1,
  WaveAmount = 2, 
  WaveLenght = 3, 
  WaveDirection = 4
};

const uint8_t settingsCount = 5; 
int16_t settings[settingsCount] = {1, 40, 3, 3, true};

// Rainbows / RGB
bool readAnalogRGB = false;
uint16_t RGBval = 255;        // rgb starting value
uint16_t rainbowHue;
uint16_t rainbowValues[3];

// Pulse
uint8_t pulseMinBrightness;
uint8_t pulseVal;
bool addPulseVal;

// Wave
int32_t selectedPixel;

// Fade
uint8_t targetColor;
uint16_t fadeCurrent[6] = {0,0,0,0,256,0};
bool directionRight;


/************************************************************************************
 * Buttons   // 4, 5, 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 35
 ***********************************************************************************/
// Button pins
const uint8_t
btn0 = 18,   
btn1 = 19,
btn2 = 23;

// Button variables
const uint8_t buttonCount = 7;
const uint8_t buttons[buttonCount] = {btn0, btn1, btn2};
byte buttonBytes[buttonCount];
byte pressedButtons;

// Arrays for togglebutton logic
bool buttonLastStates[buttonCount];
bool buttonStates[buttonCount];

/************************************************************************************
 * OLED
 ***********************************************************************************/
// pins
const uint8_t OLED_RESET = 22;

Adafruit_SSD1306 display(128,64, &Wire, OLED_RESET);

// OLED refresh
bool userDetected; // used to detect user input
bool valueChanged; // used to detect current value changed (user or esp)
bool notRefreshed; // used to detect if refreshed by userinput or onValueChange

// OLED sleep mode
bool sleeping = false;
unsigned long lastActivity;
uint16_t timeOut = 30000; // milliseconds before going sleep

/************************************************************************************
 * UI / navigation
 ***********************************************************************************/
uint16_t spacer, offset, marginT, marginL;

// menuitems
uint8_t menu;
const uint8_t colorItemCount = 4;            
String colorItems[colorItemCount] = {"Color 1", "Color 2", "Color 3", "Color 4"};
const uint8_t modeItemCount = 8;  
String modeItems[modeItemCount] = {
  "Rainbow","RGB Fade", "RGB Wave", "Static", 
  "Fade", "Wave", "Pulse", "Sparkle"};
int8_t n, ledMode;

// mode options
const uint8_t optionsCount = 6;
String options[optionsCount] = {"Return", "Speed", "Wavelenght", "Wave amount", "Direction", "Intensity"};    
int16_t opt, setting, minSetting, maxSetting;
uint8_t maxOpt = optionsCount-1;

enum menus {
 Main = 0,
 Colors = 1,
 Modes = 2,
 Settings = 3,
 AdjustSetting = 4
};

/************************************************************************************
 * Icons
 ***********************************************************************************/
// light_9x9, 9x9 px
const unsigned char light_9x9 [] PROGMEM = { 0x08, 0x00, 0x49, 0x00, 0x22, 0x00, 0x1c, 0x00, 0xdd, 0x80, 0x1c, 0x00, 0x22, 0x00, 0x49, 0x00, 0x08, 0x00};
// arrow_5x7, 5x7 px
const unsigned char arrow_5x7 [] PROGMEM = {0x18, 0x38, 0x78, 0xf8, 0x78, 0x38, 0x18};

// Pigeon  design Copyright (c) MsH. All rights reserved.
// pigeon_128x64, 128x64px                                                                   
const unsigned char pigeon_128x64 [] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x07, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x07, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x07, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x0f, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xec, 0x0f, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xee, 0x0f, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7, 0x0f, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf3, 0x0f, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf9, 0xcf, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xef, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x77, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x3b, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x1c, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x8f, 0x71, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc7, 0xb9, 0x8f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xe3, 0xdf, 0x9f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xe1, 0xef, 0xdf, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0xf7, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf8, 0xf7, 0xbf, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x7f, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x78, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0x79, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf9, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x03, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xdf, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

/************************************************************************************
 * Setup & mainloop
 ***********************************************************************************/

void setup() {

  Serial.begin(9600);

  // OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.setTextColor(WHITE);
  
  display.display();    // Draw adafruit logo
  delay(1000);          // Wait a bit
  drawLogo();           // Draw my logo
 
  // Buttons
  for ( uint8_t i = 0; i < buttonCount; i++ ) {
    pinMode(buttons[i], INPUT_PULLUP);

    buttonLastStates[i] = HIGH;
    buttonStates[i]     = HIGH;
    buttonBytes[i]      = 0b1 << i;
  }

  // Analogs
  analogSetWidth(11);
  analogReadResolution(11); // Use 0 - 2047 (11 bits) values 
                            // to get all RGB values from one analog

  for ( uint8_t i = 0; i < analogCount; i++ ) {
    pinMode(analogs[i], INPUT);
  }

  // Ledstrip
  NeoPixel.begin();
  ColorPreview.begin();
}

void loop() {

  // Display is not refreshed yet during this loop
  notRefreshed = true;

  // Check for user inputs
  userDetected = false;
  valueChanged = false;
  readButtons();
  readAnalogs();

  // Button functions
  for ( uint16_t i = 0; i < buttonCount; i++ ) {
    buttonLastStates[i] = buttonStates[i];           // Save current to last state
    buttonStates[i]     = !digitalRead(buttons[i]);  // Read new state to current

    // Do once
    if (buttonLastStates[i] == LOW && buttonStates[i] == HIGH) {
      userDetected = true;

      if (sleeping == false) {
        valueChanged = true;
        if (i == 0) buttonFunction1();
        if (i == 1) buttonFunction2();
        if (i == 2) buttonFunction3();
      }
    }
  }

  // If user touched inputs
  if (userDetected) {
    if (sleeping) {
      Serial.println("Activity detected -> Display ON");
      lastActivity = millis();
      sleeping = false;
      display.ssd1306_command(SSD1306_DISPLAYON);
    }
    else {
      Serial.println("Activity detected");
      lastActivity = millis();
      // Refresh display on user input
      updateDisplay();
      notRefreshed = false;
    }
  }
  else {
    // Turn off display on inactivity
    checkActivity();
  }

  if (valueChanged) getColorValues(vals[RGB], vals, true);

  if (menu == Colors) {
    if (valueChanged && sleeping == false) {
      ColorPreview.setPixelColor(0, ColorPreview.Color(vals[R], vals[G], vals[B]));
      ColorPreview.setPixelColor(1, ColorPreview.Color(colors[n][R], 
                                                       colors[n][G], 
                                                       colors[n][B]));
      ColorPreview.show();
    }
  }  

  // Do things to led strip
  switch(ledMode)
  {
  case 1:
    ledRGBfade();
    break;
  case 2:
    ledRGBwave();
    break;
  case 3:
    ledStatic();
    break;
  case 4:
    ledFade();  
    break;
  case 5:
    ledWave();
    break;
  case 6:
    ledPulse();
    break;
  case 7:
    ledSparkle();
    break;
  default:
    ledRainbow();
    break;
  }
  NeoPixel.show();


  // Refresh display on valueChange when in colors menu
  if (valueChanged && menu == Colors && notRefreshed && sleeping == false)
    updateDisplay();

  delay(10);
}


/************************************************************************************
 * Analog Functions
 ***********************************************************************************/

// Reads all analog values
void readAnalogs() {

  if (menu == Colors || ledMode == 3 /*ledStatic*/) {
    vals[RGB] = analogRead(rgb);
  }
  vals[W] = map(analogRead(white), 0, 2047, 0, 255);                // map to 0 - 255
  vals[Brightness] = map(analogRead(brightness), 0, 2047, 0, 100);  // 0 - 100 %

  // Looping lastValues (Check enums) (3 loops)
  for ( uint8_t i = lastW; i < lastW + 3; i++ ) {
    uint8_t current = i - 3;

    // Compare current to last value
    if ( vals[current] != vals[i] ) {
      vals[i] = vals[current];
      valueChanged = true;
      if (sleeping == false) userDetected = true;
    }
  }

}

// Converts analog range of 0 - 2023 to hue and saves them into red green and blue 
// (actual range is 255-1791)
void getColorValues(uint16_t value, uint16_t color[], bool refreshScreen) {
  // If wanted, refresh screen
  //if (refreshScreen) valueChanged = true;

  // Deadzones both ends -> red color
  if (value < 256 || value >= 1792) {
    color[R] = 255;
    color[G] = 0;
    color[B] = 0;
  }

  // Shift to orange to yellow
  else if (value < 512) {
    color[R] = 255;

    if (color[G] != value - (512 + colorValsTotal)) {   // Rising
      color[G] = value - (512 - colorValsTotal);
    }

    color[B] = 0;
  }

  // Shift to green
  else if (value < 768) {

    if (color[R] != 768 - value) {   // Falling
      color[R] = 768 - value;
    }

    color[G] = 255; 
    color[B] = 0;
  }

  // Shift to turqoise to aqua
  else if (value < 1024) {
    color[R] = 0; 
    color[G] = 255; 

    if (color[B] != value - (1024 + colorValsTotal)) {  // Rising
      color[B] = value - (1024 - colorValsTotal);
    }
  }

  // Shift to blue
  else if (value < 1280) {
    color[R] = 0;
    
    if (color[G] != 1280 - value) {  // Falling
      color[G] = 1280 - value;
    }

    color[B] = 255;
  }

  // Shift to purple to pink
  else if (value < 1536) {

    if (color[R] != value - (1536 + colorValsTotal)) {  // Rising
      color[R] = value - (1536 - colorValsTotal);
    }

    color[G] = 0;
    color[B] = 255;
  }

  // Shift to red
  else if (value < 1792) {
    color[R] = 255;
    color[G] = 0;

    if (color[B] != 1792 - value) {  // Falling
      color[B] = 1792 - value;
    }
  }


  // Converts white and brightness values to hue
  for (uint8_t i = 0; i < 3; i++) {

    // Set new value with white level
    if (color[i] < vals[W]) {
      color[i] = constrain(vals[W], 0, 255);
    }

    // Set new value with brightness level
    color[i] = constrain((color[i] * vals[Brightness]) / 100, 0, 255);
  }

}


/************************************************************************************
 * Button Functions
 ***********************************************************************************/

// Read all buttons and save pressedButton
void readButtons() {
  pressedButtons = 0;

  for ( uint8_t i = 0; i < buttonCount; i++ ) {
    if ( !digitalRead(buttons[i]) == HIGH ){
      pressedButtons = pressedButtons | buttonBytes[i];
    }
  }
}

// Change item up / down depending on menu
void buttonFunction1() {
  switch(menu)
  {
  case Colors:
    n--; if ( n < 0 ) n = colorItemCount;
    break;

  case Modes:
    ledMode++; if ( ledMode > modeItemCount-1 ) ledMode = 0;
    break;

  case Settings:
    opt++; if ( opt > maxOpt ) opt = 0;
    break;
  
  case AdjustSetting:
    if (settings[setting] != minSetting) settings[setting]--;
    break;

  default:
    menu = Colors;
    break;
  }
}

// Confirm/select button
void buttonFunction2() {
  switch(menu)
  {
  case Colors:
    if (n > colorItemCount-1) {  // return
      n = 0; 
      menu = Main; 
      ColorPreview.clear();
      ColorPreview.show();
    } 
    else saveColor(colors[n]);   // colors[n] exists -> save color  
    break;

  case Modes: // Change LED mode
    menu = Main;
    break;
  
  case Settings:
    switch(opt)
    {
    case 1:
      setting = Speed;
      minSetting = 1;
      maxSetting = 10; 
      menu = AdjustSetting;
      break;

    case 2:
      setting = WaveLenght;
      minSetting = 1;
      maxSetting = LED_COUNT / settings[WaveAmount];; 
      menu = AdjustSetting;
      break;

    case 3:
      setting = WaveAmount;
      minSetting = 1;
      maxSetting = LED_COUNT / settings[WaveLenght]; 
      menu = AdjustSetting;
      break;

    case 4:
      setting = WaveDirection;
      minSetting = 0;
      maxSetting = 1; 
      menu = AdjustSetting;
      break;
      
    case 5:
      setting = Intensity;
      minSetting = 20;
      maxSetting = 60; 
      menu = AdjustSetting;
      break;

    default:
      menu = Main;
      break;
    }
    break;
  
  case AdjustSetting:
    menu = Settings;
    break;

  default:
    menu = Modes;
    break;
  }
}

// Change item up / down depending on menu
void buttonFunction3() {
  switch(menu)
  {
  case Colors:
    n++; if ( n > colorItemCount ) n = 0;
    break;

  case Modes:
    ledMode--; if ( ledMode < 0 ) ledMode = modeItemCount-1;
    break;

  case Settings:
    opt--; if ( opt < 0 ) opt = maxOpt;
    break;

  case AdjustSetting:
    if (settings[setting] != maxSetting) settings[setting]++;
    break;

  default:
    menu = Settings;
    break;
  }
}


/************************************************************************************
 * OLED / Navigation
 ***********************************************************************************/

// OLED updating
void updateDisplay() {

  display.clearDisplay();

  switch (menu)
  {
  case Colors:
    drawRGBValues(10, 0, 44, 55, "Current", vals);

    if (n > colorItemCount-1) { 
      display.setCursor(73, 64/2-8);  
      display.println("Return"); 
    }
    else {
      drawRGBValues(73, 0, 44, 55, String(colorItems[n]), colors[n]);
    }

    drawRGBfooter(0, 59, 128, 5);
    break;

  case Modes:
    drawModeMenu(0,0);
    break;

  case Settings:
    drawModeOptions(0,0);
    break;

  case AdjustSetting:
    drawSettingAdjustment(0,0,settings[setting]);
    break;

  default:
    drawMainMenu(0,0);
    break;
  }

  display.display();
  Serial.println("Display refreshed");

}

// Sleepmode: read buttoninputs only with long delays
void checkActivity() {
  // Go sleep if time exeeds timeOut limit
  if ( sleeping == false && millis() - lastActivity > timeOut ) {
    sleeping = true;
    display.ssd1306_command(SSD1306_DISPLAYOFF);
    
    ColorPreview.clear();
    ColorPreview.show();
    Serial.println("Inactivity detected -> Display OFF");
  }
}


/************************************************************************************
 * Draw UI Elements
 ***********************************************************************************/

// mainmenu to start navigating settings
void drawMainMenu(int16_t x, int16_t y) {
  // Header
  offset = 5;
  display.setCursor(x+offset, y);  
  display.println("RGB strip controller");

  y += 10;                          
  display.drawFastHLine(x, y, 128, WHITE);

  // Menu items
  spacer = 14;
  y -= 5;

  offset = 27;        
  display.setCursor(x+offset, y+spacer*1);  
  display.println("Mode settings"); 

  offset = 33;             
  display.setCursor(x+offset, y+spacer*2);  
  display.println("Switch mode");

  offset = 23;     
  display.setCursor(x+offset, y+spacer*3);  
  display.println("Color settings");

}

// Draw modes items (origin = x,y)
void drawModeMenu(int16_t x, int16_t y) {
  ledMode;
  int8_t prev = ledMode-1; if (prev < 0) prev = modeItemCount-1;
  int8_t next = ledMode+1; if (next > modeItemCount-1) next = 0;

  int8_t prev2nd = ledMode-2; if (prev2nd < 0) prev2nd = modeItemCount-1;
  int8_t next2nd = ledMode+2; if (next2nd > modeItemCount-1) next2nd = 0;

  int8_t l;

  // Selected
  display.setTextSize(2);

  l = modeItems[ledMode].length()*12;
  offset = (128 - l) / 2;                  // Center x
  display.setCursor(x+offset, y+64/2-10);  // Center menuitem to 128x64 screen
  display.println(modeItems[ledMode]);

  // prev & next
  display.setTextSize(1);

  l = modeItems[prev].length()*6;
  offset = (128 - l) / 2;                  // Center x
  display.setCursor(x+offset, y+8);        // Top of screen
  display.println(modeItems[prev]);

  l = modeItems[next].length()*6;
  offset = (128 - l) / 2;                  // Center x
  display.setCursor(x+offset, y+64-20);    // Bottom of screen
  display.println(modeItems[next]);

  // draw 2nd prev and 2nd next (unreadable, so it's just a line)
  l = modeItems[prev2nd].length()*2;
  offset = (128 - l) / 2;                  // Center x
  display.drawFastHLine(x+offset, y, l, WHITE);

  l = modeItems[next2nd].length()*2;
  offset = (128 - l) / 2;                  // Center x
  display.drawFastHLine(x+offset, y+62, l, WHITE);

}

// Draw options to select
void drawModeOptions(int16_t x, int16_t y) {
  int8_t prev = opt-1; if (prev < 0) prev = maxOpt;
  int8_t next = opt+1; if (next > maxOpt) next = 0;
  offset = 5;
  spacer = 14;
  display.setTextSize(2);
  display.setCursor(x+offset, y);  
  display.println("Settings");
  y += 18;
  display.drawFastHLine(x, y, 128, WHITE);

  display.setTextSize(1);
  offset = 15;
  y -= 4;

  display.setCursor(x+offset, y+spacer*(1));
  display.println(String(options[prev]));
  display.setCursor(x+offset, y+spacer*(2));
  display.println(String(options[opt]));
  display.setCursor(x+offset, y+spacer*(3));
  display.println(String(options[next]));

  // Set Arrow cursor position
  display.drawBitmap(128-30, y+2*spacer, arrow_5x7, 5, 7, WHITE);

}

// Draw table of current values (origin = x,y)
void drawRGBValues(int16_t x, int16_t y, int16_t w, int16_t h,
                   String header, uint16_t color[]) 
  {
  // Header
  marginL = 1;
  marginT = 10;
  display.setCursor(x+marginL,y);
  display.println(header);
  display.drawFastHLine(x, y+marginT, w, WHITE);

  // RGB values
  marginL = 0;
  marginT = 4;
  spacer = 11;

  display.setTextSize(1);

  display.setCursor( x+marginL, marginT+spacer*1 );
  display.println("R:" + String(color[R]));

  display.setCursor( x+marginL, marginT+spacer*2 );
  display.println("G:" + String(color[G]));

  display.setCursor( x+marginL, marginT+spacer*3 );
  display.println("B:" + String(color[B])); 

  display.setCursor( x+marginL, marginT+spacer*4 );
  display.println("  " + String(color[Brightness]) + "%");
  display.drawBitmap(x+marginL, marginT+spacer*4-1, light_9x9, 9, 9, WHITE);

  // White level
  drawVerticalSlider(x+w-5, y+15, 5, 40, color[W], 0, 255, 2);

  // Divider line
  display.drawFastVLine(x+w+3, y, h, WHITE);
  display.drawFastVLine(x-4, y, h, WHITE);

}

// Draw vertical slot (origin = x,y)
void drawVerticalSlider(int16_t x, int16_t y, int16_t w, int16_t h, 
                        int16_t value, int16_t minVal, int16_t maxVal, uint8_t style)                   
  {
  offset = 1;
  int16_t xKnob = x + offset;
  int16_t yKnob = map(value, minVal, maxVal, y+offset, h+12-offset);
  int16_t wKnob = w-2;
  int16_t hKnob = 1;

  // Draw slot and knob depending on style
  if (style == 1) {
    display.drawFastVLine(x, y, h, WHITE);
    display.drawFastVLine(x+w-1, y, h, WHITE);
    display.fillRect( xKnob, yKnob, wKnob, hKnob, WHITE ); 
  }
  else if (style == 2) {
    display.drawFastVLine(x+w/2-1, y, h, WHITE);
    display.fillCircle(xKnob, yKnob, wKnob, WHITE);
  }
  else {
    Serial.println("drawVeticalSlider(): Style not found");
  }

}

// Draw arrows and selected option's value in mode settings
void drawSettingAdjustment(int16_t x, int16_t y, uint8_t value) {
  
  // Draw arrow up
  display.fillTriangle(x+63, y, x+59, y+4, x+67, y+4, WHITE);
  display.fillTriangle(x+62, y, x+58, y+4, x+66, y+4, WHITE);
  display.drawFastHLine(x+58,y+5,10,WHITE);

  // Center and draw value
  uint8_t l = String(value).length()*12;
  offset = (128 - l) / 2;                  // Center x
  display.setCursor(x+offset, y+32-7);

  display.setTextSize(2);

  if (opt == 4) {
    if (settings[WaveDirection]) {
      display.setCursor(x+35, y+32-7);
      display.println("Right");
    }
    else {
      display.setCursor(x+40, y+32-7);
      display.println("Left");
    }
  }
  else display.println(String(value));
  
  display.setTextSize(1);

  // Draw arrow down
  display.fillTriangle(x+63, y+63, x+59, y+59, x+67, y+59, WHITE);
  display.fillTriangle(x+62, y+63, x+58, y+59, x+66, y+59, WHITE);
  display.drawFastHLine(x+58,y+58,10,WHITE);
}

// Draw RGB slider footer
void drawRGBfooter(int16_t x, int16_t y, int16_t w, int16_t h) {
  // Draw line
  display.setCursor(x,y);
  display.drawFastHLine(x, y, w, WHITE);

  // Remove deadzones from both ends
  int16_t rgbNoDeadZone = constrain(vals[RGB], 255, 1792);

  // Draw slider knob
  int16_t xKnob = map(rgbNoDeadZone, 255, 1792, 1, 123);
  int16_t yKnob = y;
  int16_t wKnob = 3;
  int16_t hKnob = 5;

  display.fillRect( xKnob, yKnob, wKnob, hKnob, WHITE ); 

}


/************************************************************************************
 * LED modes
 ***********************************************************************************/

// Basic wavy rainbow shifts
void ledRainbow() {
  if (valueChanged) { // Rainbow (Static RGB)
    uint8_t b = map(vals[Brightness], 0, 100, 0, 255);
    NeoPixel.rainbow(0, settings[WaveAmount], 255-vals[W], b, true);
  }
}

// RGB colors shifting smoothly
void ledRGBfade() {
  RGBval++; if (RGBval > 1792) {RGBval = 255;}
  getColorValues(RGBval, rainbowValues, true);
  NeoPixel.fill(NeoPixel.Color(rainbowValues[R], 
                               rainbowValues[G], 
                               rainbowValues[B]), 0, LED_COUNT);
}

// RGB colors waving
void ledRGBwave() {
  rainbowHue -= 250;
  if ( rainbowHue >= 65535) rainbowHue = 0;  // Looped trough -> set next pixel as 0

  uint8_t b = map(vals[Brightness], 0, 100, 0, 255);
  NeoPixel.rainbow(rainbowHue, settings[WaveAmount], 255-vals[W], b, true);
}

// Use current values for static
void ledStatic() {
  if (valueChanged) {
    NeoPixel.fill(NeoPixel.Color(vals[R], vals[G], vals[B]), 0, LED_COUNT); 
  }
}

// LEDs fades between saved colors
void ledFade() {
  // Clamp target to range of 256-1792
  uint16_t target = constrain(colors[targetColor][RGB], 256, 1792);

  // Use shortest way to the target value
  if ( directionRight ) {
    fadeCurrent[RGB]++;
    if ( fadeCurrent[RGB] > 1792 ) fadeCurrent[RGB] = 256; 
  }
  else {
    fadeCurrent[RGB]--;
    if ( fadeCurrent[RGB] < 256 ) fadeCurrent[RGB] = 1792;
  }

  // Change target
  if ( fadeCurrent[RGB] == target ) {
    targetColor++; if ( targetColor > 3 ) targetColor = 0;

    // Calculate distance from current to target
    // going to right
    int16_t directionRight = (1792 - fadeCurrent[RGB]) + (target - 256);
    // going to left 
    int16_t directionLeft = fadeCurrent[RGB] - target;

    if ( directionRight < directionLeft ) directionRight = true;
    else directionRight = false;
  }

  getColorValues(fadeCurrent[RGB], fadeCurrent, false);  

  // Loop fadeCurrent R, G, B
  for (uint8_t i = 0; i < 3; i++) {
    // Set new values with target's white level
    if (fadeCurrent[i] < colors[targetColor][W]) {
      fadeCurrent[i]--;
    }
    if (fadeCurrent[i] > colors[targetColor][W]) {
      fadeCurrent[i]++;
    }

    // Set new values with target's brightness level
    if (fadeCurrent[Brightness] < colors[targetColor][Brightness]) {
      fadeCurrent[Brightness]++;
    }  
    if (fadeCurrent[Brightness] > colors[targetColor][Brightness]) {
      fadeCurrent[Brightness]--;
    }

    fadeCurrent[i] = constrain(fadeCurrent[i] * fadeCurrent[Brightness] / 100, 0, 255);
  }
  
  NeoPixel.fill(NeoPixel.Color(fadeCurrent[R], 
                               fadeCurrent[G], 
                               fadeCurrent[B]), 0, LED_COUNT);

  delay( 100 - settings[Speed]*10 );
}

// LEDs waves between saved colors
void ledWave() {
  // Find next pixel for wave
  selectedPixel++;
  // Looped trough -> set next pixel as 0
  if ( selectedPixel >= LED_COUNT) selectedPixel = 0;
  

  uint16_t pixel;
  // Offset waves depending on led amount and desired amount of waves of desired lenght
  uint16_t waveOffset = (LED_COUNT-settings[WaveLenght]*settings[WaveAmount])
                    / settings[WaveAmount];
  uint8_t waveColor = 1;

  // Generate diffrent colored waves at different position
  for (uint8_t wave = 0; wave < settings[WaveAmount]; wave++) {
    // Set new color for this wave iteration
    waveColor++; if (waveColor > 3) waveColor = 1;     

    // Loop pixels to light up with that color
    for (uint8_t i = 0; i < settings[WaveLenght]; i++) {
      // Find pixel to light up
      pixel = (selectedPixel + i) + (settings[WaveLenght]+waveOffset)*wave;
      if (pixel >= LED_COUNT) pixel -= LED_COUNT;

      NeoPixel.setPixelColor(pixel, NeoPixel.Color(colors[waveColor][R], 
                                                   colors[waveColor][G], 
                                                   colors[waveColor][B]));
    }

    // Find tailingPixel of this wave iteration
    int32_t tailingPixel = pixel-settings[WaveLenght]; // Tail is behind the wave
    if ( tailingPixel < 0 ) tailingPixel += LED_COUNT; // Tail is actually other end

    // Remove tailing pixel (Sets as backround color which is another saved color)
    NeoPixel.setPixelColor(tailingPixel, NeoPixel.Color(colors[0][R], 
                                                        colors[0][G],
                                                        colors[0][B]));
  }

  delay(100 - settings[Speed]*80 );
}

// LEDs pulses with given color
void ledPulse() {
  if ( addPulseVal ) {
    if ( pulseVal < vals[Brightness] ) {
      pulseVal += 2; if (pulseVal > 100) pulseVal = 100;
      NeoPixel.fill(NeoPixel.Color(vals[R]*pulseVal/100, 
                                   vals[G]*pulseVal/100, 
                                   vals[B]*pulseVal/100), 0, LED_COUNT);
    }
    else {
      pulseMinBrightness = random(constrain(vals[Brightness]-settings[Intensity],
                                            0, 
                                            vals[Brightness]-settings[Intensity]), 
                                  vals[Brightness]);

      addPulseVal = false;
    }
    delay(15-settings[Speed]);
  }

  else if ( pulseVal > pulseMinBrightness ) {
    pulseVal--;
    NeoPixel.fill(NeoPixel.Color(vals[R]*pulseVal/100, 
                                 vals[G]*pulseVal/100, 
                                 vals[B]*pulseVal/100), 0, LED_COUNT);
  }
  else {
    addPulseVal = true;
  }
}

// LEDs sparkles randomly with saved colors
void ledSparkle() {
  NeoPixel.clear();  // TODO
}


/************************************************************************************
 * Other
 ***********************************************************************************/

// Draw my logo
void drawLogo() {
  display.clearDisplay();
  display.drawBitmap(0, 0, pigeon_128x64, 128, 64, WHITE);
  display.display();
  delay(1000);
  updateDisplay();
}

// Save current vals to color n
void saveColor(uint16_t color[]) {
  for (uint8_t i = 0; i < valCount; i++){
    color[i] = vals[i];
  }
}