/*
XPAGES pour WebJaxe

Copyright (C) 2007 Observatoire de Paris-Meudon

Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier conformément aux dispositions de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; version 2 de la licence, ou encore (à votre choix) toute version ultérieure.

Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou D'ADAPTATION A UN OBJET PARTICULIER. Pour plus de détail, voir la Licence Publique Générale GNU .

Vous devez avoir reçu un exemplaire de la Licence Publique Générale GNU en même temps que ce programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, Etats-Unis.
*/

package xpages;

import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO; // JDK 1.4

import jaxe.equations.DialogueEquation;

/**
 * Copie des fichiers des contributions et réduction des images.
 * compilation: javac -d classes -encoding ISO-8859-1 FaireImages.java
 */
public class FaireImages {

    static final String slash = "/";

    public static void main(String[] args) {
        String cheminImages = null;
        transfo(args[0], args[1], args[2], args[3]);
        System.exit(0);
    }
    
    /**
     * Réduction d'une image ou animation, et création éventuelle d'un fichier HTML correspondant.
     *
     * @param dossierfichier   chemin vers le dossier de la contribution du fichier image
     * @param nomfichier   chemin vers l'image relatif à dossierfichier
     * @param localisation localisation de l'image: texte|page|icône
     * @param dossier      chemin absolu du dossier dans lequel placer les images d'origine et réduites
     *        (dossier du site web)
     * @return un object de la classe Resultat
     * (utiliser resok, largeur1, hauteur1, largeur2 et hauteur2 pour lire le résultat)
     */
    public static Object transfo(String dossierfichier, String nomfichier, String localisation, String dossierres) {
        //System.out.println("transfo " + nomfichier + " " + localisation + " " + dossierres);
        Resultat res = new Resultat();
        try {
            File fichier = new File(dossierfichier + slash + nomfichier);
            if (!fichier.exists() || !fichier.isFile()) {
                erreur("Fichier introuvable: " + fichier.getAbsolutePath());
                res.erreur = true;
                return(res);
            }
            
            int hmax;
            int wmax;
            File petitf;
            
            if ("icône".equals(localisation)) {
                hmax = 100;
                wmax = 100;
                petitf = new File(dossierres + slash + "images_icone" + slash + nomfichier);
            } else if ("page".equals(localisation)) {
                hmax = 300;
                wmax = 300;
                petitf = new File(dossierres + slash + "images_page" + slash + nomfichier);
            } else {
                hmax = 500;
                wmax = 500;
                petitf = new File(dossierres + slash + "images_texte" + slash + nomfichier);
            }
            int h=0,w=0,h2=0,w2=0;
            
            String nomsansext = nomfichier;
            if (nomsansext.indexOf('.') != -1)
                nomsansext = nomsansext.substring(0, nomsansext.lastIndexOf('.'));
            
            ImAnim imanim = ImAnimFactory.create(fichier);
            if (imanim == null) {
                erreur("Format inconnu: " + fichier.getName());
                res.erreur = true;
                return(res);
            }
            imanim.open(fichier);
            w = imanim.getWidth();
            h = imanim.getHeight();
            //System.out.println(fsite.getName()+ " w="+w+" h="+h);
            if (w < 0 || h < 0) {
                erreur("Erreur pour trouver les dimensions de " + nomfichier);
                w = 0;
                h = 0;
            }
            res.largeur1 = w;
            res.hauteur1 = h;
            if ((w > wmax || h > hmax) && !(imanim instanceof MPEGAnim) && !(imanim instanceof OggAnim)) {
                File dirpetitf = new File(petitf.getParent());
                if (!dirpetitf.exists())
                    dirpetitf.mkdirs();
                if ((1.0*w)/wmax > (1.0*h)/hmax) {
                    w2 = wmax;
                    h2 = (int)Math.round(((1.0*wmax)/w) * h);
                    if (h2 > hmax)
                        h2 = hmax;
                } else {
                    h2 = hmax;
                    w2 = (int)Math.round(((1.0*hmax)/h) * w);
                    if (w2 > wmax)
                        w2 = wmax;
                }
                res.reduit = true;
                res.largeur2 = w2;
                res.hauteur2 = h2;
                if (!petitf.exists() || fichier.lastModified() > petitf.lastModified()) {
                    message("  réduction de " + nomfichier + " de " + w + " x " + h + " en " + w2 + " x " + h2);
                    imanim.rescale(petitf, w2, h2);
                }
            }
            if (imanim instanceof GIFAnim && imanim.getImageCount() > 1) {
                res.animation = true;
                File fimfixe = new File(dossierres + slash + "images_anims" + slash +
                    nomsansext + ".gif");
                if (!fimfixe.exists() || fichier.lastModified() > fimfixe.lastModified()) {
                    fimfixe.getParentFile().mkdirs();
                    creerImageFixe(imanim, fimfixe);
                }
            }
            return(res);
        } catch (Exception ex) {
            erreur(ex);
            res.erreur = true;
            return(res);
        }
    }
    
