package be.msyben.vinylManager.entities;

import be.msyben.vinylManager.properties.Preferences;
import be.msyben.vinylManager.properties.Support;
import be.msyben.vinylManager.tools.discogs.DiscogsDownloader;
import be.msyben.vinylManager.tools.internalEntities.TreeSortColumn;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Cette classe représente un album
 * Note: this class has a natural ordering that is inconsistent with equals.
 *
 * @author Mathieu Syben
 */
public class Album implements Serializable, Comparable, Cloneable, Transferable {

    /**
     * uid
     */
    private static final long serialVersionUID = 3662221652096969867l;
    /**
     * Les préfixes.
     */
    private static final String[] PREFIXES = new String[]{"the", "le", "la", "les", "l'"};
    private transient Long id;
    private String titre;
    private String artiste;
    private String artisteSort;
    private String reference;
    private String musiciens;
    private String annee;
    private String anneeParution;
    private String editeur;
    private String genre;
    private int cote = 0;
    private boolean coteDemi=false;
    private String commentaire;
    private Image image;
    private List<Image> otherImages;
    private String etat;
    private List<Disque> disques;
    private String valeur;
    private String parolier;
    private Date dateAchat;
    private Integer support;
    private String compositeur;
    private String chef;
    private String orchestre;
    private String pays;
    private boolean compilation = false;
    private Date dateAjout;
    private Boolean possede;
    private String discogsId;
    private String location;
    
    /**
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    private String couleur;
    /**
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    private Integer taille;
    /**
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    private int vitesse;
    
    /**
     * Creates a new instance of Album
     */
    public Album() {
        dateAjout = new Date();
    }

    /**
     * Getter pour id.
     * @return id;
     */
    public Long getId() {
        return id;
    }

    /**
     * Setter pour id.
     * @param id l'id.
     */
    public void setId(Long id) {
        this.id = id;
    }
    
    
    
    

    /**
     * Getter for property titre.
     *
     * @return Value of property titre.
     */
    public String getTitre() {
        return this.titre;
    }

    /**
     * Setter for property titre.
     *
     * @param titre New value of property titre.
     */
    public void setTitre(String titre) {
        this.titre = titre;
    }

    /**
     * Getter for property artiste.
     *
     * @return Value of property artiste.
     */
    public String getArtiste() {
        return this.artiste;
    }

    /**
     * Cette méthode renvoie le nom à afficher lorsque l'on veut afficher
     * l'album dans une liste. Elle prend en compte les compilations et les
     * options qui vont avec...
     *
     * @return l'artiste à afficher.
     */
    public String getArtisteForList() {
        if (getCompliation()) {
            int currentSelection = 1;
            try {
                currentSelection = Integer.parseInt(Preferences.getInstance().get(Preferences.COMPILATION_MODE, "1"));
            } catch (NumberFormatException nfe) {
                currentSelection = 1;
            }
            switch (currentSelection) {
                case 1: // creer artiste compilation
                    return "Compilation";
                case 2: // titre de l'album
                    return getTitre();
                default: // ne devrait pas se produire.
                    return "Compilation";
            }
        } else {
            return getArtiste();
        }
    }

    /**
     * Cette méthode renvoie le nom à utiliser pour le tri.
     *
     * @return l'artiste à utiliser pour le tri.
     */
    public String getArtisteForSorting() {
        if (artisteSort == null || artisteSort.isEmpty()) {
            return convertForSorting(getArtisteForList());
        } else {
            return artisteSort;
        }
    }

    private static String convertForSorting(String res) {
        if (res == null) {
            return null;
        }
        if (Preferences.getInstance().get(Preferences.REMOVE_PREFIX, "N").equals("N")) {
            return res;
        } else {
            String[] sub = res.split(" ");
            if (sub.length <= 1) {
                return res;
            }
            boolean removeFirst = false;
            for (int i = 0; i < PREFIXES.length; i++) {
                if (sub[0].equalsIgnoreCase(PREFIXES[i])) {
                    removeFirst = true;
                    break;
                }
            }
            if (removeFirst) {
                StringBuilder sb = new StringBuilder("");
                for (int i = 1; i < sub.length; i++) {
                    sb.append(sub[i]);
                    if (i < sub.length - 1) {
                        sb.append(" ");
                    }
                }
                return sb.toString();
            } else {
                return res;
            }
        }
    }

