import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Scanner;
import java.util.stream.Collectors;

public class Backend implements BackendInterface {
  //data get from readData
  private IterableSortedCollection<SongInterface> songsCollection;
  private List<SongInterface> allSongs;
  //store the result from getRange
  private List<SongInterface> filteredSongs = new ArrayList<>();
  //store the result from filterEnergeticSongs
  private List<SongInterface> energeticSongs = new ArrayList<>();
  //store the min energy
  private int minEnergy;
  //check whether called the filterEnergeticSongs
  private boolean filter = false;
  //check whether called the getRange
  private boolean getRange = false;

  public Backend(IterableSortedCollection<SongInterface> songs) {
    this.songsCollection = songs;
    this.allSongs = new ArrayList<>();
  }

  /**
   * Loads data from the .csv file referenced by filename.
   * @param filename is the name of the csv file to load data from
   * @throws IOException when there is trouble finding/reading file
   */
  @Override
  public void readData(String filename) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
      // Skip the first line (usually headers)
      br.readLine();

      String line;
      while ((line = br.readLine()) != null) {
        List<String> info = parseCsvLine(line);
        songsCollection.insert(new Song(info.toArray(new String[0])));
      }
    } catch(Exception e){
      throw new IOException();
    }
  }

  private List<String> parseCsvLine(String line) {
    List<String> fields = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    boolean inQuotes = false;

    for (char c : line.toCharArray()) {
      if (c == '"') {
        inQuotes = !inQuotes;
        continue;
      }

      if (c == ',' && !inQuotes) {
        fields.add(trimQuotes(sb.toString()));
        sb.setLength(0); // Clear the StringBuilder
      } else {
        sb.append(c);
      }
    }

    // Add the last field
    fields.add(trimQuotes(sb.toString()));
    return fields;
  }

  private String trimQuotes(String field) {
    if (field.startsWith("\"") && field.endsWith("\"")) {
      return field.substring(1, field.length() - 1).replace("\"\"", "\"");
    }
    return field;
  }

  /**
   * Retrieves a list of song titles for songs that have a Danceability
   * within the specified range (sorted by Danceability in ascending order).
   * If a minEnergy filter has been set using filterEnergeticSongs(), then
   * only songs with an energy rating greater than or equal to this minEnergy
   * should beincluded in the list that is returned by this method.
   *
   * Note that either this danceability range, or the resulting unfiltered
   * list of songs should be saved for later use by the other methods
   * defined in this class.
   *
   * @param low is the minimum Danceability of songs in the returned list
   * @param hight is the maximum Danceability of songs in the returned list
   * @return List of titles for all songs in specified range
   */
  @Override
  public List<String> getRange(int low, int high) {
    getRange = true;
    for (SongInterface song : songsCollection) {
      if (song.getDanceability() >= low && song.getDanceability() <= high) {
        filteredSongs.add(song);
      }
    }
    Collections.sort(filteredSongs, (s1, s2) -> Integer.compare(s1.getDanceability(), s2.getDanceability()));
    List<String> songTitles = new ArrayList<>();
    for (SongInterface song : filteredSongs) {
      songTitles.add(song.getTitle());
    }
    return songTitles;
  }

  /**
   * Filters the list of songs returned by future calls of getRange() and
   * fiveFastest() to only include energetic songs.  If getRange() was
   * previously called, then this method will return a list of song titles
   * (sorted in ascending order by Danceability) that only includes songs on
   * with the specified minEnergy or higher.  If getRange() was not
   * previously called, then this method should return an empty list.
   *
   * Note that this minEnergy threshold should be saved for later use by the
   * other methods defined in this class.
   *
   * @param minEnergy is the minimum energy of songs that are returned
   * @return List of song titles, empty if getRange was not previously called
   */
  @Override
  public List<String> filterEnergeticSongs(int minEnergy) {
    if (!getRange) {
      // If getRange() was not previously called
      return new ArrayList<>();
    } else {
      this.minEnergy = minEnergy;
      filter = true;
      for (SongInterface song : filteredSongs) {
        if (song.getDanceability() >= minEnergy) {
          energeticSongs.add(song);
        }
      }
      Collections.sort(energeticSongs, (s1, s2) -> Integer.compare(s1.getDanceability(), s2.getDanceability()));
      List<String> songTitles = new ArrayList<>();
      for (SongInterface song : energeticSongs) {
        songTitles.add(song.getTitle());
      }
      return songTitles;
    }
  }

  /**
   * This method makes use of the attribute range specified by the most
   * recent call to getRange().  If a minEnergy threshold has been set by
   * filterEnergeticSongs() then that will also be utilized by this method.
   * Of those songs that match these criteria, the five fastest will be
   * returned by this method as a List of Strings in increasing order of
   * danceability.  Each string contains the speed in BPM followed by a
   * colon, a space, and then the song's title.
   * If fewer than five such songs exist, display all of them.
   *
   * @return List of five fastest song titles and their respective speeds
   * @throws IllegalStateException when getRange() was not previously called.
   */
  @Override
  public List<String> fiveFastest() {
    if (!getRange) {
      throw new IllegalStateException("getRange() was not previously called.");
    }
    List<SongInterface> fastestSongs = new ArrayList<>(filteredSongs);
    List<String> songTitles = new ArrayList<>();
    if (filter) {
      if (energeticSongs.size() < 5) {
        for (SongInterface song : energeticSongs) {
          songTitles.add(song.getTitle());
        }
      } else {
        for (int i = energeticSongs.size() - 5; i < energeticSongs.size(); i++) {
          SongInterface song = energeticSongs.get(i);
          songTitles.add(song.getTitle());
        }
      }
    } else {
      if (fastestSongs.size() < 5) {
        for (SongInterface song : filteredSongs) {
          songTitles.add(song.getTitle());
        }
      } else {
        for (int i = filteredSongs.size() - 5; i < filteredSongs.size(); i++) {
          SongInterface song = filteredSongs.get(i);
          songTitles.add(song.getTitle());
        }
      }
    }
      return songTitles;
  }
}


