package be.msyben.vinylManager.tools.musicBrainz;

import be.msyben.vinylManager.VinylManager;
import be.msyben.vinylManager.entities.Album;
import be.msyben.vinylManager.entities.Titre;
import be.msyben.vinylManager.exceptions.VinylManagerException;
import be.msyben.vinylManager.properties.Support;
import be.msyben.vinylManager.tools.Tools;
import be.msyben.vinylManager.tools.log.VinylManagerLogger;
import be.msyben.vinylManager.tools.musicBrainz.entities.Release;
import be.msyben.vinylManager.tools.musicBrainz.entities.ReleaseResponse;
import be.msyben.vinylManager.tools.musicBrainz.entities.Track;
import be.msyben.vinylManager.tools.musicBrainz.entities.TrackMedia;
import be.msyben.vinylManager.tools.musicBrainz.entities.TracksResponse;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;

/**
 * Interface avec musicBrainz pour la recherche des pistes d'un album.
 * @author Mathieu Syben
 */
public class MusicBrainzInterface {
    private static final String USER_AGENT = "VinylManager/"+VinylManager.version+" + (http://msyben.be/VinylManager)";
    
    private static final String MB_URL_WS = "http://musicbrainz.org/ws/2/";
    
    /**
     * Récupère la liste des albums possibles.
     * @param album l'album
     * @return La liste des albums possibles
     * @throws VinylManagerException en cas d'erreur.
     */
    public static List<Release> getPossibleReleases(Album album) throws VinylManagerException {
        
        String format = null;
        if (album.getSupport() != null) { 
            switch (Support.getSupport(album.getSupport())) {
                case vinyl : format = "vinyl"; break;
                case cd : format = "cd"; break;
            }
        } 
        
        String artist;
        if (album.getCompliation()) {
            artist = "Various Artists";
        } else {
            artist = album.getArtiste();
        }
        
        String query = "artist:\""+artist+"\" and release:\""+album.getTitre()+"\"";
        if (format != null) {
            query += " and format:\""+format+"\"";
        }
        
        Client client = ClientBuilder.newClient();
        WebTarget target = client.target(MB_URL_WS).path("release")
                .queryParam("query", query)
                .queryParam("limit", "100")
                .queryParam("fmt", "json");
        
        try {
            Response resp = target.request(MediaType.APPLICATION_JSON_TYPE).header("User-Agent", USER_AGENT).get();
            if (resp.getStatus() == 200) {
                String respValue = resp.readEntity(String.class);
                ReleaseResponse rr = parseReleaseResponse(respValue);
                List<Release> ret = new ArrayList<>();
                for (Release r : rr.getReleases()) {
                    r.setDistance(Tools.distance(r.getTitle(), album.getTitre()));
                    ret.add(r);
                }
                //Collections.sort(ret);
                return ret;
            } else {
                throw new VinylManagerException("Erreur de récupération des releases musicbrainz " + resp.getStatus());
            }
        } catch (Exception ex) {
            throw new VinylManagerException(ex);
        }
    }
    
    /**
     * Récupère les pistes.
     * @param release la release
     * @param compilation indique s'il s'agit d'une compilation
     * @return Une liste à plusieurs éléments avec les titres de la face A et de la face B.
     * @throws VinylManagerException en cas d'erreur.
     */
    public static List<List<Titre>> getTracks(Release release, boolean compilation) throws VinylManagerException {
        
        Client client = ClientBuilder.newClient();
        WebTarget target = client.target(MB_URL_WS).path("release").path(release.getId())
                .queryParam("inc", "recordings"+(compilation?"+artist-credits":""))
                .queryParam("fmt", "json");
        
        try {
            Response resp = target.request(MediaType.APPLICATION_JSON_TYPE).header("User-Agent", USER_AGENT).get();
            if (resp.getStatus() == 200) {
                String respValue = resp.readEntity(String.class);
                TracksResponse tr = parseTracksResponse(respValue);
                
                List<List<Titre>> ret = new ArrayList<>();
                
                char lastFace = 'A'-1;
                int nbr = 1;
                if (tr != null && tr.getMedia() != null && tr.getMedia().length > 0) {
                    
                    for (TrackMedia m : tr.getMedia()) {
                        
                        ret.add(new ArrayList<Titre>());
                        if (ret.size() % 2 == 0) {
                            // Si CD, ce n'est pas une face B...
                            ret.add(new ArrayList<Titre>());
                        }
                        nbr = 1;
                        lastFace++;
                        
                        for (Track t : m.getTracks()) {
                            char first;
                            if (t.getNumber() != null && t.getNumber().length() > 0) {
                                first = t.getNumber().toUpperCase().charAt(0);
                            } else {
                                continue;
                            }
                            if (first >= 'A' && first <= 'Z') {
                                if (first != lastFace) {
                                    lastFace = first;
                                    ret.add(new ArrayList<Titre>());
                                    nbr = 1;
                                }
                            }

                            Titre tt = new Titre();
                            tt.setNom(t.getTitle());
                            tt.setNum(nbr++);
                            if (t.getLength() > 0) {
                                tt.setDuree(t.getLength() / 1000);
                            }

                            if (compilation) {
                                if (t.getArtistCredit() != null && t.getArtistCredit().length > 0 && 
                                        t.getArtistCredit()[0] != null && t.getArtistCredit()[0].getArtist() != null &&
                                        t.getArtistCredit()[0].getArtist().getName() != null) {
                                    tt.setArtiste(t.getArtistCredit()[0].getArtist().getName());
                                }
                            }

                            ret.get(ret.size()-1).add(tt);
                        }
                    }
                }
                
                if (ret.size() % 2 == 1) {
                    ret.add(new ArrayList<Titre>());
                }
                
                return ret;
                
            } else {
                throw new VinylManagerException("Erreur de récupération des releases musicbrainz " + resp.getStatus());
            }
        } catch (Exception ex) {
            throw new VinylManagerException(ex);
        }
    }
    
    private static TracksResponse parseTracksResponse(String json) {
        try {
            GsonBuilder builder = new GsonBuilder();
            Gson gson = builder.create();
            return gson.fromJson(json, TracksResponse.class); 
        } catch (Exception e) {
            VinylManagerLogger.log("Erreur lors du décodage de la réponse", e);
            throw e;
        }
    }
    
    private static ReleaseResponse parseReleaseResponse(String json) {
        try {
            GsonBuilder builder = new GsonBuilder();
            Gson gson = builder.create();
            return gson.fromJson(json, ReleaseResponse.class); 
        } catch (Exception e) {
            VinylManagerLogger.log("Erreur lors du décodage de la réponse", e);
            throw e;
        }
    }
}