    /**
     * Setter for property artiste.
     *
     * @param artiste New value of property artiste.
     */
    public void setArtiste(String artiste) {
        this.artiste = artiste;
    }

    /**
     * Getter for property reference.
     *
     * @return Value of property reference.
     */
    public String getReference() {
        return this.reference;
    }

    /**
     * Setter for property reference.
     *
     * @param reference New value of property reference.
     */
    public void setReference(String reference) {
        this.reference = reference;
    }

    /**
     * Getter for property editeur.
     *
     * @return Value of property editeur.
     */
    public String getEditeur() {
        return this.editeur;
    }

    /**
     * Setter for property editeur.
     *
     * @param editeur New value of property editeur.
     */
    public void setEditeur(String editeur) {
        this.editeur = editeur;
    }

    /**
     * Getter for property annee.
     *
     * @return Value of property annee.
     */
    public String getAnnee() {
        return this.annee;
    }

    /**
     * Setter for property annee.
     *
     * @param annee New value of property annee.
     */
    public void setAnnee(String annee) {
        this.annee = annee;
    }

    public String getAnneeParution() {
        return anneeParution;
    }

    public void setAnneeParution(String anneeParution) {
        this.anneeParution = anneeParution;
    }

    
    
    /**
     * Getter for property vitesse.
     *
     * @return Value of property vitesse.
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    public int getVitesse() {
        return this.vitesse;
    }

    /**
     * Setter for property vitesse.
     *
     * @param vitesse New value of property vitesse.
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    public void setVitesse(int vitesse) {
        this.vitesse = vitesse;
    }

    /**
     * Getter for property musiciens.
     *
     * @return Value of property musiciens.
     */
    public String getMusiciens() {
        return this.musiciens;
    }

    /**
     * Setter for property musiciens.
     *
     * @param musiciens New value of property musiciens.
     */
    public void setMusiciens(String musiciens) {
        this.musiciens = musiciens;
    }

    /**
     * Getter for property disques.
     *
     * @return Value of property disques.
     */
    public List<Disque> getDisques() {
        if (disques == null) {
            disques = new ArrayList<Disque>();
        }
        return this.disques;
    }

    /**
     * Setter privé pour disques.
     *
     * @param disques disques.
     */
    private void setDisques(List<Disque> disques) {
        this.disques = disques;
    }
    
    /**
     * Getter for property otherImages.
     *
     * @return Value of property otherImages.
     */
    public List<Image> getOtherImages() {
        if (otherImages == null) {
            otherImages = new ArrayList<Image>();
        }
        return this.otherImages;
    }

    /**
     * Setter privé pour otherImages.
     *
     * @param otherImages otherImages.
     */
    public void setOtherImages(List<Image> otherImages) {
        this.otherImages = otherImages;
    }

    /**
     * Ajoute un disque à la liste
     *
     * @param disque Le disque à ajouter
     */
    public void addDisque(Disque disque) {
        if (disques == null) {
            disques = new ArrayList<Disque>();
        }
        int num = disques.size() + 1;
        disque.setNum(num);
        disques.add(disque);
    }

    /**
     * Getter for property etat.
     *
     * @return Value of property etat.
     */
    public String getEtat() {
        return this.etat;
    }

    /**
     * Setter for property etat.
     *
     * @param etat New value of property etat.
     */
    public void setEtat(String etat) {
        this.etat = etat;
    }

    /**
     * Getter for property image.
     *
     * @return Value of property image.
     */
    public Image getImage() {
        return this.image;
    }

    /**
     * Setter for property image.
     *
     * @param image New value of property image.
     */
    public void setImage(Image image) {
        this.image = image;
    }
    
    /**
     * Méthode utilisée pour ajouter une image depuis l'import XML.
     *
     * @param image L'image à ajouter.
     */
    public void setImageFromXML(Image image) {
        if (this.image == null) {
            this.image = image;
        } else {
            if (this.otherImages == null) {
                this.otherImages = new ArrayList<Image>();
            }
            this.otherImages.add(image);
        }
    }

