/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

package org.apache.fop.image.loader.batik;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.transform.Source;

import org.apache.commons.io.EndianUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.batik.transcoder.wmf.WMFConstants;
import org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore;

import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader;
import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.io.XmlSourceUtil;

import org.apache.fop.util.UnclosableInputStream;

/**
 * Image preloader for WMF images (Windows Metafile).
 */
public class PreloaderWMF extends AbstractImagePreloader {

    /** Logger instance */
    private static Log log = LogFactory.getLog(PreloaderWMF.class);

    private boolean batikAvailable = true;

    /** {@inheritDoc} */
    public ImageInfo preloadImage(String uri, Source src, ImageContext context)
            throws IOException {
        if (!ImageUtil.hasInputStream(src)) {
            return null;
        }
        ImageInfo info = null;
        if (batikAvailable) {
            try {
                Loader loader = new Loader();
                info = loader.getImage(uri, src, context);
            } catch (NoClassDefFoundError e) {
                batikAvailable = false;
                log.warn("Batik not in class path", e);
                return null;
            }
        }
        if (info != null) {
            XmlSourceUtil.closeQuietly(src); //Image is fully read
        }
        return info;
    }

    /**
     * This method is put in another class so that the class loader does not
     * attempt to load Batik related classes when constructing the WMFPreloader
     * class.
     */
    private final class Loader {

        private Loader() {
        }

        private ImageInfo getImage(String uri, Source src,
                ImageContext context) {
            // parse document and get the size attributes of the svg element

            InputStream in = new UnclosableInputStream(XmlSourceUtil.needInputStream(src));
            try {
                in.mark(4 + 1);

                DataInputStream din = new DataInputStream(in);
                int magic = EndianUtils.swapInteger(din.readInt());
                din.reset();
                if (magic != WMFConstants.META_ALDUS_APM) {
                    IOUtils.closeQuietly(din);
                    return null; //Not a WMF file
                }

                WMFRecordStore wmfStore = new WMFRecordStore();
                wmfStore.read(din);
                IOUtils.closeQuietly(din);

                int width = wmfStore.getWidthUnits();
                int height = wmfStore.getHeightUnits();
                int dpi = wmfStore.getMetaFileUnitsPerInch();

                ImageInfo info = new ImageInfo(uri, "image/x-wmf");
                ImageSize size = new ImageSize();
                size.setSizeInPixels(width, height);
                size.setResolution(dpi);
                size.calcSizeFromPixels();
                info.setSize(size);
                ImageWMF img = new ImageWMF(info, wmfStore);
                info.getCustomObjects().put(ImageInfo.ORIGINAL_IMAGE, img);

                return info;
            } catch (NoClassDefFoundError ncdfe) {
                try {
                    in.reset();
                } catch (IOException ioe) {
                    // we're more interested in the original exception
                }
                batikAvailable = false;
                log.warn("Batik not in class path", ncdfe);
                return null;
            } catch (IOException e) {
                // If the svg is invalid then it throws an IOException
                // so there is no way of knowing if it is an svg document

                log.debug("Error while trying to load stream as an WMF file: "
                                       + e.getMessage());
                // assuming any exception means this document is not svg
                // or could not be loaded for some reason
                try {
                    in.reset();
                } catch (IOException ioe) {
                    // we're more interested in the original exception
                }
                return null;
            }
        }
    }

}
