#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <DFRobotDFPlayerMini.h>
#include <SD.h>

// --- 핀 설정 ---
#define TFT_DC    28
#define TFT_RST   27
#define TFT_CS    22
#define SD_CS     5
#define ENCODER_S1  14
#define ENCODER_S2  13
#define ENCODER_KEY 15

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
DFRobotDFPlayerMini myDFPlayer;

struct Song {
  String title;
  String artist;
  long totalSeconds;
};

Song playlist[100];
int totalTracks = 0;
int currentTrack = 1;
int volume = 15;
bool isVolumeMode = false;
bool isPaused = true;
bool isShuffle = false; // 셔플 상태 변수

float artistScrollX = 10;
int artistPixelWidth = 0;

// --- 엔코더 로직 ---
volatile int encoderPos = 0;
int lastReportedPos = 0;
static uint8_t encoderState = 0;
const int8_t KNOB_DIR[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};

void readEncoder() {
  encoderState <<= 2;
  encoderState |= (digitalRead(ENCODER_S1) << 1) | digitalRead(ENCODER_S2);
  encoderPos += KNOB_DIR[encoderState & 0x0F];
}

unsigned long buttonPressTime = 0;
bool buttonActive = false;
bool feedbackGiven = false;
bool modeChanged = false;
bool shuffleChanged = false; // 셔플 중복 토글 방지
unsigned long lastEncoderMoveTime = 0;
bool isWaitingToPlay = false;
unsigned long songStartTime = 0;
unsigned long pausedTime = 0;
unsigned long lastBarUpdate = 0;

float scrollX = 10;
unsigned long lastScrollTime = 0;
int titlePixelWidth = 0;

// --- 볼륨 표시 ---
void renderVolumeText(bool erase) {
  tft.setTextSize(2);
  tft.setCursor(150, 295);
  if (erase) {
    tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
  } else {
    tft.setTextColor(isVolumeMode ? ILI9341_ORANGE : ILI9341_LIGHTGREY, ILI9341_BLACK);
  }

  tft.print("VOL:");
  int displayVol = map(volume, 0, 30, 0, 100);
  if (displayVol < 10) tft.print("  ");
  else if (displayVol < 100) tft.print(" ");
  tft.print(displayVol);
}

String formatTime(long seconds) {
  int m = seconds / 60;
  int s = seconds % 60;
  return (m < 10 ? "0" : "") + String(m) + ":" + (s < 10 ? "0" : "") + String(s);
}

void setup() {
  Serial1.setRX(1);
  Serial1.setTX(0);
  Serial1.begin(9600);
  randomSeed(analogRead(26)); // 셔플용 랜덤 시드

  pinMode(ENCODER_S1, INPUT_PULLUP);
  pinMode(ENCODER_S2, INPUT_PULLUP);
  pinMode(ENCODER_KEY, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_S1), readEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENCODER_S2), readEncoder, CHANGE);

  pinMode(TFT_CS, OUTPUT);
  pinMode(SD_CS, OUTPUT);
  digitalWrite(TFT_CS, HIGH);
  digitalWrite(SD_CS, HIGH);

  tft.begin();
  tft.setRotation(0);
  tft.setTextWrap(false);

  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(0x7BEF); tft.setTextSize(1);
  tft.setCursor(92, 125); tft.print("PRESENT");
  tft.setTextColor(ILI9341_WHITE); tft.setTextSize(3);
  tft.setCursor(45, 145); tft.print("Seojeong");
  tft.drawFastHLine(85, 180, 70, 0x07FF);

  bool sdReady = false;
  for (int i = 0; i < 10; i++) {
    if (SD.begin(SD_CS)) {
      sdReady = true;
      break;
    }
    delay(150);
  }

  if (sdReady) {
    File listFile = SD.open("list.txt");
    if (listFile) {
      totalTracks = 0;
      while (listFile.available() && totalTracks < 100) {
        String line = listFile.readStringUntil('\n');
        line.trim();
        int firstDash = line.indexOf('-');
        int lastDash = line.lastIndexOf('-');
        if (firstDash != -1 && lastDash != firstDash) {
          playlist[totalTracks].artist = line.substring(0, firstDash);
          playlist[totalTracks].title = line.substring(firstDash + 1, lastDash);
          String timeStr = line.substring(lastDash + 1);
          int colon = timeStr.indexOf(':');
          if (colon != -1) {
            int m = timeStr.substring(0, colon).toInt();
            int s = timeStr.substring(colon + 1).toInt();
            playlist[totalTracks].totalSeconds = (m * 60) + s;
          }
          playlist[totalTracks].artist.trim();
          playlist[totalTracks].title.trim();
          totalTracks++;
        }
      }
      listFile.close();
    }
  }

  if (totalTracks > 0) {
    // 1부터 totalTracks 사이의 숫자를 무작위로 선택
    currentTrack = random(1, totalTracks + 1);
  }

  if (myDFPlayer.begin(Serial1)) {
    // [수정] 부팅 직후 볼륨이 튀는 것을 방지하기 위해 초기화 지연 후 설정
    delay(500);
    myDFPlayer.volume(volume);
    delay(100);
  }

  delay(1200);
  updateUI();
}