    /**
     * Getter for property commentaire.
     *
     * @return Value of property commentaire.
     */
    public String getCommentaire() {
        return this.commentaire;
    }

    /**
     * Setter for property commentaire.
     *
     * @param commentaire New value of property commentaire.
     */
    public void setCommentaire(String commentaire) {
        this.commentaire = commentaire;
    }

    /**
     * Getter for property cote.
     *
     * @return Value of property cote.
     */
    public int getCote() {
        return this.cote;
    }

    /**
     * Setter for property cote.
     *
     * @param cote New value of property cote.
     */
    public void setCote(int cote) {
        this.cote = cote;
    }

    /**
     * Getter for property coteDemi.
     *
     * @return Value of property coteDemi.
     */
    public boolean getCoteDemi() {
        return this.coteDemi;
    }

    /**
     * Setter for property coteDemi.
     *
     * @param coteDemi New value of property coteDemi.
     */
    public void setCoteDemi(boolean coteDemi) {
        this.coteDemi = coteDemi;
    }
    
    /**
     * Sette la cote lors de l'import xml. Prend en compte la valeur double (avec demi).
     * @param cote La cote.
     */
    public void setCote(String cote) {
        try {
            double c = Double.parseDouble(cote);
            setCote((int) c);
            setCoteDemi(c-(1.0*getCote()) != 0.0);
            
        } catch (NumberFormatException ex) {
            
        }
    }

    /**
     * Getter for property genre.
     *
     * @return Value of property genre.
     */
    public String getGenre() {
        return this.genre;
    }

    /**
     * Setter for property genre.
     *
     * @param genre New value of property genre.
     */
    public void setGenre(String genre) {
        this.genre = genre;
    }

    /**
     * Getter for property taille.
     *
     * @return Value of property taille.
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    public Integer getTaille() {
        return this.taille;
    }

    /**
     * Setter for property taille.
     *
     * @param taille New value of property taille.
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    public void setTaille(Integer taille) {
        this.taille = taille;
    }

    /**
     * Getter for property valeur.
     *
     * @return Value of property valeur.
     */
    public String getValeur() {
        return this.valeur;
    }

    /**
     * Setter for property valeur.
     *
     * @param valeur New value of property valeur.
     */
    public void setValeur(String valeur) {
        this.valeur = valeur;
    }

    /**
     * Getter for property couleur.
     *
     * @return Value of property couleur.
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    public String getCouleur() {
        return this.couleur;
    }

    /**
     * Setter for property couleur.
     *
     * @param couleur New value of property couleur.
     * @deprecated plus utilisé depuis 1.10
     */
    @Deprecated
    public void setCouleur(String couleur) {
        this.couleur = couleur;
    }

    /**
     * Getter for property compilation.
     *
     * @return Value of property compilation.
     */
    public boolean getCompliation() {
        return this.compilation;
    }

    /**
     * Setter for property compilation.
     *
     * @param compilation New value of property compilation.
     */
    public void setCompilation(boolean compilation) {
        this.compilation = compilation;
    }

    /**
     * Getter for property chef.
     *
     * @return Value of property chef.
     */
    public String getChef() {
        return chef;
    }

    /**
     * Setter for property chef.
     *
     * @param chef New value of property chef.
     */
    public void setChef(String chef) {
        this.chef = chef;
    }

    /**
     * Getter for property orchestre.
     *
     * @return Value of property orchestre.
     */
    public String getOrchestre() {
        return orchestre;
    }

    /**
     * Setter for property orchestre.
     *
     * @param orchestre New value of property orchestre.
     */
    public void setOrchestre(String orchestre) {
        this.orchestre = orchestre;
    }

    /**
     * Getter for property compositeur.
     *
     * @return Value of property compositeur.
     */
    public String getCompositeur() {
        return compositeur;
    }

    /**
     * Setter pour compositeur.
     *
     * @param compositeur compositeur
     */
    public void setCompositeur(String compositeur) {
        this.compositeur = compositeur;
    }

    /**
     * Getter for property orchestre.
     *
     * @return Value of property orchestre.
     */
    public Integer getSupport() {
        return support;
    }

    /**
     * Récupère le support pour le tri.
     *
     * @return le support pour le tri.
     */
    public Comparable getSupportForSorting() {
        return Support.getSupport(getSupport()).getId();
    }

