/**
 * 
 */
package cz.neubauer.tomas.hazardanalysis.model.components;

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

import cz.neubauer.tomas.hazardanalysis.model.dangers.DangerGroup;
import cz.neubauer.tomas.hazardanalysis.model.dangers.DangerType;
import cz.neubauer.tomas.hazardanalysis.model.dangers.DangerVariant;

/**
 * Třída reprezentující jednu komponentu.
 * 
 * @author Tomáš Neubauer
 */
public class Component {
	/**
	 * ID - hodnota primárního klíče posledního vkládaného zaáznamu v databázi.
	 * Sdílená promenná používaná pro generování klíče pro vkládání nového
	 * záznamu do tabulky v databázi.
	 */
	private static int last_insert_id = -1;

	/**
	 * Klíč záznamu v datavázi.
	 */
	private int id;

	/**
	 * Název komponenty.
	 */
	private String name;

	/**
	 * Poloha komponenty.
	 */
	private String location;

	/**
	 * Kolekce skupin nebezpečí přiřazených komponentě - kořen hierarchie
	 * nebezpečí, které jsou přiřazeny komponentě.
	 */
	private ArrayList<DangerGroup> dangerGroups;

	/**
	 * Vytvoří novou komponentu s danými atributy.
	 * 
	 * @param id
	 *            Hodnota klíče komponenty v databázové tabulce.
	 * @param name
	 *            Název komponenty.
	 * @param location
	 *            Poloha komponenty.
	 */
	public Component(int id, String name, String location) {
		this.id = id;
		this.name = name;
		this.location = location;
		dangerGroups = new ArrayList<DangerGroup>(10);
	}

	/**
	 * Vrátí název komponenty.
	 * 
	 * @return Název komponenty.
	 */
	public String getName() {
		return name;
	}

	/**
	 * Nastaví název komponenty.
	 * 
	 * @param name
	 *            Název komponenty.
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Vloží do kolekce novou skupinu nebezpečí.
	 * 
	 * @param danger
	 *            Vkládaná skupina nebezpečí.
	 */
	public void addDangerGroup(DangerGroup danger) {
		dangerGroups.add(danger);
	}

	/**
	 * Vrátí skupinu nebezpečí, která se nachází v kolekci na pozici s daným
	 * indexem.
	 * 
	 * @param index
	 *            Index pozice skupiny nebezpečí v kolekci.
	 * @return Skupina nebezpečí na dané pozici.
	 */
	public DangerGroup getDangerGroup(int index) {
		return dangerGroups.get(index);
	}

	/**
	 * Vrátí aktuální počet skupin nebezpečí.
	 * 
	 * @return Počet skupin nebezpečí.
	 */
	public int getDangerGroupCount() {
		return dangerGroups.size();
	}

	/**
	 * Vrátí kolekci všech typů nebezpečí v hierarchii nebezpečí.
	 * 
	 * @return Kolekce typů nebezpečí.
	 */
	public ArrayList<DangerType> getDangerTypes() {
		ArrayList<DangerType> types = new ArrayList<DangerType>(10);
		int dangerGroupCount = dangerGroups.size();
		for (int i = 0; i < dangerGroupCount; i++) {
			DangerGroup currentDangerGroup = dangerGroups.get(i);
			int dangerTypeCount = currentDangerGroup.getDangerTypeCount();
			for (int j = 0; j < dangerTypeCount; j++) {
				DangerType currentDangerType = currentDangerGroup
						.getDangerType(j);
				types.add(currentDangerType);
			}
		}
		return types;
	}

	/**
	 * Vrátí textový řetězec obsahující seznam skupin nebezpečí oddělený čárkou
	 * (',').
	 * 
	 * @return Textový řetězec obsahující seznam skupin nebezpečí.
	 */
	public String getDangersAsString() {
		String retVal = "";
		int dangerGroupCount = dangerGroups.size();
		int count = 0;
		for (int i = 0; i < dangerGroupCount; i++) {
			DangerGroup currentDangerGroup = dangerGroups.get(i);
			int dangerTypeCount = currentDangerGroup.getDangerTypeCount();
			for (int j = 0; j < dangerTypeCount; j++) {
				DangerType currentDangerType = currentDangerGroup
						.getDangerType(j);
				if (count > 0) {
					retVal += ", ";
				}
				retVal += currentDangerType.getDescription();
				count++;
			}
		}
		return retVal;
	}

	/**
	 * Vrátí klíč záznamu v databázové tabulce.
	 * 
	 * @return Klíč záznamu.
	 */
	public int getId() {
		return id;
	}

	/**
	 * Nastaví klíč záznamu.
	 * 
	 * @param id
	 *            Nový klíč záznamu.
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * Vrátí kolekci skupin nebezpečí.
	 * 
	 * @return Kolekce skupin nebezpečí.
	 */
	public ArrayList<DangerGroup> getDangerGroups() {
		return dangerGroups;
	}

	/**
	 * Nastaví kolekci skupin nebezpečí.
	 * 
	 * @param dangerGroups
	 *            Nová kolekce skupin nebezpečí.
	 */
	public void setDangerGroups(ArrayList<DangerGroup> dangerGroups) {
		this.dangerGroups = dangerGroups;
	}

