/**
 * Class copyright 2003 by the Ravensfield Digital Resource Group, Ltd, Granville, OH.
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without a written agreement
 * is hereby granted, provided that the above copyright notice and this
 * paragraph and the following two paragraphs appear in all copies.
 *
 * IN NO EVENT SHALL THE RAVENSFIELD DIGITAL RESOURCE GROUP, LTD BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
 * DOCUMENTATION, EVEN IF THE RAVENSFIELD DIGITAL RESOURCE GROUP, LTD HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE RAVENSFIELD DIGITAL RESOURCE GROUP, LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE RAVENSFIELD DIGITAL RESOURCE GROUP, LTD HAS NO OBLIGATIONS TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 * 
 * (Quick readers will recognize that as the stock BSD license)
 */
package org.postgresql.ers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

import java.sql.Connection;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Properties;

//the jakarta command line processing tools
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;


/**
 *  Class to send SQL statements to the primary and all slaves in the system.
 *
 * @author    Andrew Rawnsley
 */
public class ERSExecSQL extends ERS {

	/**
	 * Constructor for the ERSExecSQL object
	 *
	 *USAGE: java org.postgresql.ers.ERSExecSQL {options} <statement 1> <statement 2> ... <statement n>
	 *
	 * @param  primary      Connection to the primary
	 * @param  replicants   Connections to the replicants
	 * @param  commands     Array of SQL statements
	 * @param  slavesFirst  Flag to indicate slave first or master first execution
	 */
	public ERSExecSQL(Connection primary, Connection[] replicants, String[] commands, boolean slavesFirst, String host, int port) {

		Statement stmnt;
		int i;
		if (slavesFirst) {
			for (i = 0; i < replicants.length; i++) {
				execSQL(commands, replicants[i]);
			}
		}

		execSQL(commands, primary);

		if (!slavesFirst) {
			for (i = 0; i < replicants.length; i++) {
				execSQL(commands, replicants[i]);
			}

		}

		// signal backend
		if (host != null) {
			try {
				new ERSCtl(2, host, port);
				System.out.println("Backend successfully signaled.");
			} catch (Exception ex) {
				System.out.println("Backend signal failed.");
				ex.printStackTrace();
			}
		}

	}

