/*
 * File:     Preferences.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package mpi.eudico.client.annotator;

import mpi.eudico.server.corpora.clom.Transcription;

import mpi.eudico.server.corpora.clomimpl.abstr.TranscriptionImpl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


// test 2 open instanties van dezelfde .eaf, die delen 1 .prf file
// check of exceptions elegant kunnen worden opgevangen

/**
 * Administrates the global preferences for Elan and the preferences for each
 * document A document is in this implementation the same as a Transcription,
 * maybe this can be made more generic. The methods that return a document key
 * and a preference file path for a document must then be adapted.
 */
public class Preferences {
    /** Holds value of property DOCUMENT ME! */
    private final static String GLOBAL_PREFS_KEY = "elan global prefs key";

    /** Holds value of property DOCUMENT ME! */
    private final static String GLOBAL_PREFS_FILE_NAME = "elan.pfs";

    // hashtable of hashtables, each document has its own hashtable with key value pairs for preferences
    private static Hashtable preferences;

    /**
     * Get the preference value for a certain preference key If the document is
     * not null a document specific value is returned if it exists otherwise
     * the global preference value is returned
     *
     * @param key the preference key value
     * @param document the document for which preferences are asked
     *
     * @return the preference value
     */
    public static Object get(String key, Transcription document) {
        // prevent null pointer exception in HashTables
        if (key == null) {
            return null;
        }

        // make sure the preferences data structure is initialized
        initPreferencesFor(document);

        // first look for a document specific preference setting
        Object result = ((Hashtable) preferences.get(documentKeyFor(document))).get(key);

        // ready if the preference exists 
        if (result != null) {
            return result;
        }

        // no document specific preference value found, look for a global value
        return ((Hashtable) preferences.get(GLOBAL_PREFS_KEY)).get(key);
    }

    /**
     * Set the preference value for a certain document.  If the document ==
     * null a global preference is set
     *
     * @param key preference key
     * @param value preference value
     * @param document identifier for document specific preferences
     */
    public static void set(String key, Object value, Transcription document) {
        // prevent null pointer exception in HashTables
        if (key == null) {
            return;
        }

        // make sure the preference data structure is initialized
        initPreferencesFor(document);

        // if two ElanFrames are opened for the same .eaf file a cvs like
        // update must be done here. 
        // Disabled because it is not obvious better than doing nothing special
        //preferences.put(documentKeyFor(document), readPreferencesFor(document));
        // put the preference value in the hash table for the document
        ((Hashtable) preferences.get(documentKeyFor(document))).put(key, value);

        // make the current preferences for this document persistent
        writePreferencesFor(document);
    }

    /**
     * Specialized version for the Object value version of setPreference
     *
     * @param key preference key
     * @param value preference value as an int
     * @param document identifier for document specific preferences
     */
    public static void set(String key, int value, Transcription document) {
        set(key, new Integer(value), document);
    }

    /**
     * Specialized version for the Object value version of setPreference
     *
     * @param key preference key
     * @param value preference value as a long
     * @param document identifier for document specific preferences
     */
    public static void set(String key, long value, Transcription document) {
        set(key, new Long(value), document);
    }

    /**
     * Specialized version for the Object value version of setPreference
     *
     * @param key preference key
     * @param value preference value as a float
     * @param document identifier for document specific preferences
     */
    public static void set(String key, float value, Transcription document) {
        set(key, new Float(value), document);
    }

    /**
     * Specialized version for the Object value version of setPreference
     *
     * @param key preference key
     * @param value preference value as a double
     * @param document identifier for document specific preferences
     */
    public static void set(String key, double value, Transcription document) {
        set(key, new Double(value), document);
    }

    /**
     * Removes the stored preferences Hashtable for the specified document
     * from the global Hashtable.
     *
     * @param document the transcription, used as a key in the hashtable
     */
    public static void removeDocument(Transcription document) {
        if (preferences != null) {
            preferences.remove(document);
        }
    }

    /**
     * Takes care of initializing the data structures for the preferences of a
     * certain document
     *
     * @param document the document for which preferences are to be
     *        initialized, null means global preferences
     */
    private static void initPreferencesFor(Transcription document) {
        // make sure the master hash table exists 
        if (preferences == null) {
            preferences = new Hashtable();
        }

        // make sure the preferences for the document are initialized
        if (!preferences.containsKey(documentKeyFor(document))) {
            // read the Hashtable from the preference file if it exists, otherwise create a new hash table
            Hashtable documentPreferences = readPreferencesFor(document);

            // place the document specific preferences table in the master table
            preferences.put(documentKeyFor(document), documentPreferences);
        }
    }