	/**
	 * Uloží záznam do databáze prostřednictvím daného připojení. Pokud se v
	 * tabulce záznam s daným klíčem nachází, pouze jej aktualizuje (UPDATE),
	 * jinak se do tabulky vloží nový záznam (INSERT).
	 * 
	 * @param connection
	 *            Připojení k databázi.
	 */
	public void saveToDB(Connection connection) {
		if (connection != null) {
			try {
				Statement stmt = connection.createStatement();
				if (last_insert_id < 1) {
					stmt.executeQuery("SELECT max(id) FROM component;");
					ResultSet rs = stmt.getResultSet();
					if (rs.next()) {
						last_insert_id = rs.getInt(1);
					}
				}
				stmt
						.executeQuery("SELECT count(id) as count FROM component WHERE id = '"
								+ id + "';");
				ResultSet rs1 = stmt.getResultSet();
				if (rs1.next() && rs1.getInt(1) == 0) {
					stmt.executeUpdate("INSERT INTO component VALUES ("
							+ (last_insert_id > 0 ? "'" + (++last_insert_id)
									+ "'" : "LAST_INSERT_ID()") + ", '" + name
							+ "', '" + location + "');",
							Statement.RETURN_GENERATED_KEYS);
				} else {

					stmt.executeUpdate("UPDATE component SET name='" + name
							+ "', location='" + location + "' WHERE id='" + id
							+ "';");
				}
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Smaže záznam z databáze prostřednictvím daného připojení a zruší
	 * instanci.
	 * 
	 * @param connection
	 *            Připojení k databázi.
	 */
	public void deleteFromDB(Connection connection) {
		if (connection != null) {
			try {
				Statement stmt = connection.createStatement();

				stmt.executeUpdate("DELETE FROM component WHERE id = '" + id
						+ "';");

				stmt
						.executeUpdate("DELETE FROM component_danger_variant WHERE component_id = '"
								+ id + "';");
				stmt.close();
				finalize();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Prohledá hierarchii nebezpečí a rozhodne o tom, zda-li se daná varianta
	 * nebezpečí v hierarchii nachází.
	 * 
	 * @param dangerVariant
	 *            Hledaná varianta nebezpečí.
	 * @return <code>true</code>, pokud se daná varianta nebepzečí nachází v
	 *         hierarchii, jinak vrátí <code>false</code>.
	 */
	public boolean hasDangerVariant(DangerVariant dangerVariant) {

		int dangerGroupCount = dangerGroups.size();
		DangerGroup currentDangerGroup;
		for (int i = 0; i < dangerGroupCount; i++) {
			currentDangerGroup = dangerGroups.get(i);
			DangerType dangerType = dangerVariant.getDangerType();
			if (dangerType.getDangerGroup().getId() == currentDangerGroup
					.getId()) {
				ArrayList<DangerType> dangerTypes = currentDangerGroup
						.getTypes();
				int dangerTypeCount = dangerTypes.size();
				DangerType currentDangerType;
				for (int j = 0; j < dangerTypeCount; j++) {
					currentDangerType = dangerTypes.get(j);
					if (dangerVariant.getDangerType().getId() == currentDangerType
							.getId()) {
						ArrayList<DangerVariant> dangerVariants = currentDangerType
								.getVariants();
						int dangerVariantCount = dangerVariants.size();
						DangerVariant currentDangerVariant;
						for (int k = 0; k < dangerVariantCount; k++) {
							currentDangerVariant = dangerVariants.get(k);
							if (currentDangerVariant.getId() == dangerVariant
									.getId()) {
								return true;
							}
						}
					}
				}
			}
		}
		return false;
	}

	/**
	 * Vrátí kolekci všech variant nebezpečí v hierarchii nebezpečí.
	 * 
	 * @return Kolekce variant nebezpečí.
	 */
	public ArrayList<DangerVariant> getDangerVariants() {
		ArrayList<DangerVariant> variants = new ArrayList<DangerVariant>(100);
		int dangerGroupCount = dangerGroups.size();
		DangerGroup dg;
		DangerType dt;
		for (int i = 0; i < dangerGroupCount; i++) {
			dg = dangerGroups.get(i);
			int dtc = dg.getDangerTypeCount();

			for (int j = 0; j < dtc; j++) {
				dt = dg.getDangerType(j);
				int dvc = dt.getDangerVariantCount();
				for (int k = 0; k < dvc; k++) {
					variants.add(dt.getDangerVariant(k));
				}
			}
		}

		return variants;
	}

	/**
	 * Vrátí kolekci všech variant nebezpečí v hierarchii nebezpečí, které jsou
	 * označeny jako závažné (kritické).
	 * 
	 * @return Kolekce závažných variant nebezpečí.
	 */
	public ArrayList<DangerVariant> getCriticalDangerVariants() {
		ArrayList<DangerVariant> variants = new ArrayList<DangerVariant>(100);
		int dangerGroupCount = dangerGroups.size();
		DangerGroup dg;
		DangerType dt;
		DangerVariant dv;
		for (int i = 0; i < dangerGroupCount; i++) {
			dg = dangerGroups.get(i);
			int dtc = dg.getDangerTypeCount();

			for (int j = 0; j < dtc; j++) {
				dt = dg.getDangerType(i);
				int dvc = dt.getDangerVariantCount();
				for (int k = 0; k < dvc; k++) {
					dv = dt.getDangerVariant(k);
					if (dv.isCritical()) {
						variants.add(dv);
					}
				}
			}
		}

		return variants;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return name;
	}

	/**
	 * Vrátí polohu komponenty.
	 * 
	 * @return Poloha komponenty.
	 */
	public String getLocation() {
		return location;
	}

	/**
	 * Nastaví polohu komponenty.
	 * 
	 * @param location
	 *            Nová poloha komponenty.
	 */
	public void setLocation(String location) {
		this.location = location;
	}
}