	/**
	 *  Add tables to the replicator
	 *
	 * @param  args  The command line arguments
	 */
	public static void main(String[] args) {

		int i;
		String fileName;
		String[] fileArgs;
		Properties properties = null;
		String[] replicantURL = null;
		String[] replicantUser = null;
		String[] replicantPass = null;

		Options options = new Options();

		options.addOption("d", "ers_home", true, "Install directory for eRServer (default /opt/erserver)");
		options.addOption("f", "file", true, "File containing SQL commands");
		options.addOption("q", "quiet", false, "Run quiet");
		options.addOption("m", "master-first", false, "Execute on master first");
		options.addOption("h", "host", true, "Host name of running eRServer (default localhost)");
		options.addOption("p", "port", true, "Port # for signal (default 1099)");
		options.addOption("ns", "nosignal", false, "Don't signal backend");
		options.addOption("s", "slave-first", false, "Execute on slaves first (default)");

		//parse the commandline arguments
		GnuParser parser = new GnuParser();
		CommandLine line = null;
		try {
			line = parser.parse(options, args);
		} catch (org.apache.commons.cli.ParseException exp) {
			System.out.println("Parsing failed. Reason: " + exp.getMessage());
			return;
		}

		if (args.length == 0) {
			HelpFormatter formatter = new HelpFormatter();
			formatter.printHelp("USAGE: java org.postgresql.ers.ERSAddTable <options> <table1> ... <table n>", options);
			return;
		}

		String ersHome = line.getOptionValue("d", "/opt/erserver/");
		quiet = line.hasOption("q");

		if (!ersHome.endsWith(System.getProperty("file.separator"))) {
			ersHome += System.getProperty("file.separator");
		}

		if (!quiet) {
			System.out.println("Using " + ersHome + " as ERS installation directory");
		}

		String s;
		String host = null;
		int port = 1099;

		if (!line.hasOption("ns")) {
			host = line.getOptionValue("h","localhost");
			if ((s = line.getOptionValue("p")) != null) {
				try {
                    port = Integer.parseInt(s);
				} catch (Exception ex) {
					ex.printStackTrace();
					port = 1099;
				}
			}
		}

		// Read the replication.cfg file
		try {
			File file = new File(ersHome + "etc/replication.cfg");
			if (!file.exists()) {
				System.out.println("Cannot find replication.cfg in " + ersHome);
				System.exit(1);
			}

			properties = new Properties();
			FileInputStream in = new FileInputStream(file);
			properties.load(in);
			in.close();
		} catch (IOException ix) {
			ix.printStackTrace();
			System.exit(1);
		}

		// Get connection properties for the primary
		String primaryURL = properties.getProperty("replic.master.JDBCConnectionURL");
		String primaryUser = properties.getProperty("replic.master.user");
		String primaryPass = properties.getProperty("replic.master.password");

		if (primaryURL == null || primaryUser == null || primaryPass == null) {
			System.out.println("Invalid primary URL/user/password");
			System.exit(1);
		}

		// Get connection properties for the replicants
		s = properties.getProperty("replic.slave.JDBCConnectionURL");

		if (s != null) {
			replicantURL = split(s, ",");
		}

		s = properties.getProperty("replic.slave.user");

		if (s != null) {
			replicantUser = split(s, ",");
		}

		s = properties.getProperty("replic.slave.password");

		if (s != null) {
			replicantPass = split(s, ",");
		}

		if (replicantURL == null || replicantUser == null || replicantPass == null) {
			System.out.println("Bad replicant configuration");
			System.exit(1);
		}

		if (replicantURL.length != replicantUser.length || replicantURL.length != replicantPass.length) {
			System.out.println("Bad replicant configuration");
			System.exit(1);
		}

		// Create the primary and replicant DB connections
		Connection primary = getConnection(primaryURL, primaryUser, primaryPass);
		Connection[] replicants = new Connection[replicantURL.length];

		for (i = 0; i < replicantURL.length; i++) {
			replicants[i] = getConnection(replicantURL[i], replicantUser[i], replicantPass[i]);
		}

		boolean slavesFirst = true;

		if (line.hasOption("m")) {
			slavesFirst = false;
		}

		if ((fileName = line.getOptionValue("f")) != null) {
			ArrayList commands = new ArrayList();
			StringBuffer sb = new StringBuffer();
			try {
				File f = new File(fileName);
				if (f.exists()) {
					LineNumberReader in = new LineNumberReader(new FileReader(f));
					while ((s = in.readLine()) != null) {
						s = s.trim();
						if (s.length() == 0) {
							continue;
						}
						if (s.startsWith("--")) {
							continue;
						}
						sb.append(' ').append(s);
						if (s.endsWith(";")) {
							commands.add(sb.toString());
							sb = new StringBuffer();
						}
					}

					if (sb.length() > 0) {
						commands.add(sb.toString());
					}

					fileArgs = (String[]) commands.toArray(new String[0]);
					new ERSExecSQL(primary, replicants, fileArgs, slavesFirst, host, port);
				}
			} catch (IOException iox) {
				iox.printStackTrace();

			}
		} else {
			new ERSExecSQL(primary, replicants, line.getArgs(), slavesFirst, host, port);
		}

		if (!quiet) {
			System.out.println("\nDone\n");
		}
	}

	/**
	 *  Description of the Method
	 *
	 * @param  commands          Parameter
	 * @param  c                 Parameter
	 */
	private void execSQL(String[] commands, Connection c) {
		Statement stmnt = null;
		try {
			c.setAutoCommit(true);
			stmnt = c.createStatement();

			for (int i = 0; i < commands.length; i++) {
				try {
					stmnt.execute(commands[i]);
				} catch (SQLException sx) {
					sx.printStackTrace();
				}
			}
		} catch (SQLException ex) {
			ex.printStackTrace();
		} finally {
			try {
				stmnt.close();
			} catch (Exception ex) {}
		}

	}

}

