package be.msyben.vinylManager.tools.importexport;

import be.msyben.vinylManager.entities.*;
import be.msyben.vinylManager.exceptions.VinylManagerException;
import be.msyben.vinylManager.properties.Language;
import be.msyben.vinylManager.tools.Tools;
import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.digester.Digester;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * Parse un fichier xml pour importer une collection.
 * @author Mathieu Syben
 */
public class XmlImport {
    /**
     * Import une collection en XML.
     * @param f Le fichier à importer.
     * @return La collection contenue dans le fichier.
     * @throws VinylManagerException En cas d'erreur.
     */
    public static Collection importXml(File f) throws VinylManagerException {
        XmlImport importer = new XmlImport();
        importer.parse(f);
        return importer.getCollection();
    }
    private Collection collection = new Collection();
    private String path;
    
    
    private XmlImport() {
        
    }
    
    /**
     * Renvoie la collection parsée.
     * @return La collection parsée.
     */
    public Collection getCollection() {
        return collection;
    }
    
    /**
     * Parse le fichier.
     * @param f Le fichier à parser.
     * @throws VinylManagerException En cas d'erreur. 
     */
    public void parse(File f) throws VinylManagerException {
        path = f.getParent();
        Digester digester = new Digester();
        
        digester.setErrorHandler(new ErrorHandler() {
            
            @Override
            public void warning(SAXParseException exception) throws SAXException {
                throw new SAXException(exception);
            }
            
            @Override
            public void error(SAXParseException exception) throws SAXException {
                throw new SAXException(exception);
            }
            
            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
                throw new SAXException(exception);
            }
        });
        digester.push(this);
        digester.setValidating(false);
        ConvertUtils.register(new MyDateConverter(), Date.class);
        ConvertUtils.register(new MyFileByteArrayConverter(), byte[].class);
        