void loop() {
  static unsigned long lastSDCheck = 0;
  if (millis() - lastSDCheck > 2000) { // 2초마다 체크 (성능 저하 방지)
    lastSDCheck = millis();
    if (!SD.exists("/")) { // 루트 경로가 보이지 않으면 카드 제거로 판단
      handleSDError();
      return; // 에러 상태일 때는 아래 기능을 수행하지 않음
    }
  }

  checkEncoder();
  handleButton();
  handleSmartSelect();
  updateProgressBar();
  drawVolumeBlink();
  drawVisualizer();
  updateTitleScroll();
}

void playNextTrack() {
  if (isShuffle) {
    currentTrack = random(1, totalTracks + 1);
  } else {
    currentTrack++;
    if (currentTrack > totalTracks) currentTrack = 1;
  }
  playTrack(currentTrack);
}

void updateTitleScroll() {
  if (totalTracks == 0) return;

  unsigned long now = millis();
  if (now - lastScrollTime > 30) {
    lastScrollTime = now;

    // --- 제목(Title) 스크롤 로직 (기존) ---
    if (titlePixelWidth > 220) {
      scrollX -= 2;
      if (scrollX < -(titlePixelWidth + 40)) scrollX = 240;
      tft.setTextSize(3);
      tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
      tft.setCursor((int)scrollX, 55);
      tft.print(playlist[currentTrack - 1].title);
      tft.print("    ");
    }

    // --- 가수(Artist) 스크롤 로직 (추가) ---
    if (artistPixelWidth > 220) {
      artistScrollX -= 1.5; // 제목보다 약간 느리게 설정 가능
      if (artistScrollX < -(artistPixelWidth + 40)) artistScrollX = 240;
      tft.setTextSize(2);
      tft.setTextColor(0xBDD7, ILI9341_BLACK);
      tft.setCursor((int)artistScrollX, 95); // Y좌표 95 유지
      tft.print(playlist[currentTrack - 1].artist);
      tft.print("    ");
    }

    // 검은색 잔상 방지 (화면 끝 처리)
    if (scrollX < 0 && scrollX > -20) tft.fillRect(0, 50, 10, 35, ILI9341_BLACK);
    if (artistScrollX < 0 && artistScrollX > -20) tft.fillRect(0, 90, 10, 25, ILI9341_BLACK);
  }
}

void checkEncoder() {
  if (encoderPos != lastReportedPos) {
    int diff = (encoderPos - lastReportedPos) / 4;
    if (diff != 0) {
      onEncoderMove(diff);
      lastReportedPos = encoderPos;
      if (!isVolumeMode) {
        lastEncoderMoveTime = millis();
        isWaitingToPlay = true;
      }
    }
  }
}

void onEncoderMove(int dir) {
  if (isVolumeMode) {
    volume = constrain(volume + dir, 0, 30);
    myDFPlayer.volume(volume);
    renderVolumeText(false);
  } else {
    currentTrack += dir;
    if (currentTrack > totalTracks) currentTrack = 1;
    if (currentTrack < 1) currentTrack = totalTracks;
    updateUI();
  }
}

void handleButton() {
  bool btnState = (digitalRead(ENCODER_KEY) == LOW);

  if (btnState) {
    if (!buttonActive) {
      buttonActive = true;
      buttonPressTime = millis();
      feedbackGiven = false;
    }

    // --- [추가] 실시간 하단 가이드 메시지 ---
    unsigned long pressDur = millis() - buttonPressTime;
    tft.setTextSize(2);

    if (pressDur > 3000) { // 3초 경과
      tft.setTextColor(ILI9341_CYAN, ILI9341_BLACK);
      tft.setCursor(10, 295);
      tft.print("[SHUFFLE?] "); // 3초 후 떼면 셔플
    }
    else if (pressDur > 1000) { // 1초 경과
      tft.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
      tft.setCursor(10, 295);
      tft.print("[VOL MODE?]"); // 1초 후 떼면 볼륨
    }
  }
  else { // 버튼을 뗐을 때
    if (buttonActive) {
      unsigned long duration = millis() - buttonPressTime;

      // 동작 실행 후 하단 가이드 영역 지우기
      tft.fillRect(0, 290, 130, 30, ILI9341_BLACK);

      if (duration > 3000) {
        isShuffle = !isShuffle;
        updateUI();
      } else if (duration > 1000) {
        isVolumeMode = !isVolumeMode;
        if (!isVolumeMode) showSaveMsg();
        updateUI();
      } else if (duration > 50) {
        togglePause();
      }

      buttonActive = false;
      feedbackGiven = false;
    }
  }
}

