/*
 * This file is part of the DistSim distributed simulation framework (client)
 * Copyright (C) 2007 Ulf Hermann
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package brn.distsim.client.data;

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

import brn.distsim.client.data.Simulation.Parameter;


/**
 * A study represents some simulations executed with the same codebase
 * represented by the set of packages to be loaded. The simulations are not
 * directly dependent obejcts of the Study, because the parameter ranges from
 * which they are created must be remembered for later reuse. This is why we
 * keep groups as dependent data here.
 *
 * @author alve
 *
 */
public class Study extends DbBackedData<Group> implements Saveable {

	private String name;

	private String command;

	private String version;

	private StudyPackages packages;

	/**
	 * create a study with the given connection and id for later loading from
	 * the database, but don't load it now.
	 *
	 * @param db
	 *            the connection to be used
	 * @param id
	 *            the id of the study
	 */
	public Study(Connection db, int id) {
		super(db);
		this.id = id;
		packages = new StudyPackages(db, id);
	}

	/**
	 * create a study with name, version and command, that can be saved to the
	 * database as it is, but don't save it now.
	 *
	 * @param db
	 *            the database connection to be used
	 * @param name
	 *            name of the study
	 * @param version
	 *            version of the study
	 * @param command
	 *            command to be executed when simulating
	 */
	public Study(Connection db, String name, String version, String command) {
		super(db);
		this.id = 0;
		this.name = name;
		this.version = version;
		this.command = command;
		packages = new StudyPackages(db, 0);
	}

	/**
	 * Recursively clone a study assigning a new version and a new command.
	 * Don't take over the other study's ids
	 *
	 * @param other
	 *            the study to be cloned
	 * @param newVersion
	 *            the new version to be assigned
	 * @param newCommand
	 *            the new command to be assigned
	 */
	public Study(Study other, String newVersion, String newCommand) {
		super(other.definitions);
		this.name = other.name;
		this.version = newVersion;
		this.command = newCommand;
		packages = new StudyPackages(other.packages);
	}

	/**
	 * create a study from a database record. Load the packages from the
	 * database, using the id given in data.
	 *
	 * @param db
	 *            the database to be used
	 * @param data
	 *            the record to be read from
	 * @throws SQLException
	 *             if reading from data or loading the packages fails
	 */
	public Study(Connection db, ResultSet data) throws SQLException {
		super(db);
		id = data.getInt("id");
		name = data.getString("name");
		version = data.getString("version");
		command = data.getString("command");
		packages = new StudyPackages(db, id);
		packages.loadFromDb();
	}

	/**
	 * update this study, its groups and its packages in the database
	 */
	public void performDbUpdate() throws SQLException {
		commit();
		statement.execute("UPDATE studies SET command='" + command
				+ "' WHERE id='" + id + "';");
		packages.commit();
	}

	/**
	 * delete this this study, all its groups, and all its packages from the
	 * database.
	 */
	public void performDbDelete() throws SQLException {
		deleteAll();
		commit();
		packages.deleteAll();
		packages.commit();
		statement.execute("DELETE FROM studies WHERE id='" + id + "';");
	}

	public void performDbInsert(int nothing) throws SQLException {
		performDbInsert();
	}

	/**
	 * insert this study, all its packages and all its groups into the database.
	 */
	public void performDbInsert() throws SQLException {
		statement.executeUpdate("INSERT INTO studies SET name='" + name
				+ "', version='" + version + "', command='" + command + "';",
				Statement.RETURN_GENERATED_KEYS);
		ResultSet key = statement.getGeneratedKeys();
		key.next();
		id = key.getInt(1);
		packages.id = id;
		commit();
		packages.commit();
	}

	/**
	 * load this study, its packages and its groups from the database using the
	 * id. Don't load simulations for the groups.
	 */
	public void loadFromDb() throws SQLException {
		ResultSet groups = statement
				.executeQuery("SELECT * FROM groups WHERE studyId = " + id
						+ ";");
		super.loadFromDb(groups);
		packages.loadFromDb();
	}

	/**
	 * return this study's name or null if unknown
	 */
	public String getName() {
		return name;
	}

	/**
	 *
	 * @return this study's version of null if unknown
	 */
	public String getVersion() {
		return version;
	}

	/**
	 *
	 * @return the command to be executed when simulating or null if unknown
	 */
	public String getCommand() {
		return command;
	}

	/**
	 * create an empty group
	 */
	@Override
	public Group getEmptyDependent() {
		return new Group(definitions, null, 0);
	}

	/**
	 * load a group from a database record
	 */
	@Override
	public Group getDependent(ResultSet data) throws SQLException {
		return new Group(definitions, data);
	}

	/**
	 * clone a group
	 *
	 * @param other
	 * @return group
	 */
	@Override
	public Group getDependent(Group other) {
		return new Group(other);
	}

	/**
	 * return the packages subobject.
	 *
	 * @return the packages subobject.
	 */
	public StudyPackages getPackages() {
		return packages;
	}


	/**
	 * this method can be used to make a deep copy of an existing study so that the same
	 * set of simulations can be executed again, possibly with different code.
	 * @param newVersion the new version to be assigned to the copy
	 * @return a deep copy of this study
	 */
	public Study deepCopy(String newVersion) {
		Study other = new Study(this, newVersion, command);
		for (Group g : dependentData) {
			Group newGroup = new Group(g, id);
			other.add(newGroup);
			for (Simulation sim : g) {
				Simulation newSim = new Simulation(sim, newGroup.getId());
				newGroup.add(newSim);
				for (Parameter p : sim) {
					newSim.add(newSim.new Parameter(p));
				}
			}
		}
		return other;
	}


}
