/*
     This file is part of libextractor.
     (C) 2002, 2003, 2004, 2007 Vidyut Samanta and Christian Grothoff

     libextractor is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published
     by the Free Software Foundation; either version 2, or (at your
     option) any later version.

     libextractor is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with libextractor; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
 */
package org.gnunet.libextractor;

import java.util.ArrayList;
import java.io.File;

/**
 * Java Binding for libextractor.
 *
 * @see Xtract
 * @author Christian Grothoff
 */ 
public final class Extractor {
	

    private static final boolean warn_;

    /**
     * LE version.  0 if LE was compiled without JNI/Java support, in which
     * case we better not call any native methods...
     */
    private static final int version_;

    /**
     * Cached list of Strings describing keyword types.
     */
    private final static String[] typeCache_;

    static {	
	// first, initialize warn_
	boolean warn = false;
	try {
	    if (System.getProperty("libextractor.warn") != null)
		warn = true;
	} catch (SecurityException se) {
	    // ignore
	} finally {
	    warn_ = warn;
	}

	// next, load library and determine version_
	int ver = 0;
	try {
	    System.loadLibrary("extractor_java");
	} catch (UnsatisfiedLinkError ule) {
	    ver = -1;
	    warn("Did not find libextractor_java library: " + ule);
	}
	if (ver == 0) {
	    try {
		ver = getVersionInternal();
	    } catch (UnsatisfiedLinkError ule) {
		// warn: libextractor compiled without Java support
		warn("libextractor library compiled without Java support: " + ule);
	    }
	}
	version_ = ver;

	// finally, initialize typeCache_
	if (ver > 0) {
	    typeCache_ = new String[getMaxTypeInternal()];
	} else {
	    typeCache_ = null;
	}
    }    

    private static void warn(String warning) {
	if (warn_)
	    System.err.println("WARNING: " + warning);
    }

    /**
     * @return -1 if LE library was not found, 0 if LE library
     *  was found but compiled without JNI support, otherwise
     *  the LE version number
     */
    public static int getVersion() {
	return version_;
    }

    /**
     * Get the 'default' extractor, that is an extractor that loads
     * the default set of extractor plugins.
     */
    public static Extractor getDefault() {
	if (version_ > 0)
	    return new Extractor(loadDefaultInternal());
	return new Extractor(0);
    }

    /**
     * Get the 'empty' extractor, that is an extractor that does not
     * have any plugins loaded.  This is useful to manually construct
     * an Extractor from scratch.
     */
    public static Extractor getEmpty() {
	return new Extractor(0L);
    }

    /**
     * Note that the current implementation of function is quite
     * costly, we should probably build a cache (String[]) of all
     * keyword types on startup and keep that around instead of
     * doing a native call each time (initialize cache lazily!,
     * just determine the size in the static initializer!).
     *
     * @throws IllegalArgumentException if the type is not within range
     * @return an empty string if LE was not loaded
     */
    public static String getTypeAsString(int type) {
	if (version_ > 0) {
	    if ( (type >= 0) && 
		 (type <= typeCache_.length) ) {
		if (typeCache_[type] == null)
		    typeCache_[type]
			= getTypeAsStringInternal(type);
		return typeCache_[type];
	    }
	    throw new IllegalArgumentException("Type out of range [0,"+typeCache_.length+")");
	}
	return "";
    }

    /**
     * Handle to the list of plugins (a C pointer, long to support
     * 64-bit architectures!).
     */
    private long pluginHandle_;

    /**
     * Creates an extractor.
     *
     * @param pluginHandle the internal handle (C pointer!) refering
     *   to the list of plugins.  0 means no plugins.
     */
    private Extractor(long pluginHandle) {
	pluginHandle_ = pluginHandle;
    }

    protected void finalize() {
	if (pluginHandle_ != 0)
	    unloadInternal(pluginHandle_);
    }

    public void unloadPlugin(String pluginName) {
	if (pluginHandle_ != 0) 
	    pluginHandle_ = unloadPlugin(pluginHandle_,
					 pluginName);	
    }

    /**
     * @param append if true, add the plugin at the end, otherwise at the
     *        beginning
     */
    public void loadPlugin(String pluginName,
			   boolean append) {
	if (version_ <= 0)
	    return; 
	pluginHandle_ = loadPlugin(pluginHandle_,
				   pluginName,
				   append);
    }

    /**
     * Extract keywords (meta-data) from the given file.
     *
     * @param f the file to extract meta-data from
     * @return a ArrayList of Extractor.Keywords
     */
    public ArrayList extract(File f) {
	return extract(f.getAbsolutePath());
    }

    /**
     * Extract keywords (meta-data) from the given file.
     *
     * @param file the name of the file
     * @return a ArrayList of Extractor.Keywords
     */
    public ArrayList extract(String filename) {
	if (pluginHandle_ == 0)
	    return new ArrayList(0); // fast way out
	long list
	    = extractInternal(pluginHandle_,
			      filename); // toChars?	
	ArrayList ret = convert(list);
	freeInternal(list);
	return ret;
    }
    