    /**
     * Renvoit "ok" si tout s'est bien passé mais sans réduction, "réduit" s'il y a eu une
     * réduction, et "erreur" si une erreur s'est produite.
     */
    public static String resok(Object o) {
        if (!(o instanceof Resultat))
            return("erreur");
        Resultat res = (Resultat)o;
        if (res.erreur)
            return("erreur");
        if (res.reduit)
            return("réduit");
        return("ok");
    }
    
    /**
     * Largeur initiale
     */
    public static int largeur1(Object o) {
        if (!(o instanceof Resultat))
            return(300);
        Resultat res = (Resultat)o;
        return(res.largeur1);
    }
    
    /**
     * Hauteur initiale
     */
    public static int hauteur1(Object o) {
        if (!(o instanceof Resultat))
            return(300);
        Resultat res = (Resultat)o;
        return(res.hauteur1);
    }
    
    /**
     * Largeur après réduction
     */
    public static int largeur2(Object o) {
        if (!(o instanceof Resultat))
            return(300);
        Resultat res = (Resultat)o;
        return(res.largeur2);
    }
    
    /**
     * Hauteur après réduction
     */
    public static int hauteur2(Object o) {
        if (!(o instanceof Resultat))
            return(300);
        Resultat res = (Resultat)o;
        return(res.hauteur2);
    }
    
    /**
     * Renvoie true si le fichier correspond à une animation GIF
     */
    public static boolean animation(Object o) {
        if (!(o instanceof Resultat))
            return(false);
        Resultat res = (Resultat)o;
        return(res.animation);
    }
    