    /**
     * Setter pour support.
     *
     * @param support support.
     */
    public void setSupport(Integer support) {
        this.support = support;
    }

    /**
     * Getter for property dateAchat.
     *
     * @return Value of property dateAchat.
     */
    public Date getDateAchat() {
        return dateAchat;
    }

    /**
     * Getter for property dateAjout.
     *
     * @return Value of property dateAjout.
     */
    public Date getDateAjout() {
        return dateAjout;
    }

    /**
     * Setter pour dateAchat.
     *
     * @param dateAchat dateAchat
     */
    public void setDateAchat(Date dateAchat) {
        this.dateAchat = dateAchat;
    }

    /**
     * Récupère le mois d'achat.
     *
     * @return le mois d'achat.
     */
    public String getAchatMois() {
        if (dateAchat == null) {
            return "";
        }
        return new SimpleDateFormat("MMM").format(dateAchat);
    }

    /**
     * Récupère l'année d'achat.
     *
     * @return l'année d'achat.
     */
    public String getAchatAnnee() {
        if (dateAchat == null) {
            return "";
        }
        return new SimpleDateFormat("yyyy").format(dateAchat);
    }

    /**
     * Getter pour parolier.
     *
     * @return parolier.
     */
    public String getParolier() {
        return parolier;
    }

    /**
     * Setter pour parolier.
     *
     * @param parolier parolier.
     */
    public void setParolier(String parolier) {
        this.parolier = parolier;
    }

    /**
     * Getter pour pays.
     *
     * @return pays.
     */
    public String getPays() {
        return pays;
    }

    /**
     * Setter pour pays.
     *
     * @param pays pays.
     */
    public void setPays(String pays) {
        this.pays = pays;
    }

    /**
     * Getter pour location.
     *
     * @return location.
     */
    public String getLocation() {
        return location;
    }

    /**
     * Setter pour location.
     *
     * @param location location.
     */
    public void setLocation(String location) {
        this.location = location;
    }
    
    
    

    /**
     * Getter for property possede.
     *
     * @return Value of property coteDemi.
     */
    public boolean getPossede() {
        if (possede == null) {
            possede = Boolean.TRUE;
        }
        return this.possede;
    }

    /**
     * Setter for property possede.
     *
     * @param possede New value of property possede.
     */
    public void setPossede(Boolean possede) {
        this.possede = possede;
    }

    public String getArtisteSort() {
        return artisteSort;
    }

    public void setArtisteSort(String artisteSort) {
        this.artisteSort = artisteSort;
    }

    
    
    /**
     * Récupère les canaux pour le tri.
     *
     * @return les canaux pour le tri.
     */
    public String getCanauxForTree() {
        return TreeSortColumn.getCanauxForTree(this);
    }

    /**
     * Récupère la taille pour le tri.
     *
     * @return la taille pour le tri.
     */
    public String getTailleForTree() {
        return TreeSortColumn.getTailleForAlbum(this);
    }

    /**
     * Récupère la vitesse pour le tri.
     *
     * @return la vitesse pour le tri.
     */
    public String getVitesseForTree() {
        return TreeSortColumn.getVitesseForAlbum(this);
    }

    public String getDiscogsId() {
        if (discogsId != null && ! discogsId.isEmpty()) {
            return discogsId;
        }
        
        if (commentaire != null && commentaire.startsWith(DiscogsDownloader.DISCOGS_API_URL+"/releases/")) {
            String line = commentaire.split("\\r?\\n")[0];
            line = line.substring(32);
            line = line.replaceAll("\\r", ""); // Bizarre... il peut se retrouver tout seul...
            
            if (line.contains(" ")) {
                line = line.substring(0, line.indexOf(" "));
                return line;
            } else {
                return line;
            }
        }
        
        return null;
    }

    public void setDiscogsId(String discogsId) {
        this.discogsId = discogsId;
    }

    
    
