/*
 * Copyright (c) 2011, Patrik Dufresne. All rights reserved.
 * Patrik Dufresne PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.planimod.core;

import java.util.Arrays;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;

import com.patrikdufresne.managers.DatabaseUpdateHelper;
import com.patrikdufresne.managers.DatabaseUrl;
import com.patrikdufresne.managers.IManager;
import com.patrikdufresne.managers.ManagerException;
import com.patrikdufresne.managers.Managers;

/**
 * Used to easily access one set of manager using the same session factory.
 * 
 * @author Patrik Dufresne
 * 
 */
public class PlanimodManagers extends Managers {

	/**
	 * Constant for version 1.0
	 */
	public static final String VERSION_10 = "1.0"; //$NON-NLS-1$

	/**
	 * Determine the database version
	 * 
	 * @param factory
	 *            the session factory
	 * @return the database version or null if it can be determined
	 */
	private static String determineVersion(SessionFactory factory) {
		if (isVersion10(factory)) {
			return VERSION_10;
		}
		return null;
	}

	/**
	 * Check if the database is version 10.
	 * 
	 * @param factory
	 * @return True if the database metadata matches version10.
	 */
	private static boolean isVersion10(SessionFactory factory) {
		String schema = DatabaseUpdateHelper.getSchema(factory);
		String catalog = DatabaseUpdateHelper.getCatalog(factory);
		List<String> expectedTables = Arrays.asList("ApplicationSetting",
				"Employee", "EmployeePreference", "NonAvailability",
				"Position", "ProductionEvent", "Product", "ProductPosition",
				"Qualification", "Section", "Shift", "Team");
		for (String expectedTable : expectedTables) {
			if (DatabaseUpdateHelper.getTableMetadata(factory, expectedTable,
					schema, catalog) == null) {
				return false;
			}
		}
		return true;
	}

	/**
	 * The application setting manager in this sets of managers.
	 */
	private ApplicationSettingManager applicationSettingManager;
	/**
	 * The employee manager in this sets of managers.
	 */
	private EmployeeManager employeeManager;
	/**
	 * The employee preference manager in this sets of managers.
	 */
	private EmployeePreferenceManager employeePreferenceManager;

	/**
	 * The calendar event manager in this sets of managers.
	 */
	private NonAvailabilityManager nonAvailabilityManager;

	/**
	 * The Position manager in this sets of managers.
	 */
	private PositionManager positionManager;

	private ProductionEventManager productionEventManager;

	/**
	 * The product manager in this sets of managers.
	 */
	private ProductManager productManager;

	/**
	 * The product position manager in this sets of managers.
	 */
	private ProductPositionManager productPositionManager;

	/**
	 * The Qualification manager in this sets of managers.
	 */
	private QualificationManager qualificationManager;

	/**
	 * The Section manager in this sets of managers.
	 */
	private SectionManager sectionManager;

	private ShiftManager shiftManager;

	/**
	 * This planif event manager in this sets of managers.
	 */
	private TaskManager taskManager;

	/**
	 * The non-availability manager in this sets of managers.
	 */
	private TeamManager teamManager;

	/**
	 * Create a new managers with the given session factory.
	 * 
	 * @param factory
	 * @throws ManagerException
	 *             If the database can't be started or created
	 */
	public PlanimodManagers(DatabaseUrl url) throws ManagerException {
		super(url);
	}

	@Override
	protected void configure(Configuration cfg) {
		super.configure(cfg);
		// Identify persistant class
		cfg.addAnnotatedClass(ApplicationSetting.class);
		cfg.addAnnotatedClass(Employee.class);
		cfg.addAnnotatedClass(EmployeePreference.class);
		cfg.addAnnotatedClass(NonAvailability.class);
		cfg.addAnnotatedClass(Task.class);
		cfg.addAnnotatedClass(Position.class);
		cfg.addAnnotatedClass(Product.class);
		cfg.addAnnotatedClass(ProductionEvent.class);
		cfg.addAnnotatedClass(ProductPosition.class);
		cfg.addAnnotatedClass(Qualification.class);
		cfg.addAnnotatedClass(Section.class);
		cfg.addAnnotatedClass(Team.class);
		cfg.addAnnotatedClass(Shift.class);
	}

	public ApplicationSettingManager getApplicationSettingManager() {
		if (this.applicationSettingManager == null) {
			this.applicationSettingManager = new ApplicationSettingManager(this);
		}
		return this.applicationSettingManager;
	}

