#include <EEPROM.h> // EEPROM 사용을 위한 라이브러리
#include <Wire.h> // i2C 통신을 위한 라이브러리
#include <LiquidCrystal_I2C.h> // LCD 2004 I2C용 라이브러리

int CLK = 2;        // 로터리 엔코더의 CLK를 연결한 핀
int DT = 3;         // 로터리 엔코더의 DT를 연결한 핀
int SW = 4;         // 로터리 엔코더의 SW를 연결한 핀

int RED_PIN = 5;    // 빨간색 LED 연결핀번호
int GREEN_PIN = 6;  // 초록색 LED 연결핀번호
int BLUE_PIN = 9;   // 파란색 LED 연결핀번호          

int led_onoff=1;

static int oldCLK = LOW;  // CLK 핀의 값을 저장하는 변수
static int oldDT = LOW;   // DT 핀의 값을 저장하는 변수

LiquidCrystal_I2C lcd(0x27,20,4); // 접근주소: 0x3F or 0x27

int RED,GREEN,BLUE;
int INTERVAL,DURATION;

int currb=HIGH,lastb=HIGH;

int mr,mg,mb,mi,md,mcur;

char buf[20];

int mode=0; // 0=normal, 1=setup
int menu=0; // 0=RED, 1=GREEN, 2=BLUE, 3=INTV, 4=DUR, 5=END

void setup()
{
  initsetting(); 
  loadsetting();
  lcd.init(); // LCD 초기화
  lcd.backlight(); // 백라이트 켜기

  pinMode(CLK, INPUT_PULLUP); // CLK를 내장 풀업 저항을 사용하는 입력으로 설정
  pinMode(DT, INPUT_PULLUP);  // DT를 내장 풀업 저항을 사용하는 입력으로 설정
  pinMode(SW, INPUT_PULLUP);  // SW를 내장 풀업 저항을 사용하는 입력으로 설정

  //attachInterrupt(digitalPinToInterrupt(SW), rotbutton, LOW);
  attachInterrupt(digitalPinToInterrupt(DT), rotdial, CHANGE);
}
void loop()
{

  if ((millis()/100)%(INTERVAL+DURATION)<DURATION) led_onoff=1; else led_onoff=0;

  currb=debounceButton(currb);

  if (mode==0 && currb==LOW && lastb==HIGH) {
    lcd.clear(); mode=1; mcur=0; mr=RED; mg=GREEN; mb=BLUE; mi=INTERVAL; md=DURATION; 
  }
  else if (mode==1 && currb==LOW && lastb==HIGH && mcur!=5) {
    lcd.clear(); mode=2; menu=mcur; 
  }
  else if (mode==1 && currb==LOW && lastb==HIGH && mcur==5) {
    lcd.clear();
    lcd.setCursor(0,2); sprintf(buf,"STORING SETTING ."); lcd.print(buf); delay(500);
    lcd.setCursor(0,2); sprintf(buf,"STORING SETTING .."); lcd.print(buf); delay(500);
    lcd.setCursor(0,2); sprintf(buf,"STORING SETTING ..."); lcd.print(buf); delay(500);
    savesetting(); mode=0;
  }
  else if (mode==2 && currb==LOW && lastb==HIGH) {
    lcd.clear(); mode=1;
  }
  
  lcdshow();
  ledlight();

  lastb=currb;

}

boolean debounceButton(boolean state)
{
	boolean stateNow=digitalRead(SW);
	if (state!=stateNow) {
		delay(10);
		stateNow=digitalRead(SW);
	}
	return stateNow;
}


void ledlight()
{
  if (led_onoff==1) {
    analogWrite(RED_PIN, 255-RED);
    analogWrite(GREEN_PIN, 255-GREEN);
    analogWrite(BLUE_PIN, 255-BLUE);
  }
  else {
    analogWrite(RED_PIN, 255);
    analogWrite(GREEN_PIN, 255);
    analogWrite(BLUE_PIN, 255);
  }
}