void handleSmartSelect() {
  if (isWaitingToPlay && !isVolumeMode) {
    if (millis() - lastEncoderMoveTime > 1500) {
      playTrack(currentTrack);
      isWaitingToPlay = false;
    }
  }
}

void playTrack(int trackNum) {
  myDFPlayer.play(trackNum);
  isPaused = false;
  isWaitingToPlay = false;
  songStartTime = millis();
  updateUI();
}

void togglePause() {
  if (isPaused) {
    if (songStartTime == 0) playTrack(currentTrack);
    else {
      myDFPlayer.start();
      songStartTime += (millis() - pausedTime);
      isPaused = false;
    }
  } else {
    myDFPlayer.pause(); pausedTime = millis(); isPaused = true;
  }
  updateUI();
}

void updateUI() {
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
  tft.setTextSize(2);
  tft.setCursor(10, 20);

  if (isWaitingToPlay) tft.print(">> SELECT");
  else {
    tft.print(isPaused ? "PAUSED" : "PLAYING");
    if (isShuffle) {
      tft.setTextColor(ILI9341_CYAN);
      tft.print(" [SHUFFLE]"); // 셔플 활성화 표시
      tft.setTextColor(ILI9341_WHITE);
    }
  }

  if (totalTracks > 0) {
    String currentTitle = playlist[currentTrack - 1].title;
    titlePixelWidth = currentTitle.length() * 18;
    scrollX = 10;

    tft.setTextSize(3);
    tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
    tft.setCursor(10, 55);
    tft.print(currentTitle);


    String currentArtist = playlist[currentTrack - 1].artist;
    artistPixelWidth = currentArtist.length() * 12; // TextSize 2이므로 글자당 약 12픽셀
    artistScrollX = 10;

    if (artistPixelWidth <= 220) {
      tft.setTextSize(2);
      tft.setTextColor(0xBDD7, ILI9341_BLACK);
      tft.setCursor(10, 95);
      tft.print(currentArtist);
    }
  }
  tft.drawRect(18, 146, 204, 12, ILI9341_WHITE);
  renderVolumeText(false);
}

void updateProgressBar() {
  if (totalTracks == 0) return;

  if (myDFPlayer.readType() == DFPlayerError) {
    isPaused = true;
    return;
  }

  if (millis() - lastBarUpdate > 500) {
    lastBarUpdate = millis();
    long elapsed = (isPaused || songStartTime == 0) ? (pausedTime - songStartTime) / 1000 : (millis() - songStartTime) / 1000;
    if (songStartTime == 0) elapsed = 0;
    long total = playlist[currentTrack - 1].totalSeconds;

    tft.setTextSize(1);
    tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
    tft.setCursor(20, 135); tft.print(formatTime(elapsed));
    tft.setCursor(190, 135); tft.print(formatTime(total));

    if (total > 0) {
      int barWidth = map(constrain(elapsed, 0, total), 0, total, 0, 200);
      tft.fillRect(20, 148, barWidth, 8, ILI9341_CYAN);
      tft.fillRect(20 + barWidth, 148, 200 - barWidth, 8, 0x2104);

      // 곡이 끝났을 때 자동 다음 곡 재생 로직
      if (!isPaused && elapsed >= total) {
        delay(500);
        playNextTrack();
      }
    }
  }
}

void drawVisualizer() {
  if (isPaused) {
    tft.fillRect(20, 210, 200, 45, ILI9341_BLACK);
    return;
  }
  static unsigned long lastVis = 0;
  if (millis() - lastVis > 120) {
    lastVis = millis();
    for (int i = 0; i < 8; i++) {
      int h = random(5, 40);
      int x = 25 + (i * 25);
      tft.fillRect(x, 210, 15, 40, ILI9341_BLACK);
      tft.fillRect(x, 250 - h, 15, h, 0xF81F);
    }
  }
}

void drawVolumeBlink() {
  static unsigned long lastBlink = 0;
  static bool show = true;
  if (!isVolumeMode) return;
  if (millis() - lastBlink > 400) {
    lastBlink = millis();
    show = !show;
    renderVolumeText(!show);
  }
}

void showSaveMsg() {
  tft.fillRect(0, 285, 240, 35, ILI9341_DARKGREEN);
  tft.setCursor(35, 295);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.print("SETTINGS SAVED");
  delay(800);
}

void handleSDError() {
  tft.fillScreen(ILI9341_RED);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.setCursor(30, 140);
  tft.print("SD CARD REMOVED!");
  tft.setCursor(20, 170);
  tft.print("Please Re-insert...");
  myDFPlayer.stop(); // 재생 중단 명령
  while (!SD.begin(SD_CS)) {
    delay(500);  // 재삽입 대기
  }
  updateUI(); // 복구 시 UI 갱신
}