        digester.addObjectCreate("collection/album", Album.class);
        digester.addCallMethod("collection/album/artiste", "setArtiste", 0);
        digester.addCallMethod("collection/album/titre", "setTitre", 0);
        digester.addCallMethod("collection/album/compositeur", "setCompositeur", 0);
        digester.addCallMethod("collection/album/parolier", "setParolier", 0);
        digester.addCallMethod("collection/album/annee", "setAnnee", 0);
        digester.addCallMethod("collection/album/anneeParution", "setAnneeParution", 0);
        digester.addCallMethod("collection/album/pays", "setPays", 0);
        digester.addCallMethod("collection/album/commentaire", "setCommentaire", 0);
        digester.addCallMethod("collection/album/genre", "setGenre", 0);
        digester.addCallMethod("collection/album/editeur", "setEditeur", 0);
        digester.addCallMethod("collection/album/etat", "setEtat", 0);
        digester.addCallMethod("collection/album/musiciens", "setMusiciens", 0);
        digester.addCallMethod("collection/album/chef", "setChef", 0);
        digester.addCallMethod("collection/album/orchestre", "setOrchestre", 0);
        digester.addCallMethod("collection/album/location", "setLocation", 0);
        digester.addCallMethod("collection/album/reference", "setReference", 0);
        digester.addCallMethod("collection/album/cote", "setCote", 0);
        digester.addCallMethod("collection/album/valeur", "setValeur", 0);
        digester.addCallMethod("collection/album/possede", "setPossede", 0, new Class[]{Boolean.class});
        digester.addCallMethod("collection/album/support", "setSupport", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/compilation", "setCompilation", 0, new Class[]{Boolean.class});
        digester.addCallMethod("collection/album/dateAchat", "setDateAchat", 0, new Class[]{Date.class});
        digester.addObjectCreate("collection/album/image", Image.class);
        digester.addCallMethod("collection/album/image/nom", "setNom", 0);
        digester.addCallMethod("collection/album/image/fichier", "setContenu", 0, new Class[]{byte[].class});
        digester.addSetNext("collection/album/image", "setImageFromXML");
        digester.addObjectCreate("collection/album/disques/disque", Disque.class);
        digester.addCallMethod("collection/album/disques/disque/num", "setNum", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/disqueEtat", "setEtat", 0);
        digester.addCallMethod("collection/album/disques/disque/couleur", "setCouleur", 0);
        digester.addCallMethod("collection/album/disques/disque/vitesse", "setVitesse", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/taille", "setTaille", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/canaux", "setCanaux", 0, new Class[]{Integer.class});
        digester.addObjectCreate("collection/album/disques/disque/faceA/plage", Titre.class);
        digester.addCallMethod("collection/album/disques/disque/faceA/plage/duree", "setDuree", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/faceA/plage/numero", "setNum", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/faceA/plage/titre", "setNom", 0);
        digester.addCallMethod("collection/album/disques/disque/faceA/plage/artiste", "setArtiste", 0);
        digester.addCallMethod("collection/album/disques/disque/faceA/plage/annee", "setAnnee", 0);
        digester.addCallMethod("collection/album/disques/disque/faceA/plage/paroles", "setLyrics", 0);
        digester.addSetNext("collection/album/disques/disque/faceA/plage", "addTitreToFaceA");
        digester.addObjectCreate("collection/album/disques/disque/faceB/plage", Titre.class);
        digester.addCallMethod("collection/album/disques/disque/faceB/plage/duree", "setDuree", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/faceB/plage/numero", "setNum", 0, new Class[]{Integer.class});
        digester.addCallMethod("collection/album/disques/disque/faceB/plage/titre", "setNom", 0);
        digester.addCallMethod("collection/album/disques/disque/faceB/plage/artiste", "setArtiste", 0);
        digester.addCallMethod("collection/album/disques/disque/faceB/plage/annee", "setAnnee", 0);
        digester.addCallMethod("collection/album/disques/disque/faceB/plage/paroles", "setLyrics", 0);
        digester.addSetNext("collection/album/disques/disque/faceB/plage", "addTitreToFaceB");
        digester.addSetNext("collection/album/disques/disque", "addDisque");
        digester.addSetNext("collection/album", "addAlbum");
        
        InputStream is = null;
        try {
            is = new FileInputStream(f);
            
            digester.parse(is);
            
        } catch (IOException ex) {
            throw new VinylManagerException(Language.getInstance().get("xml.import.error"), ex);
        } catch (SAXException ex) {
            throw new VinylManagerException(Language.getInstance().get("xml.import.error"), ex);
        } finally {
            Tools.closeQuietly(is);
        }
        
    }
    
    /**
     * Ajoute un album à la collection.
     * @param a L'album à ajouter.
     */
    public void addAlbum(Album a) {
        collection.getAlbums().add(a);
    }
    
    /**
     * Classe de conversion de fichier.
     */
    class MyFileByteArrayConverter implements Converter {

        @Override
        public Object convert(Class clazz, Object value) {
            if (clazz.equals(byte[].class)) {
                if (value == null) {
                    return null;
                }
                String fileName = (String) value;
                fileName = XmlImport.this.path+File.separator+fileName;
                File f = new File(fileName);
                if (!f.exists() || !f.isFile()) {
                    throw new IllegalArgumentException("File not exists");
                }
                InputStream is = null;
                ByteArrayOutputStream baos = null;
                try {
                    is = new FileInputStream(f);
                    baos = new ByteArrayOutputStream();
                    Tools.copy(is, baos);
                    return baos.toByteArray();
                } catch (IOException ex) {
                    throw new IllegalArgumentException(ex);
                } finally {
                    Tools.closeQuietly(is);
                    Tools.closeQuietly(baos);
                }
            }else {
                throw new IllegalArgumentException("Expected byte[] class");
            }
        }
        
    }

    /**
     * Classe de conversion de date.
     */
    class MyDateConverter implements Converter {

        @Override
        public Object convert(Class clazz, Object value) {
            if (clazz.equals(Date.class)) {
                
                if (value == null) {
                    return null;
                }
                String svalue = (String) value;
                if (svalue.isEmpty()) {
                    return null;
                }
                
                DateFormat df= new SimpleDateFormat("dd/MM/yyyy");
                try {
                    return df.parse(svalue);
                } catch (ParseException pe) {
                    throw new IllegalArgumentException(pe);
                } catch (ClassCastException cce) {
                    throw new IllegalArgumentException(cce);
                }
            } else {
                throw new IllegalArgumentException("Expected Date class");
            }
        }
    }
    
    
}