void lcdshow()
{
  if (mode==0) {
    lcd.setCursor(0,0);             
    sprintf(buf,"==RGB LED LIGHTING==");
    lcd.print(buf);
    lcd.setCursor(0,1);             
    sprintf(buf,"R:%3d  G:%3d  B:%3d  ",RED,GREEN,BLUE);
    lcd.print(buf);  
    lcd.setCursor(0,2);             
    sprintf(buf," INTERVAL:%2d.%d(sec) ",INTERVAL/10,INTERVAL%10);
    lcd.print(buf);      
    lcd.setCursor(0,3);             
    sprintf(buf," DURATION:%2d.%d(sec) ",DURATION/10, DURATION%10);
    lcd.print(buf);      
  }
  if (mode==1){
    lcd.setCursor(0,0);             
    sprintf(buf,"<<<CHANGE SETTING>>>");
    lcd.print(buf);   

    lcd.setCursor(0,1);             
    sprintf(buf,"%cRED      %cGREEN    ",mcur==0?'>':' ',mcur==1?'>':' ');
    lcd.print(buf);  

    lcd.setCursor(0,2);             
    sprintf(buf,"%cBLUE     %cINTERVAL ",mcur==2?'>':' ',mcur==3?'>':' ');
    lcd.print(buf);  

    lcd.setCursor(0,3);             
    sprintf(buf,"%cDURATION %cEXIT     ",mcur==4?'>':' ',mcur==5?'>':' ');
    lcd.print(buf);      

  }
  if (mode==2){

    switch (menu) {
      case 0:
        lcd.setCursor(0,0);             
        sprintf(buf,"<<<CHANGE SETTING>>>");
        lcd.print(buf);      

        lcd.setCursor(0,2);             
        sprintf(buf,"        %3d",mr);
        lcd.print(buf);
        lcd.setCursor(0,3);             
        sprintf(buf,"   RED INTENSITY");
        lcd.print(buf);
        break;

      case 1:
        lcd.setCursor(0,0);             
        sprintf(buf,"<<<CHANGE SETTING>>>");
        lcd.print(buf);      

        lcd.setCursor(0,2);             
        sprintf(buf,"        %3d",mg);
        lcd.print(buf);
        lcd.setCursor(0,3);             
        sprintf(buf,"  GREEN INTENSITY");
        lcd.print(buf);
        break;

      case 2:
        lcd.setCursor(0,0);             
        sprintf(buf,"<<<CHANGE SETTING>>>");
        lcd.print(buf);      

        lcd.setCursor(0,2);             
        sprintf(buf,"        %3d",mb);
        lcd.print(buf);
        lcd.setCursor(0,3);             
        sprintf(buf,"   BLUE INTENSITY");
        lcd.print(buf);
        break;

      case 3:
        lcd.setCursor(0,0);             
        sprintf(buf,"<<<CHANGE SETTING>>>");
        lcd.print(buf);      

        lcd.setCursor(0,2);             
        sprintf(buf,"       %2d.%d",mi/10,mi%10);
        lcd.print(buf);
        lcd.setCursor(0,3);             
        sprintf(buf,"   BLINK INTERVAL");
        lcd.print(buf);
        break;

      case 4:
        lcd.setCursor(0,0);             
        sprintf(buf,"<<<CHANGE SETTING>>>");
        lcd.print(buf);  

        lcd.setCursor(0,2);             
        sprintf(buf,"       %2d.%d",md/10,md%10);
        lcd.print(buf);
        lcd.setCursor(0,3);             
        sprintf(buf,"   BLINK DURATION");
        lcd.print(buf);
        break;
    }    
  }
}

void loadsetting()
{
  RED=EEPROM.read(1);
  GREEN=EEPROM.read(2);
  BLUE=EEPROM.read(3);
  INTERVAL=EEPROM.read(4);
  DURATION=EEPROM.read(5);
}
void initsetting()
{
  if (EEPROM.read(0)!=101) {
    EEPROM.update(0,101);
    EEPROM.update(1,255);
    EEPROM.update(2,255);
    EEPROM.update(3,255);
    EEPROM.update(4,10);
    EEPROM.update(5,10);
  }
}
void savesetting()
{
  RED=mr; EEPROM.update(1,RED);
  GREEN=mg; EEPROM.update(2,GREEN);
  BLUE=mb; EEPROM.update(3,BLUE);
  INTERVAL=mi; EEPROM.update(4,INTERVAL);
  DURATION=md; EEPROM.update(5,DURATION);
}

void rotdial() {

  if (mode==0) return;

  int direct = 0;                 // 방향을 0으로 초기화
  int newCLK = digitalRead(CLK);  // 현재 CLK 값을 저장하는 변수
  int newDT = digitalRead(DT);    // 현재 DT 값을 저장하는 변수
  if (newCLK != oldCLK) {         // CLK 값이 변한 경우
    if (oldCLK == LOW) {          // LOW에서 HIGH로 변한 경우
      direct = -1*(oldDT * 2 - 1);     // DT 값을 이용해 direct 값 변경 
    }
  }
  oldCLK = newCLK;  // oldCLK 갱신
  oldDT = newDT;    // oldDT 갱신

  if (mode==1) {
    mcur+=direct; if (mcur>5) mcur=0; if (mcur<0) mcur=5; return;
  }

  switch (menu) {
      case 0: mr+=5*direct; if (mr>255) mr=0; if(mr<0) mr=255; break;
      case 1: mg+=5*direct; if (mg>255) mg=0; if(mg<0) mg=255;break;
      case 2: mb+=5*direct; if (mb>255) mb=0; if(mb<0) mb=255;break;
      case 3: mi+=direct; if (mi>100) mi=0; if(mi<0) mi=100;break;
      case 4: md+=direct; if (md>100) md=0; if(md<0) md=100;break;
  }
} 

/*
void rotbutton() {
  

  if (mode==0) {
    lcd.clear(); mode=1; menu=0; mr=RED; mg=GREEN; mb=BLUE; mi=INTERVAL; md=DURATION;
  }
  
  if (mode==1) {
    lcd.clear(); menu=menu+1;
  }
}
*/