/*
 *    TDBWrapper.java
 *    Copyright (C) 2015 New Zealand Digital Library, http://www.nzdl.org
 *
 *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.greenstone.gsdl3.util;

import java.net.URLEncoder;
import java.util.Enumeration;

import org.apache.log4j.Logger;
import org.greenstone.gsdl3.util.DBHelper;
import org.greenstone.gsdl3.util.FlatDatabaseWrapper;

import org.greenstone.tdbjava.TDBJava;

/**
 * Java wrapper class for TDB - uses TDBJava (a JNI wrapper to TDB)
 */
public class TDBWrapper
    implements FlatDatabaseWrapper
{
    // A logger for messaging (mostly error)
    static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.TDBWrapper.class.getName());

    static
    {
	/** Register this Wrapper with the DBHelper */
	DBHelper.registerDBTypeExt("tdb",".tdb");
	logger.info("Registered TDB database type with file extension .tdb");
    }

    // The open database (if any)
    protected TDBJava db_ = null;


    /** @function openDatabase(String, int)
     *  Open the database filename, with mode mode
     */
    public boolean openDatabase(String filename, int mode)
    {
	// convert mode to TDBJava open mode
	if (mode == this.READ) {
	    mode = TDBJava.O_READONLY;
	}
	else if (mode == this.WRITE) {
	    mode = TDBJava.O_DEFAULT;
	}
	else {
	    logger.error("invalid mode, " + mode + ", opening db for reading only");
	    mode = TDBJava.O_READONLY;
	}

	try {
	    if (this.db_ != null) {
		this.db_.close();
	    }
	    this.db_ = TDBJava.getConnection(filename);
	}
	catch (Exception e) {
	    logger.error("couldn't open database: " + filename);
	    logger.error("  Exception: " + e.toString());
	    return false;
	}
	logger.info("opened with tdb_flags=" + TDBJava.TDB_DEFAULT + " and mode=" + mode);
	return true;
    }
    /** openDatabase(String, int) **/


    /** @function closeDatabase
     *  Close the database associated with this wrapper
     */
    public void closeDatabase()
    {
	if (this.db_ != null) {
	    try {
		this.db_.close();
		this.db_ = null;
		// Hack: force Windows to let go of the gdb file
		if (System.getProperty("os.name").startsWith("Windows")) {
		    System.gc();
		}
	    }
	    catch (Exception e) {
		logger.error("error on close()");
		logger.error("  Exception: " + e.toString());
	    }
	}
    }
    /** closeDatabase() **/

    /** @function getValue(String)
     */
    public String getValue(String key)
    {
	String value = null;
	if (null != this.db_) {
	    try {
		value = this.db_.fetch(key);
	    }
	    catch (Exception e) {
		logger.error("error on getValue(" + key + ")");
		logger.error("  Exception: " + e.toString());
	    }
	}
	if (null == value) {
	    logger.error("key " + key + " not present in db");
	}
	return value;
    }
    /** getValue(String) **/

    /** @function setValue(String, String)
     *  Sets the given key to the given value in the database
     */
    public boolean setValue(String key, String value)
    {
	boolean result = false;
	if (this.db_ == null) {
	    logger.error("database is not open");
	}
	else if (!this.db_.isWritable()) {
	    logger.error("database is read-only");
	}
	else {
	    try {
		this.db_.store(key, value);
		result = true;
	    }
	    catch (Exception e) {
		logger.error("error storing " + key + " = " + value);
		logger.error("  Exception: " + e.toString());
	    }
	}
	return result;
    }
    /** setValue(String, String) **/

    /** @function deleteKey(String key)
     *  Deletes the entry for given key
     */
    public boolean deleteKey(String key)
    {
	boolean result = false;
	if (null == this.db_) {
	    logger.error("database is not open");
	}
	else if (!this.db_.isWritable()) {
	    logger.error("database is read-only");
	}
	else {
	    try {
		this.db_.delete(key);
		result = true;
	    }
	    catch (Exception e) {
		logger.error("error deleting key " + key);
		logger.error("  Exception: " + e.toString());
	    }
	}
	return result;
    }
    /** deleteKey(String) **/

    /** @function displayAllEntries()
     *  Returns a string of key-value entries that can be printed for debugging
     * purposes.
     */
    public String displayAllEntries()
    {
	StringBuffer output = new StringBuffer();
	if (null == this.db_) {
	    logger.error("database is not open");
	}
	else {
	    try {
		Enumeration keys = this.db_.keys();
		while (keys.hasMoreElements()) {
		    String key = (String) keys.nextElement();
		    String value = this.db_.fetch(key);
		    String urlkey = URLEncoder.encode((String) key, "UTF8");
		    output.append("key href: ");
		    output.append(key);
		    output.append("\tvalue ID: ");
		    output.append(value);
		    output.append("\n");
		    output.append("URL encoded key: " + urlkey);
		}
	    }
	    catch (Exception e) {
		logger.error("error when trying to display all entries");
		logger.error("  Exception: " + e.toString());
	    }
	}
	return output.toString();
    }
    /** displayAllEntries() **/
    
}
/** Class: TDBWrapper **/
