/* FichothequeLib_Tools - Copyright (c) 2014-2022 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.tools.conversion.imagetoillustration;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.IIOException;
import net.fichotheque.ExistingIdException;
import net.fichotheque.Fichotheque;
import net.fichotheque.album.AlbumEditor;
import net.fichotheque.album.Illustration;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.fiche.FicheAPI;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.ImageItem;
import net.fichotheque.corpus.fiche.Item;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.croisement.CroisementEditor;
import net.fichotheque.syntax.FormSyntax;
import net.fichotheque.tools.croisement.CroisementChangeEngine;
import net.fichotheque.utils.AlbumUtils;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.images.ImagesUtils;
import net.mapeadores.util.io.IOUtils;
import net.mapeadores.util.localisation.LocalisationUtils;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.logging.MultiMessageHandler;
import net.mapeadores.util.mimetype.MimeTypeConstants;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class ImageToIllustrationConverter {

    private final MultiMessageHandler messageHandler;
    private final Map<String, Illustration> illustrationMap = new HashMap<String, Illustration>();
    private final AlbumEditor albumEditor;
    private final CroisementEditor croisementEditor;
    private final Fichotheque fichotheque;
    private final boolean testOnly;

    public ImageToIllustrationConverter(AlbumEditor albumEditor, CroisementEditor croisementEditor, MultiMessageHandler messageHandler, boolean testOnly) {
        this.albumEditor = albumEditor;
        this.croisementEditor = croisementEditor;
        this.messageHandler = messageHandler;
        this.fichotheque = albumEditor.getAlbum().getFichotheque();
        this.testOnly = testOnly;
    }

    public void convert(FicheMeta ficheMeta, FieldKey fieldKey, String baseUrl, String mode, int weight) {
        String currentURI = ficheMeta.getGlobalId();
        messageHandler.setCurrentSource(currentURI);
        CroisementChangeEngine croisementChangeEngine = CroisementChangeEngine.appendEngine(ficheMeta);
        FicheAPI ficheAPI = ficheMeta.getFicheAPI(false);
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY: {
                FicheItem ficheItem = (FicheItem) ficheAPI.getValue(fieldKey);
                if (ficheItem != null) {
                    Illustration illustration = convertFicheItem(ficheItem, baseUrl, currentURI);
                    if (illustration != null) {
                        croisementChangeEngine.addLien(illustration, mode, weight);
                    }
                }
                break;
            }
            case FieldKey.INFO_CATEGORY: {
                FicheItems ficheItems = (FicheItems) ficheAPI.getValue(fieldKey);
                if (ficheItems != null) {
                    for (FicheItem ficheItem : ficheItems) {
                        Illustration illustration = convertFicheItem(ficheItem, baseUrl, currentURI);
                        if (illustration != null) {
                            croisementChangeEngine.addLien(illustration, mode, weight);
                        }
                    }
                }
                break;
            }
        }
        if (!testOnly) {
            croisementEditor.updateCroisements(ficheMeta, croisementChangeEngine.toCroisementChanges());
        }
    }

    private Illustration convertFicheItem(FicheItem ficheItem, String baseUrl, String currentURI) {
        String src;
        if (ficheItem instanceof Item) {
            src = ((Item) ficheItem).getValue();
        } else if (!(ficheItem instanceof ImageItem)) {
            src = ((ImageItem) ficheItem).getSrc();
        } else {
            addWarning("_ error.unsupported.ficheitemtype", FormSyntax.toString(ficheItem, fichotheque, null));
            return null;
        }
        return getIllustration(src, baseUrl, currentURI);
    }

    private Illustration getIllustration(String src, String baseUrl, String currentURI) {
        String urlString;
        if (!StringUtils.isAbsoluteUrlString(src)) {
            urlString = baseUrl + src;
        } else {
            urlString = src;
        }
        if (illustrationMap.containsKey(urlString)) {
            return illustrationMap.get(urlString);
        }
        messageHandler.setCurrentSource(currentURI + "/" + urlString);
        File file = download(urlString);
        if (file == null) {
            return null;
        }
        if (testOnly) {
            file.delete();
            return null;
        }
        String fileName = file.getName();
        int idx = fileName.lastIndexOf(".");
        short formatType = AlbumUtils.formatTypeToShort(fileName.substring(idx + 1));
        Illustration illustration;
        try (InputStream is = new FileInputStream(file)) {
            illustration = albumEditor.createIllustration(-1, formatType);
            albumEditor.updateIllustration(illustration, is, formatType);
            illustrationMap.put(urlString, illustration);
            return illustration;
        } catch (ExistingIdException eie) {
            throw new ShouldNotOccurException("id= -1");
        } catch (IOException ioe) {
            addWarning("_ error.exception.io", ioe.getMessage());
            illustration = null;
        } catch (ErrorMessageException eme) {
            messageHandler.addMessage("warning.conversion", eme.getErrorMessage());
            illustration = null;
        } finally {
            file.delete();
        }
        return illustration;
    }

    private File download(String urlString) {
        URL imageURL;
        try {
            imageURL = new URL(urlString);
        } catch (MalformedURLException mue) {
            addWarning("_ error.wrong.url", urlString);
            return null;
        }
        String protocol = imageURL.getProtocol();
        if ((protocol == null) || (protocol.isEmpty())) {
            addWarning("_ error.wrong.url", urlString);
            return null;
        }
        if (!(protocol.equals("http"))) {
            addWarning("_ error.unknown.url_protocol", protocol);
            return null;
        }
        try {
            HttpURLConnection urlConnection = (HttpURLConnection) imageURL.openConnection();
            urlConnection.setInstanceFollowRedirects(true);
            urlConnection.connect();
            int code = urlConnection.getResponseCode();
            if (code != 200) {
                addWarning("_ error.unsupported.httpstatus", code);
                return null;
            }
            String format;
            String mimetype = urlConnection.getContentType();
            if ((mimetype != null) && (!mimetype.equals(MimeTypeConstants.OCTETSTREAM))) {
                int idx = mimetype.indexOf("/");
                format = checkExtension("img." + mimetype.substring(idx + 1));
                if (format == null) {
                    addWarning("_ error.unknown.url_mimetype", mimetype);
                    return null;
                }
            } else {
                int idx = urlString.lastIndexOf("/");
                format = checkExtension(urlString.substring(idx + 1));
                if (format == null) {
                    addWarning("_ error.unknown.extension", urlString.substring(idx + 1));
                    return null;
                }
            }
            try (InputStream is = urlConnection.getInputStream()) {
                byte[] array = IOUtils.toByteArray(is);
                return saveTmpFile(array, format, urlString);
            }
        } catch (IOException ioe) {
            addWarning("_ error.exception.io", ioe.getMessage());
            return null;
        }
    }

    private File saveTmpFile(byte[] array, String extension, String urlString) throws IOException {
        try {
            BufferedImage srcImg = ImagesUtils.read(new ByteArrayInputStream(array));
            if (srcImg == null) {
                addWarning("_ error.exception.iio", urlString);
                return null;
            }
        } catch (IIOException ioe) {
            if (ioe instanceof IIOException) {
                addWarning("_ error.exception.iio", ioe.getMessage());
            } else {
                addWarning("_ error.exception.io", ioe.getMessage());
            }
            return null;
        } catch (ErrorMessageException eme) {
            messageHandler.addMessage("warning.conversion", eme.getErrorMessage());
        }
        File tmpFile = File.createTempFile("illustration", "." + extension);
        try (OutputStream os = new FileOutputStream(tmpFile)) {
            IOUtils.copy(new ByteArrayInputStream(array), os);
        }
        return tmpFile;
    }

    private void addWarning(String messageKey, Object messageValue) {
        messageHandler.addMessage("warning.conversion", LocalisationUtils.toMessage(messageKey, messageValue));
    }

    private static String checkExtension(String fileName) {
        int idx = fileName.lastIndexOf(".");
        if (idx == -1) {
            return null;
        }
        String extension = fileName.substring(idx + 1);
        if (extension.equalsIgnoreCase("png")) {
            return "png";
        }
        if (extension.equalsIgnoreCase("jpg")) {
            return "jpg";
        }
        if (extension.equalsIgnoreCase("jpeg")) {
            return "jpg";
        }
        return null;
    }

}
