/*
 * 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;

/**
 * a group represents ranges of parameters which can be combined to create
 * simulations. Also, simulations can be manually added to groups, regardless of
 * parameters Group has four direct subobject, GroupStringParameters,
 * GroupNumericParameters GroupConfigurationFiles and GroupResultsFiles
 * 
 * @author alve
 * 
 */
public class Group extends DbBackedData<Simulation> implements Saveable {

	private int studyId;

	private String name;

	private GroupStringParameters stringParameters;

	private GroupNumericParameters numericParameters;

	private GroupConfigurationFiles configurationFiles;

	private GroupResultsFiles resultsFiles;

	/**
	 * create a group with a name for a given study. Useful for inserting into
	 * the database
	 * 
	 * @param db
	 * @param name
	 * @param studyId
	 */
	public Group(Connection db, String name, int studyId) {
		super(db);
		this.id = 0;
		this.name = name;
		this.studyId = studyId;
		configurationFiles = new GroupConfigurationFiles(db, id);
		resultsFiles = new GroupResultsFiles(db, id);
		stringParameters = new GroupStringParameters(db, id);
		numericParameters = new GroupNumericParameters(db, id);
	}

	/**
	 * recursively clone a group, assigning the same name, but no id
	 */
	public Group(Group other) {
		super(other.definitions);
		this.name = other.name;
		this.studyId = other.studyId;
		configurationFiles = new GroupConfigurationFiles(
				other.configurationFiles);
		resultsFiles = new GroupResultsFiles(other.resultsFiles);
		numericParameters = new GroupNumericParameters(other.numericParameters);
		stringParameters = new GroupStringParameters(other.stringParameters);
	}

	/**
	 * create a group from a database record. Load all the subobjects (but no
	 * simulations)
	 * 
	 * @param db
	 * @param groups
	 * @throws SQLException
	 */
	public Group(Connection db, ResultSet groups) throws SQLException {
		super(db);
		name = groups.getString("name");
		studyId = groups.getInt("studyId");
		id = groups.getInt("id");
		String idColumn = "groupId";
		configurationFiles = new GroupConfigurationFiles(db, id, loadSubObject(
				GroupConfigurationFiles.TABLE_NAME, idColumn));

		resultsFiles = new GroupResultsFiles(db, id, loadSubObject(
				GroupResultsFiles.TABLE_NAME, idColumn));

		numericParameters = new GroupNumericParameters(db, id, loadSubObject(
				GroupNumericParameters.TABLE_NAME, idColumn));

		stringParameters = new GroupStringParameters(db, id, loadSubObject(
				GroupStringParameters.TABLE_NAME, idColumn));

		ResultSet simulations = statement
				.executeQuery("SELECT * FROM simulations WHERE groupId = " + id
						+ ";");
		while (simulations.next()) {
			dependentData.add(new Simulation(db, simulations));
		}
	}

	/**
	 * clone a group assigning a new name, otherwise like Group(Group other)
	 * 
	 * @param group
	 * @param groupName
	 */
	public Group(Group group, String groupName) {
		this(group);
		name = groupName;
	}

	public Group(Group other, int studyId) {
		this(other);
		this.studyId = studyId;
	}
	
	private ResultSet loadSubObject(String tableName, String idColumn)
			throws SQLException {
		return statement.executeQuery("SELECT * FROM " + tableName + " WHERE "
				+ idColumn + "='" + id + "';");
	}

	/**
	 * update this group and its subobjects in the database updating the
	 * respective simulations isn't implemented yet
	 */
	public void performDbUpdate() throws SQLException {
		statement.executeUpdate("UPDATE groups SET studyId=" + studyId
				+ ", name='" + name + "' WHERE id=" + id + ";");
		commitAll();
	}

	/**
	 * recursively delete this group, its subobjects and its simulations from
	 * the database
	 */
	public void performDbDelete() throws SQLException {
		deleteSubObject(configurationFiles);
		deleteSubObject(resultsFiles);
		deleteSubObject(stringParameters);
		deleteSubObject(numericParameters);
		deleteSubObject(this);
		statement.executeUpdate("DELETE FROM groups WHERE id=" + id + ";");
	}

	private void deleteSubObject(DbBackedData b) throws SQLException {
		b.deleteAll();
		b.commit();
	}

	/**
	 * insert this group and its subobjects into the database. inserting the
	 * simulations isn't implemented yet.
	 */
	public void performDbInsert(int studyId) throws SQLException {
		this.studyId = studyId;
		statement.executeUpdate("INSERT INTO groups SET studyId=" + studyId
				+ ", name='" + name + "';", Statement.RETURN_GENERATED_KEYS);
		ResultSet key = statement.getGeneratedKeys();
		key.next();
		id = key.getInt(1);
		configurationFiles.id = id;
		resultsFiles.id = id;
		stringParameters.id = id;
		numericParameters.id = id;
		commitAll();
	}

	public void createSimulations() throws SQLException {
		SimulationCreator c = new SimulationCreator(definitions, this);
		Simulation sim = c.next();
		while(sim != null) {
			add(sim);
			sim  = c.next();
		}
	}
	
	private void commitAll() throws SQLException {
		commit();
		configurationFiles.commit();
		resultsFiles.commit();
		stringParameters.commit();
		numericParameters.commit();
	}

	/**
	 * load this group, its subobjects and its simulations from the database,
	 * using the id
	 */
	@Override
	public void loadFromDb() throws SQLException {
		ResultSet group = statement
				.executeQuery("SELECT * FROM groups WHERE id=" + id + ";");
		if (group.next()) {
			name = group.getString("name");
			studyId = group.getInt("studyId");
			ResultSet sims = statement
					.executeQuery("SELECT * FROM simulations WHERE groupId="
							+ id + ";");
			super.loadFromDb(sims);
		}
		numericParameters.loadFromDb();
		stringParameters.loadFromDb();
		configurationFiles.loadFromDb();
		resultsFiles.loadFromDb();
	}

	@Override
	public Simulation getEmptyDependent() {
		return new Simulation(definitions, id);
	}

	@Override
	public Simulation getDependent(ResultSet data) throws SQLException {
		return new Simulation(definitions, data);
	}

	@Override
	public Simulation getDependent(Simulation other) {
		return new Simulation(other);
	}

	public String getName() {
		return name;
	}

	public GroupNumericParameters getNumericParameters() {
		return numericParameters;
	}

	public GroupStringParameters getStringParameters() {
		return stringParameters;
	}

	public GroupConfigurationFiles getConfigurationFiles() {
		return configurationFiles;
	}

	public GroupResultsFiles getResultsFiles() {
		return resultsFiles;
	}
}