    /**
     * Create a Hashtable with preference key/value pairs from the persistent
     * format. If there are no persistent preferences for the document an
     * empty Hashtable is returned
     *
     * @param document the document for which the preferences are asked
     *
     * @return a Hashtable with the persistent preferences for the document
     */
    private static Hashtable readPreferencesFor(Transcription document) {
        Hashtable preferencesHashtable = new Hashtable();

        try {
            if (new File(preferenceFilePathFor(document)).exists()) {
                FileInputStream fileIn = new FileInputStream(preferenceFilePathFor(
                            document));
                ObjectInputStream objectIn = new ObjectInputStream(fileIn);
                preferencesHashtable = (Hashtable) objectIn.readObject();

                // printPrefs(preferencesHashtable);
                objectIn.close();
                fileIn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return preferencesHashtable;
    }

    /**
     * Make the preferences for a certain document persistent
     *
     * @param document the dociment for which the preferences are to be saved
     */
    private static void writePreferencesFor(Transcription document) {
        ObjectOutputStream objectOut = null;
        FileOutputStream fileOut = null;

        try {
            // do not save prefs for a new file that has no new name yet
            if ((document != null) &&
                    document.getName().equals(TranscriptionImpl.UNDEFINED_FILE_NAME)) {
                return;
            }

            fileOut = new FileOutputStream(preferenceFilePathFor(document));

            objectOut = new ObjectOutputStream(fileOut);
            objectOut.writeObject(preferences.get(documentKeyFor(document)));
            objectOut.close();
            fileOut.close();
        } catch (Exception e) {
            e.printStackTrace();

            try {
                if (objectOut != null) {
                    objectOut.close();
                }

                if (fileOut != null) {
                    fileOut.close();
                }
            } catch (Exception e2) {
            }
        }
    }

    /**
     * A valid Hashtable key is generated for a Transcription document If the
     * transcription == null the global preferences key is returned
     *
     * @param document a Transcription or null for the global preferences
     *
     * @return a unique key for the hashtable that holds the preferences for
     *         this document
     */
    private static Object documentKeyFor(Transcription document) {
        if (document == null) {
            return GLOBAL_PREFS_KEY;
        } else {
            return document;
        }
    }

    /**
     * Gets a preference file name for a document If the key == null the global
     * preference file is used Otherwise the .eaf file name is used with
     * extension .pfs instead of .eaf
     *
     * @param document a Transcription or null for the global preferences
     *
     * @return a full path to the preferences file for the document
     *
     * @throws Exception DOCUMENT ME!
     */
    private static String preferenceFilePathFor(Transcription document)
        throws Exception {
        if (document == null) {
            return Constants.ELAN_DATA_DIR +
            System.getProperty("file.separator") + GLOBAL_PREFS_FILE_NAME;
        } else {
            String prefFileName = "";

            if (document instanceof TranscriptionImpl) {
                prefFileName = ((TranscriptionImpl) document).getPathName(); // do not use getFullPath Name from Transcription
            }

            if (prefFileName.length() > 3) {
                prefFileName = prefFileName.substring(0,
                        prefFileName.length() - 3) + "pfs";
            }

            return prefFileName;
        }
    }

    /**
     * Print the preferences that are currently stored
     *
     * @param prefs a collection of preferences
     */
    private static void printPrefs(Object prefs) {
        if ((prefs == null) || !(prefs instanceof Hashtable)) {
            return;
        }

        Hashtable prf = (Hashtable) prefs;
        Iterator it = prf.keySet().iterator();

        while (it.hasNext()) {
            Object ke = it.next();
            Object val = prf.get(ke);

            if (ke == null) {
                System.out.println("Entry: key is null...");

                continue;
            }

            System.out.println("K: " + ke + " (" + ke.getClass() + ")");

            if (val == null) {
                System.out.println("Entry: value is null...");

                continue;
            }

            System.out.println("V: " + val + " (" + val.getClass() + ")");

            if (val instanceof Object[]) {
                val = Arrays.asList((Object[]) val);
            }

            if (val instanceof List) {
                List li = (List) val;

                for (int i = 0; i < li.size(); i++) {
                    Object vv = li.get(i);

                    if (vv != null) {
                        System.out.println("\tentry: " + vv.toString());
                    }
                }
            } else if (val instanceof Map) {
                Map mm = (Map) val;
                Iterator mit = mm.keySet().iterator();

                while (mit.hasNext()) {
                    Object kk = mit.next();
                    Object vv = mm.get(kk);

                    if (kk != null) {
                        System.out.println("\tK: " + kk + " (" + kk.getClass() +
                            ")");

                        if (vv != null) {
                            System.out.println("\tV: " + vv + " (" +
                                vv.getClass() + ")");
                        } else {
                            System.out.println("\tvalue is null");
                        }
                    } else {
                        System.out.println("\tkey is null");
                    }
                }
            }
        }
    }
}