    public static void creerImageFixe(ImAnim anim, File newf) {
        int width = anim.getWidth();
        int height = anim.getHeight();
        int width2 = width;
        int height2 = height;
        if (width2 > 250) {
            width2 = 250;
            height2 = (int)Math.round((250.0/width) * height);
        }
        int n = anim.getImageCount();
        try {
            BufferedImage buffImage = new BufferedImage(width2*2, height2*2, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = buffImage.createGraphics();
            WaitingObserver wobs = new WaitingObserver();
            Image img1 = anim.getImage(n/4+1);
            Image img1b;
            if (width2 != width)
                img1b = img1.getScaledInstance(width2, height2, Image.SCALE_SMOOTH);
            else
                img1b = img1;
            if (!g2.drawImage(img1b, 0, 0, wobs)) {
                wobs.attendre();
                g2.drawImage(img1b, 0, 0, null);
            }
            wobs = new WaitingObserver();
            Image img2 = anim.getImage(n/2+1);
            Image img2b;
            if (width2 != width)
                img2b = img2.getScaledInstance(width2, height2, Image.SCALE_SMOOTH);
            else
                img2b = img2;
            if (!g2.drawImage(img2b, width2, 0, wobs)) {
                wobs.attendre();
                g2.drawImage(img2b, width2, 0, null);
            }
            wobs = new WaitingObserver();
            Image img3 = anim.getImage((n*3)/4+1);
            Image img3b;
            if (width2 != width)
                img3b = img3.getScaledInstance(width2, height2, Image.SCALE_SMOOTH);
            else
                img3b = img3;
            if (!g2.drawImage(img3b, 0, height2, wobs)) {
                wobs.attendre();
                g2.drawImage(img3b, 0, height2, null);
            }
            wobs = new WaitingObserver();
            Image img4 = anim.getImage(n);
            Image img4b;
            if (width2 != width)
                img4b = img4.getScaledInstance(width2, height2, Image.SCALE_SMOOTH);
            else
                img4b = img4;
            if (!g2.drawImage(img4b, width2, height2, wobs)) {
                wobs.attendre();
                g2.drawImage(img4b, width2, height2, null);
            }
            g2.setColor(Color.black);
            g2.drawRect(0, 0, width2*2-1, height2*2-1);
            g2.drawLine(0, height2, width2*2-1, height2);
            g2.drawLine(width2, 0, width2, height2*2-1);
            g2.dispose();
            
            ImageIO.write(buffImage, "PNG", newf);
        } catch (IOException ex) {
            erreur(ex);
        }
    }
    
    public static String creerImageEquation(String contenu, String numero, String labelcontrib, String dossierres) {
        BufferedImage img = lireImageEquation(contenu);
        if (img == null)
            return("erreur");
        File dossierEquations = new File(dossierres, "equations_" + labelcontrib);
        if (!dossierEquations.exists())
            dossierEquations.mkdirs();
        File f = new File(dossierEquations, "equation" + numero + ".png");
        try {
            DialogueEquation.enregistrerImage(img, f);
        } catch (final IOException ex) {
            erreur("creerImageEquation(): IOException: " + ex.getMessage());
            return("erreur");
        }
        return("ok"); // si on met void ça ne marche pas avec Java 6
    }
    
    
    /**
     * Lecture de l'image de l'équation au format PNG encodé en base64 dans l'élément
     */
    protected static BufferedImage lireImageEquation(String contenu) {
        try {
            return(ImageIO.read(new DecodeurBase64(new StringReader(contenu))));
        } catch (IOException ex) {
            erreur("lireImageEquation(): IOException: " + ex.getMessage());
            return(null);
        }
    }
    
    static void erreur(Exception ex) {
        ex.printStackTrace();
        erreur(ex.getClass().getName() + ": " + ex.getMessage());
    }
    
    static void erreur(String msg) {
        System.err.println(msg);
    }
    
    static void message(String msg) {
        System.out.println(msg);
    }
    
    public static void copierFichier(File inputFile, File outputFile) throws IOException {
        int bufSize = 1024; 
        BufferedInputStream in  = new BufferedInputStream( 
                                      new FileInputStream(inputFile),bufSize); 
        BufferedOutputStream out = new BufferedOutputStream( 
                                      new FileOutputStream(outputFile), bufSize); 
        int length = 32;  
        byte[] ch = new byte[length]; 
        while((length = in.read(ch))!= -1) { 
            out.write(ch,0,length); 
        } 
        out.flush(); 
        in.close(); 
        out.close(); 
    }
    
    /**
     * Copie le dossier d'une contrib (dont le label est passé en paramètre) vers
     * un dossier pages_label dans le site dont le chemin est passé en paramètre,
     * sans le fichier XML de la contrib.
     * Utilisé pour XPAGES.
     */
    public static String copierDossierContrib(String cheminContrib, String cheminSite) throws IOException {
        File d1 = new File(cheminContrib);
        File site = new File(cheminSite);
        if (!site.exists())
            if (!site.mkdir())
                throw new IOException("Impossible de créer le répertoire: " + site.getPath());
        File d2 = new File(site, "pages_" + d1.getName());
        //copierDossier(d1, d2);
        // on exclut la copie du dossier "site" et du fichier XML
        if (!d2.exists())
            if (!d2.mkdir())
                throw new IOException("Impossible de créer le répertoire: " + d2.getPath());
        File[] liste = d1.listFiles();
        if (liste == null) {
            erreur("pas un dossier? " + d1.getPath());
            return("erreur");
        }
        for (int i=0; i<liste.length; i++)
            if (liste[i].isDirectory() && !d2.getAbsolutePath().startsWith(liste[i].getAbsolutePath()) &&
                    !liste[i].getName().equals("site"))
                copierDossier(liste[i], new File(d2, liste[i].getName()));
        for (int i=0; i<liste.length; i++)
            if (!liste[i].isDirectory() && !liste[i].getName().equals(d1.getName() + ".xml"))
                copierFichier(liste[i], new File(d2, liste[i].getName()));
        return("ok"); // si on met void ça ne marche pas avec Java 6
    }
    
    /**
     * Copie un dossier d1 et son contenu récursivement (la destination d2 ne doit pas exister).
     * Ne copie pas bêtement le dossier destination à l'intérieur de lui-même.
     */
    public static void copierDossier(File d1, File d2) throws IOException {
        if (!d2.exists())
            if (!d2.mkdir())
                throw new IOException("Impossible de créer le répertoire: " + d2.getPath());
        File[] liste = d1.listFiles();
        if (liste == null)
            erreur("pas un dossier? " + d1.getPath());
        for (int i=0; i<liste.length; i++)
            if (liste[i].isDirectory() && !d2.getAbsolutePath().startsWith(liste[i].getAbsolutePath()))
                copierDossier(liste[i], new File(d2, liste[i].getName()));
        for (int i=0; i<liste.length; i++)
            if (!liste[i].isDirectory())
                copierFichier(liste[i], new File(d2, liste[i].getName()));
    }
    
    /**
     * Même chose avec des strings (sinon XSLTC n'arrive pas à compiler...)
     */
    public static String copierDossier(String d1, String d2) throws IOException {
        copierDossier(new File(d1), new File(d2));
        return("ok");
    }
    
    
    static class Resultat extends Object {
        boolean reduit;
        boolean erreur;
        int largeur1;
        int hauteur1;
        int largeur2;
        int hauteur2;
        boolean animation;
        
        Resultat() {
            reduit = false;
            erreur = false;
            largeur1 = 0;
            hauteur1 = 0;
            largeur2 = 0;
            hauteur2 = 0;
            animation = false;
        }
    }
    
    
    static class WaitingObserver implements ImageObserver {
        boolean stillwaiting = true;
        public void attendre() {
            while (stillwaiting) {
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) {
                    System.err.println("InterruptedException: " + ex.getMessage());
                }
            }
        }
        public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
            if ((infoflags & ImageObserver.ALLBITS) != 0) {
                stillwaiting = false;
                return false;
            }
            if ((infoflags & ImageObserver.FRAMEBITS) != 0) {
                stillwaiting = false;
                return true;
            }
            if ((infoflags & ImageObserver.ERROR) != 0)
                System.err.println("imageUpdate: ERROR");
            if ((infoflags & ImageObserver.ABORT) != 0) {
                System.err.println("imageUpdate: ABORT");
                stillwaiting = false;
                return false;
            }
            return true;
        }
    }
}