    /**
     * Extract keywords (meta-data) from the given block
     * of data.
     *
     * @param data the file data
     * @return a ArrayList of Extractor.Keywords
     */
    public ArrayList extract(byte[] data) {
	if (pluginHandle_ == 0)
	    return new ArrayList(0); // fast way out
	long list
	    = extractInternal2(pluginHandle_,
			      data);	
	ArrayList ret = convert(list);
	freeInternal(list);
	return ret;
    }

    /**
     * Convert a list of keywords in C to an ArrayList
     * in Java.
     */
    private ArrayList convert(long list) {
	long pos 
	    = list;
	ArrayList res 
	    = new ArrayList();
	while (pos != 0) {
	    int type 
		= typeInternal(pos);
	    String keyword
		= keywordInternal(pos);
	    res.add(new Keyword(type, keyword));
	    pos = nextInternal(pos);
	}
	return res;
    }
    
    
    /* ********************* native calls ******************** */

    private static native long loadDefaultInternal();

    private static native void unloadInternal(long handle);
    
    private static native long extractInternal(long handle,
					       String filename);

    private static native long extractInternal2(long handle,
					       byte[] data);

    // free memory allocated by extractInternal
    private static native void freeInternal(long list);

    private static native int typeInternal(long pos);

    private static native String keywordInternal(long pos);

    private static native long nextInternal(long pos);

    private static native String getTypeAsStringInternal(int type);

    private static native int getVersionInternal();

    private static native int getMaxTypeInternal();

    private static native long unloadPlugin(long handle,
					    String pluginName);

    /**
     * @param append if true, add the plugin at the end, otherwise at the
     *        beginning
     */
    private static native long loadPlugin(long handle,
					  String pluginName,
					  boolean append);

    /**
     * Representation of a keyword.  Each keyword in libextractor
     * has a type (in Java modeled as an int) which describes what
     * the keyword is about.
     * 
     * @author Christian Grothoff
     */
    public static final class Keyword {
	
