package doc.walkthru;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.benow.java.annotations.Required;
import org.benow.java.annotations.StringLength;
import org.benow.repository.mapping.JSQLArrayList;
import org.benow.repository.mapping.JSQLList;
import org.benow.repository.mapping.JSQLObject;

/**
 * An album that is persisted in the repository.  JSQLObject adds repository support, including
 * key management and field fetching.
 */
public class AlbumImpl extends JSQLObject implements Album {

  // serialVersionUID is used by the repository to determine when tables need changing
  private static final long serialVersionUID = 1L;
  
  // @Required ensures that this field is always fetched from the repository whenever this object is.
  // Note that performer is an interface.  The repository will manage appropriate reconstruction.
  @Required
  private Performer performer;
  // A constant from the Format enumeration
  private Format format;
  // @StringLength is a hint for the repository which guides table column construction making, for nice schema 
  @StringLength(128)
  private String title;
  private Date releaseDate;
  /* a list of tracks, fetched from the repository as required. */
  private final JSQLList<AlbumTrack> tracks=new JSQLArrayList<AlbumTrack>(this,"tracks");
  // cached tracks sorted by number on album.  Transient fields are not persisted by repository
  private transient List<Track> sortedTracks = new ArrayList<Track>();

  /**
   * A zero parameter constructor is required by the repository.
   */
  protected AlbumImpl() {
    super();
  }

  public AlbumImpl(String title, Performer artist) {
    super();
    this.title = title;
    this.performer=artist;
  }
  
  @Override
  public Performer getArtist() {
    // artist is always available as @Required is specified.
    return performer;
  }

  @Override
  public Format getFormat() {
    return format;
  }

  @Override
  public String getTitle() {
    return title;
  }

  @Override
  public Date getReleaseDate() {
    return releaseDate;
  }

  @Override
  public List<Track> getTracks() {
    fetchFieldQuiet("tracks");
    sortedTracks.clear();
    Map sorter = new TreeMap<Integer, Track>();
    for (AlbumTrack atrack : tracks) {
      sorter.put(atrack.getPositionInAlbum(), atrack.getTrack());
    }
    sortedTracks.addAll(sorter.values());
    
    return sortedTracks;
  }

  /**
   * Add a track to the end of the tracks
   * @param track track to add
   * @return added track for this album
   */
  public AlbumTrack addTrack(Track track) {
    AlbumTrack atrack = new AlbumTrack(this, (TrackImpl) track, tracks.size());
    this.tracks.add(atrack);
    // it's at the end, so add to cached is fine.
    sortedTracks.add(track);
    return atrack;
  }
  
  /**
   * Add track at a given position
   * @param track track to add
   * @param pos position to add at
   * @return added track for this album
   */
  public AlbumTrack addTrack(Track track, int pos) {
    // invalidate cache
    sortedTracks=null;
    
    // renumber then add this track
    for (AlbumTrack a: tracks) {
      if (a.getPositionInAlbum()>=pos)
        a.setPosition(a.getPositionInAlbum()+1);
    }
    AlbumTrack atrack = new AlbumTrack(this, (TrackImpl) track, pos);
    this.tracks.add(atrack);
    return atrack;
  }

  @Override
  public boolean isCompilation() {
    return performer==null;
  }

  public void setTracks(Track[] tracks) {
    this.tracks.clear();
    for (int i = 0; i < tracks.length; i++) {
      Track curr = tracks[i];
      addTrack(curr);
    }
  }
  
  @Override
  public String toString() {
    String msg = (performer != null ? performer.getName() : "various") + " - " + title;
    return msg;
  }

  public String toFullString() {
    String msg = toString();
    getTracks();
    int count = 1;
    for (Track t : sortedTracks) {
      msg += "\n\t" + count + "\t" + t.getName() + " (" + t.durationInSeconds() + "s)";
      count++;
    }
    return msg;
  }

}