    @Override
    public int compareTo(Object o) {
        if (o instanceof Album) {
            Album alb = (Album) o;
            if (this.getArtisteForSorting().toUpperCase().equals(alb.getArtisteForSorting().toUpperCase())) {
                if (this.getAnnee().equals(alb.getAnnee())) {
                    return this.getTitre().toUpperCase().compareTo(alb.getTitre().toUpperCase());
                } else {
                    return this.getAnnee().compareTo(alb.getAnnee());
                }
            } else {
                return this.getArtisteForSorting().toUpperCase().compareTo(alb.getArtisteForSorting().toUpperCase());
            }
        } else {
            return -1;
        }
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Album) {
            Album a = (Album) o;
            return a.getId().longValue() == this.getId().longValue();
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + (this.id != null ? this.id.hashCode() : 0);
        hash = 79 * hash + (this.titre != null ? this.titre.hashCode() : 0);
        hash = 79 * hash + (this.artiste != null ? this.artiste.hashCode() : 0);
        hash = 79 * hash + (this.annee != null ? this.annee.hashCode() : 0);
        return hash;
    }

    @Override
    public Album clone() throws CloneNotSupportedException {

        Album clone = (Album) super.clone();

        if (getImage() != null) {
            clone.setImage(getImage().clone());
        }
        List<Disque> disquesC = new ArrayList<Disque>();
        for (Disque d : getDisques()) {
            disquesC.add(d.clone());
        }        
        clone.setDisques(disquesC);
        
        List<Image> imagesC = new ArrayList<Image>();
        for (Image i : getOtherImages()) {
            imagesC.add(i.clone());
        }
        clone.setOtherImages(imagesC);
        
        return clone;

    }

    /**
     * Copie les propriétés d'un album dans l'objet courant.
     *
     * @param source la source dont il faut copier les infos dans l'objet
     * courant.
     */
    public void copyProperties(Album source) {
        this.setAnnee(source.getAnnee());
        this.setArtiste(source.getArtiste());
        this.setChef(source.getChef());
        this.setOrchestre(source.getOrchestre());
        this.setCommentaire(source.getCommentaire());
        this.setCompilation(source.getCompliation());
        this.setCote(source.getCote());
        this.setCouleur(source.getCouleur());
        this.setEditeur(source.getEditeur());
        this.setEtat(source.getEtat());
        this.setGenre(source.getGenre());
        this.setMusiciens(source.getMusiciens());
        this.setReference(source.getReference());
        this.setTaille(source.getTaille());
        this.setTitre(source.getTitre());
        this.setValeur(source.getValeur());
        this.setVitesse(source.getVitesse());
        this.setCompositeur(source.getCompositeur());
        this.setSupport(source.getSupport());
        this.setDateAchat(source.getDateAchat());
        this.setParolier(source.getParolier());
        this.setPays(source.getPays());
        this.setPossede(source.getPossede());
        this.setDiscogsId(source.getDiscogsId());
        
        this.setImage(source.getImage());
        this.setDisques(source.getDisques());
        this.setOtherImages(source.getOtherImages());
        this.dateAjout=source.getDateAjout();
    }

    @Override
    public String toString() {
        return "Album{ " + "artiste = " + artiste + " | titre = " + titre + " | annee = " + annee + '}';
    }

    public static final DataFlavor ALBUM_FLAVOR = new DataFlavor(Album.class, "vm-album-type");
    
    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{ALBUM_FLAVOR};
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return ALBUM_FLAVOR.equals(flavor);
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if (!isDataFlavorSupported(flavor)) {
            throw new UnsupportedFlavorException(flavor);
        }
        try {
            return this.clone();
        } catch (CloneNotSupportedException ex) {
            throw new IOException("ERROR PASTE", ex);
        }
    }
    
    public String getDureeTotale() {
        int dureeTotale = 0;
        for (Disque d : getDisques())  {
            for (Titre t : d.getFaceA()) {
                dureeTotale += t.getDuree();
            }
            for (Titre t : d.getFaceB()) {
                dureeTotale += t.getDuree();
            }
            if (dureeTotale == 0) {
                return null;
            }
        }
        int min = dureeTotale / 60;
        int sec = dureeTotale % 60;
        
        String secS = sec < 10 ? "0" + sec : "" + sec;
        return min+":"+secS;
    }
    
    public void renumberDiscs() {
        int i = 1;
        for (Disque d : getDisques()) {
            d.setNum(i);
            i++;
        }
    }
}
