/*  Copyright (C) 2003-2011 JabRef contributors.
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General public static 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 static License for more details.

    You should have received a copy of the GNU General public static License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package net.sf.jabref.sql.exporter;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import net.sf.jabref.Util;
import net.sf.jabref.sql.DBStrings;
import net.sf.jabref.sql.SQLUtil;

/**
 * 
 * @author ifsteinm.
 * 
 *  Jan 20th	Extends DBExporter to provide features specific for PostgreSQL
 *  			Created after a refactory on SQLUtil
 *          
 */
public class PostgreSQLExporter extends DBExporter {

	private static PostgreSQLExporter instance = null;

	private PostgreSQLExporter() {
	}

	/**
	 * 
	 * @return The singleton instance of the PostgreSQLExporter
	 */
	public static PostgreSQLExporter getInstance() {
		if (instance == null)
			instance = new PostgreSQLExporter();
		return instance;
	}

	@Override
	public Connection connectToDB(DBStrings dbstrings) throws Exception {
		this.dbStrings = dbstrings;
		String url = SQLUtil.createJDBCurl(dbstrings, true);
		String drv = "org.postgresql.Driver";

		Class.forName(drv).newInstance();
		Connection conn = DriverManager.getConnection(url,
				dbstrings.getUsername(), dbstrings.getPassword());
		ResultSet rs = ((Statement) SQLUtil.processQueryWithResults(conn,
				"SELECT count(*) AS alreadyThere FROM pg_database WHERE datname='"
						+ dbStrings.getDatabase() + "'")).getResultSet();
		rs.next();
		if (rs.getInt("alreadyThere") == 0) {
			SQLUtil.processQuery(conn, "CREATE DATABASE " + dbStrings.getDatabase());
		}
		rs.getStatement().close();
		conn.close();

		conn = DriverManager.getConnection(url, dbstrings.getUsername(),
				dbstrings.getPassword());
		createPLPGSQLFunction(conn);

		return conn;
	}


	private void createPLPGSQLFunction(Connection conn) throws SQLException {
		SQLUtil.processQuery(
				conn,
				"create or replace function create_table_if_not_exists (create_sql text) returns bool as $$"
						+ "BEGIN"
						+ "\tBEGIN"
						+ "\t\tEXECUTE create_sql;"
						+ "\t\tException when duplicate_table THEN"
						+ "\t\tRETURN false;"
						+ "\tEND;"
						+ "\tRETURN true;"
						+ "END;" + "$$" + "Language plpgsql;");
	}


	/**
	 * Generates SQL necessary to create all tables in a MySQL database, and
	 * writes it to appropriate output.
	 * 
	 * @param out
	 *            The output (PrintStream or Connection) object to which the DML
	 *            should be written.
	 */
	@Override
	protected void createTables(Object out) throws SQLException {

		SQLUtil.processQuery(out,
				"SELECT create_table_if_not_exists ('CREATE TABLE jabref_database ( \n"
						+ "database_id SERIAL NOT NULL, \n"
						+ "database_name VARCHAR(64) NOT NULL, \n"
						+ "md5_path VARCHAR(32) NOT NULL, \n"
						+ "PRIMARY KEY (database_id)\n );')");
		SQLUtil.processQuery(
				out,
				"SELECT create_table_if_not_exists ('CREATE TABLE entry_types ( \n"
						+ "entry_types_id    SERIAL, \n"
						+ "label TEXT, \n"
						+ SQLUtil.fieldsAsCols(SQLUtil.getAllFields(),
								" VARCHAR(3) DEFAULT NULL") + ", \n"
						+ "PRIMARY KEY (entry_types_id) \n" + ");')");
		SQLUtil.processQuery(
				out,
				"SELECT create_table_if_not_exists ('CREATE TABLE entries ( \n"
						+ "entries_id      SERIAL, \n"
						+ "jabref_eid      VARCHAR("
						+ Util.getMinimumIntegerDigits()
						+ ")   DEFAULT NULL, \n"
						+ "database_id INTEGER, \n"
						+ "entry_types_id  INTEGER DEFAULT NULL, \n"
						+ "cite_key        VARCHAR(100)     DEFAULT NULL, \n"
						+ SQLUtil.fieldsAsCols(SQLUtil.getAllFields(),
								" TEXT DEFAULT NULL")
						+ ",\n"
						+ "PRIMARY KEY (entries_id), \n"
						+ "FOREIGN KEY (entry_types_id) REFERENCES entry_types (entry_types_id), \n"
						+ "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id) \n"
						+ ");')");
		SQLUtil.processQuery(out,
				"SELECT create_table_if_not_exists ('CREATE TABLE strings ( \n"
						+ "strings_id      SERIAL, \n"
						+ "label      VARCHAR(100)  DEFAULT NULL, \n"
						+ "content    VARCHAR(200)  DEFAULT NULL, \n"
						+ "database_id INTEGER, \n"
						+ "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id), \n"
						+ "PRIMARY KEY (strings_id) \n" + ");')");
		SQLUtil.processQuery(out,
				"SELECT create_table_if_not_exists ('CREATE TABLE group_types ( \n"
						+ "group_types_id  SERIAL, \n"
						+ "label   VARCHAR(100)    DEFAULT NULL, \n"
						+ "PRIMARY KEY (group_types_id) \n" + ");')");
		SQLUtil.processQuery(
				out,
				"SELECT create_table_if_not_exists ('CREATE TABLE groups ( \n"
						+ "groups_id       SERIAL, \n"
						+ "group_types_id  INTEGER         DEFAULT NULL, \n"
						+ "label           VARCHAR(100)    DEFAULT NULL, \n"
						+ "database_id INTEGER, \n"
						+ "parent_id       INTEGER         DEFAULT NULL, \n"
						+ "search_field       VARCHAR(100)          DEFAULT NULL, \n"
						+ "search_expression  VARCHAR(200)          DEFAULT NULL, \n"
						+ "case_sensitive  BOOLEAN       DEFAULT NULL, \n"
						+ "reg_exp BOOLEAN DEFAULT NULL, \n"
						+ "hierarchical_context INTEGER DEFAULT NULL, \n"
						+ "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id), \n"
						+ "PRIMARY KEY (groups_id) \n" + ");')");
		SQLUtil.processQuery(
				out,
				"SELECT create_table_if_not_exists ('CREATE TABLE entry_group ( \n"
						+ "entries_id       SERIAL, \n"
						+ "groups_id        INTEGER        DEFAULT NULL, \n"
						+ "FOREIGN KEY (entries_id) REFERENCES entries (entries_id) ON DELETE CASCADE, \n"
						+ "FOREIGN KEY (groups_id)  REFERENCES groups (groups_id), \n"
						+ "PRIMARY KEY (groups_id, entries_id) \n" + ");')");
	}
}