	//Keyword types
	public static final int EXTRACTOR_UNKNOWN = 0;
	public static final int EXTRACTOR_FILENAME = 1;
	public static final int EXTRACTOR_MIMETYPE = 2;
	public static final int EXTRACTOR_TITLE = 3;
	public static final int EXTRACTOR_AUTHOR = 4;
	public static final int EXTRACTOR_ARTIST = 5;
	public static final int EXTRACTOR_DESCRIPTION = 6;
	public static final int EXTRACTOR_COMMENT = 7;
	public static final int EXTRACTOR_DATE = 8;
	public static final int EXTRACTOR_PUBLISHER = 9;
	public static final int EXTRACTOR_LANGUAGE = 10;
	public static final int EXTRACTOR_ALBUM = 11;
	public static final int EXTRACTOR_GENRE = 12;
	public static final int EXTRACTOR_LOCATION = 13;
	public static final int EXTRACTOR_VERSIONNUMBER = 14;
	public static final int EXTRACTOR_ORGANIZATION = 15;
	public static final int EXTRACTOR_COPYRIGHT = 16;
	public static final int EXTRACTOR_SUBJECT = 17;
	public static final int EXTRACTOR_KEYWORDS = 18;
	public static final int EXTRACTOR_CONTRIBUTOR = 19;
	public static final int EXTRACTOR_RESOURCE_TYPE = 20;
	public static final int EXTRACTOR_FORMAT = 21;
	public static final int EXTRACTOR_RESOURCE_IDENTIFIER = 22;
	public static final int EXTRACTOR_SOURCE = 23;
	public static final int EXTRACTOR_RELATION = 24;
	public static final int EXTRACTOR_COVERAGE = 25;
	public static final int EXTRACTOR_SOFTWARE = 26;
	public static final int EXTRACTOR_DISCLAIMER = 27;
	public static final int EXTRACTOR_WARNING = 28;
	public static final int EXTRACTOR_TRANSLATED = 29;
	public static final int EXTRACTOR_CREATION_DATE = 30;
	public static final int EXTRACTOR_MODIFICATION_DATE = 31;
	public static final int EXTRACTOR_CREATOR = 32;
	public static final int EXTRACTOR_PRODUCER = 33;
	public static final int EXTRACTOR_PAGE_COUNT = 34;
	public static final int EXTRACTOR_PAGE_ORIENTATION = 35;
	public static final int EXTRACTOR_PAPER_SIZE = 36;
	public static final int EXTRACTOR_USED_FONTS = 37;
	public static final int EXTRACTOR_PAGE_ORDER = 38;
	public static final int EXTRACTOR_CREATED_FOR = 39;
	public static final int EXTRACTOR_MAGNIFICATION = 40;
	public static final int EXTRACTOR_RELEASE = 41;
	public static final int EXTRACTOR_GROUP = 42;
	public static final int EXTRACTOR_SIZE = 43;
	public static final int EXTRACTOR_SUMMARY = 44;
	public static final int EXTRACTOR_PACKAGER = 45;
	public static final int EXTRACTOR_VENDOR = 46;
	public static final int EXTRACTOR_LICENSE = 47;
	public static final int EXTRACTOR_DISTRIBUTION = 48;
	public static final int EXTRACTOR_BUILDHOST = 49;
	public static final int EXTRACTOR_OS = 50;
	public static final int EXTRACTOR_DEPENDENCY = 51;
	public static final int EXTRACTOR_HASH_MD4 = 52;
	public static final int EXTRACTOR_HASH_MD5 = 53;
	public static final int EXTRACTOR_HASH_SHA0 = 54;
	public static final int EXTRACTOR_HASH_SHA1 = 55;
	public static final int EXTRACTOR_HASH_RMD160 = 56;
	public static final int EXTRACTOR_RESOLUTION = 57;
	public static final int EXTRACTOR_CATEGORY = 58;
	public static final int EXTRACTOR_BOOKTITLE = 59;
	public static final int EXTRACTOR_PRIORITY = 60;
	public static final int EXTRACTOR_CONFLICTS = 61;
	public static final int EXTRACTOR_REPLACES = 62;
	public static final int EXTRACTOR_PROVIDES = 63;
	public static final int EXTRACTOR_CONDUCTOR = 64;
	public static final int EXTRACTOR_INTERPRET = 65;
	public static final int EXTRACTOR_OWNER = 66;
	public static final int EXTRACTOR_LYRICS = 67;
	public static final int EXTRACTOR_MEDIA_TYPE = 68;
	public static final int EXTRACTOR_CONTACT = 69;
	public static final int EXTRACTOR_THUMBNAIL_DATA = 70;
	public static final int EXTRACTOR_PUBLICATION_DATE = 71;
	public static final int EXTRACTOR_CAMERA_MAKE = 72;
	public static final int EXTRACTOR_CAMERA_MODEL = 73;
	public static final int EXTRACTOR_EXPOSURE = 74;
	public static final int EXTRACTOR_APERTURE = 75;
	public static final int EXTRACTOR_EXPOSURE_BIAS = 76;
	public static final int EXTRACTOR_FLASH = 77;
	public static final int EXTRACTOR_FLASH_BIAS = 78;
	public static final int EXTRACTOR_FOCAL_LENGTH = 79;
	public static final int EXTRACTOR_FOCAL_LENGTH_35MM = 80;
	public static final int EXTRACTOR_ISO_SPEED = 81;
	public static final int EXTRACTOR_EXPOSURE_MODE = 82;
	public static final int EXTRACTOR_METERING_MODE = 83;
	public static final int EXTRACTOR_MACRO_MODE = 84;
	public static final int EXTRACTOR_IMAGE_QUALITY = 85;
	public static final int EXTRACTOR_WHITE_BALANCE = 86;
	public static final int EXTRACTOR_ORIENTATION = 87;
	public static final int EXTRACTOR_TEMPLATE = 88;
	public static final int EXTRACTOR_SPLIT = 89;
	public static final int EXTRACTOR_PRODUCTVERSION = 90;  
	public static final int EXTRACTOR_LAST_SAVED_BY = 91;
	public static final int EXTRACTOR_LAST_PRINTED = 92;  
	public static final int EXTRACTOR_WORD_COUNT = 93;
	public static final int EXTRACTOR_CHARACTER_COUNT = 94;
	public static final int EXTRACTOR_TOTAL_EDITING_TIME = 95;
	public static final int EXTRACTOR_THUMBNAILS = 96;
	public static final int EXTRACTOR_SECURITY = 97;
	public static final int EXTRACTOR_CREATED_BY_SOFTWARE = 98;
	public static final int EXTRACTOR_MODIFIED_BY_SOFTWARE = 99;
	public static final int EXTRACTOR_REVISION_HISTORY = 100;
	public static final int EXTRACTOR_LOWERCASE = 101;
	public static final int EXTRACTOR_COMPANY = 102;
	public static final int EXTRACTOR_GENERATOR = 103;
	public static final int EXTRACTOR_CHARACTER_SET = 104;
	public static final int EXTRACTOR_LINE_COUNT = 105;
	public static final int EXTRACTOR_PARAGRAPH_COUNT = 106;
	public static final int EXTRACTOR_EDITING_CYCLES = 107;
	public static final int EXTRACTOR_SCALE = 108;
	public static final int EXTRACTOR_MANAGER = 109;
	public static final int EXTRACTOR_MOVIE_DIRECTOR = 110;
	public static final int EXTRACTOR_DURATION = 111;
	public static final int EXTRACTOR_INFORMATION = 112;
	public static final int EXTRACTOR_FULL_NAME = 113;
	public static final int EXTRACTOR_CHAPTER = 114;

	private final int type_;

	private final String keyword_;

	Keyword(int type,
		String key) {
	    this.type_ = type;
	    this.keyword_ = key;
	}

	public String toString() {
	    return getTypeAsString(type_) + ": " + keyword_;
	}

	public int getType() {
	    return type_;
	}

	public String getKeyword() {
	    return keyword_;
	}

    } // end of Extractor.Keyword

} // end of Extractor