	/**
	 * Return the employee manager in this sets of managers.
	 * 
	 * @return
	 */
	public EmployeeManager getEmployeeManager() {
		if (this.employeeManager == null) {
			this.employeeManager = new EmployeeManager(this);
		}
		return this.employeeManager;
	}

	public EmployeePreferenceManager getEmployeePreferenceManager() {
		if (this.employeePreferenceManager == null) {
			this.employeePreferenceManager = new EmployeePreferenceManager(this);
		}
		return this.employeePreferenceManager;
	}

	@Override
	public IManager getManagerForClass(Class clazz) {
		IManager m[] = new IManager[] { this.getApplicationSettingManager(),
				this.getTeamManager(), this.getNonAvailabilityManager(),
				this.getEmployeeManager(), this.getEmployeePreferenceManager(),
				this.getTaskManager(), this.getPositionManager(),
				this.getProductManager(), this.getProductPositionManager(),
				this.getQualificationManager(), this.getSectionManager(),
				this.getShiftManager(),this.getProductionEventManager() };
		IManager match = null;
		IManager perfectMatch = null;
		for (IManager n : m) {
			if (n.objectClass().equals(clazz)) {
				perfectMatch = n;
			} else if (n.objectClass().isAssignableFrom(clazz)) {
				match = n;
			}
		}
		if (perfectMatch != null) {
			return perfectMatch;
		}
		return match;
	}

	public NonAvailabilityManager getNonAvailabilityManager() {
		if (this.nonAvailabilityManager == null) {
			this.nonAvailabilityManager = new NonAvailabilityManager(this);
		}
		return this.nonAvailabilityManager;
	}

	/**
	 * Return the position manager in this sets of managers.
	 * 
	 * @return
	 */
	public PositionManager getPositionManager() {
		if (this.positionManager == null) {
			this.positionManager = new PositionManager(this);
		}
		return this.positionManager;
	}

	public ProductionEventManager getProductionEventManager() {
		if (this.productionEventManager == null) {
			this.productionEventManager = new ProductionEventManager(this);
		}
		return this.productionEventManager;
	}

	/**
	 * Return the shift manager in this sets of managers.
	 * 
	 * @return
	 */
	public ProductManager getProductManager() {
		if (this.productManager == null) {
			this.productManager = new ProductManager(this);
		}
		return this.productManager;
	}

	/**
	 * Return the product-position manager in this sets of managers.
	 * 
	 * @return
	 */
	public ProductPositionManager getProductPositionManager() {
		if (this.productPositionManager == null) {
			this.productPositionManager = new ProductPositionManager(this);
		}
		return this.productPositionManager;
	}

	/**
	 * Return the qualification manager from this sets of managers.
	 * 
	 * @return
	 */
	public QualificationManager getQualificationManager() {
		if (this.qualificationManager == null) {
			this.qualificationManager = new QualificationManager(this);
		}
		return this.qualificationManager;
	}

	/**
	 * Return the section manager from this sets of managers.
	 * 
	 * @return
	 */
	public SectionManager getSectionManager() {
		if (this.sectionManager == null) {
			this.sectionManager = new SectionManager(this);
		}
		return this.sectionManager;
	}

	public ShiftManager getShiftManager() {
		if (this.shiftManager == null) {
			this.shiftManager = new ShiftManager(this);
		}
		return this.shiftManager;
	}

	/**
	 * Return the planif event manager in this sets of managers.
	 * 
	 * @return
	 */
	public TaskManager getTaskManager() {
		if (this.taskManager == null) {
			this.taskManager = new TaskManager(this);
		}
		return this.taskManager;
	}

	/**
	 * Return the non-availability manager in this sets of managers.
	 * 
	 * @return
	 */
	public TeamManager getTeamManager() {
		if (this.teamManager == null) {
			this.teamManager = new TeamManager(this);
		}
		return this.teamManager;
	}

	/**
	 * This implementation check if the database is valid and update it.
	 */
	@Override
	protected void updateDatabase(SessionFactory factory) {
		String version = determineVersion(factory);
		if (version == null) {
			throw new HibernateException("Database is invalid.");
		}
		if (version.equals(VERSION_10)) {
			return;
		}
	}

	/**
	 * This implementation check the database version.
	 */
	@Override
	protected void validateDatabase(SessionFactory factory) {
		String version = determineVersion(factory);
		if (version == null) {
			throw new HibernateException("Database is invalid.");
		}
		if (!version.equals(VERSION_10)) {
			throw new HibernateException("Database should be updated.");
		}
	}

}
