package com.tandbergtv.watchpoint.pmm.title.conf;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import com.tandbergtv.metadatamanager.spec.ISpecHandler;
import com.tandbergtv.metadatamanager.spec.ITranslator;
import com.tandbergtv.metadatamanager.specimpl.SpecHandlerBase;

/**
 * Class that represent a PMM Title Specification.
 * 
 * @author Raj Prakash
 */
public class Specification {
	private String name;

	private String converterClass;

	private List<MenuOption> jobRuleMenuOptions = new ArrayList<MenuOption>();

	private List<MenuOption> jobParameterMenuOptions = new ArrayList<MenuOption>();

	private List<TitleConf> rootTitleConfs = new ArrayList<TitleConf>();

	private ClassLoader classLoader = null;

	private String tableConfigurationPath;

	private String definitionPath;
	
	private ISpecHandler specHandler;
	
	private ITranslator translator;
	
	private ResourceBundle bundle;
	
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the converterClass
	 */
	public String getConverterClass() {
		return converterClass;
	}

	/**
	 * @param converterClass the converterClass to set
	 */
	public void setConverterClass(String converterClass) {
		this.converterClass = converterClass;
	}

	/**
	 * @return the jobRuleMenuOptions
	 */
	public List<MenuOption> getJobRuleMenuOptions() {
		return jobRuleMenuOptions;
	}

	/**
	 * @param jobRuleMenuOptions the jobRuleMenuOptions to set
	 */
	void setJobRuleMenuOptions(List<MenuOption> jobRuleMenuOptions) {
		this.jobRuleMenuOptions = jobRuleMenuOptions;
	}

	/**
	 * @return the jobParameterMenuOptions
	 */
	public List<MenuOption> getJobParameterMenuOptions() {
		return jobParameterMenuOptions;
	}

	/**
	 * @param jobParameterMenuOptions the jobParameterMenuOptions to set
	 */
	void setJobParameterMenuOptions(List<MenuOption> jobParameterMenuOptions) {
		this.jobParameterMenuOptions = jobParameterMenuOptions;
	}

	/**
	 * Gets the root titleConf of this specification, if available. Else, it returns null.
	 */
	public TitleConf getRootTitleConf() {
		return (rootTitleConfs.size() > 0) ?  rootTitleConfs.get(0) : null; 
	}

	public List<TitleConf> getRootTitleConfs() {
		return rootTitleConfs;
	}
	
	/**
	 * Sets the root title conf for this specification.
	 */
	void setRootTitleConfs(List<TitleConf> rootTitleConfs) {
		this.rootTitleConfs = (rootTitleConfs != null) ? rootTitleConfs : new ArrayList<TitleConf>();
	}

	/**
	 * Add a single root title to the existing list of root titles
	 * @param rootTitleConf The title conf
	 */
	void addRootTitleConf(TitleConf rootTitleConf) {
		this.rootTitleConfs.add(rootTitleConf);
	}
	
	/**
	 * @return the classLoader
	 */
	public ClassLoader getClassLoader() {
		return classLoader;
	}

	/**
	 * @param classLoader the classLoader to set
	 */
	public void setClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	/**
	 * @return the tableConfigurationPath
	 */
	public String getTableConfigurationPath() {
		return tableConfigurationPath;
	}

	/**
	 * @param tableConfigurationPath the tableConfigurationPath to set
	 */
	public void setTableConfigurationPath(String tableConfigurationPath) {
		this.tableConfigurationPath = tableConfigurationPath;
	}

	/**
	 * @return the definitionPath
	 */
	public String getDefinitionPath() {
		return definitionPath;
	}

	/**
	 * @param definitionPath the definitionPath to set
	 */
	public void setDefinitionPath(String definitionPath) {
		this.definitionPath = definitionPath;
	}

	/**
	 * @return the bundle
	 */
	public ResourceBundle getBundle() {
		return bundle;
	}

	/**
	 * @param bundle the bundle to set
	 */
	public void setBundle(ResourceBundle bundle) {
		this.bundle = bundle;
	}

	/**
	 * Adds a job rule menu option.
	 * 
	 * @param name the name of the option
	 * @param value the value of the option
	 */
	void addJobRuleMenuOption(String name, String value) {
		if (jobRuleMenuOptions == null)
			jobRuleMenuOptions = new ArrayList<MenuOption>();
		jobRuleMenuOptions.add(new MenuOption(name, value));
	}

	/**
	 * Gets the job rule menu option for the given name. If name not found, returns null.
	 * 
	 * @param optionName the name of the job rule menu option
	 * @return
	 */
	public MenuOption getJobRuleMenuOption(String optionName) {
		if (jobRuleMenuOptions != null) {
			for (MenuOption jrmo : jobRuleMenuOptions) {
				if (jrmo.getName().equals(optionName))
					return jrmo;
			}
		}
		return null;
	}

	/**
	 * Gets the job parameter menu option for the given name. If name not found, returns null.
	 * 
	 * @param optionName the name of the job parameter menu option
	 * @return
	 */
	public MenuOption getJobParameterMenuOption(String optionName) {
		if (jobParameterMenuOptions != null) {
			for (MenuOption jpmo : jobParameterMenuOptions) {
				if (jpmo.getName().equals(optionName))
					return jpmo;
			}
		}
		return null;
	}

	/**
	 * Adds a job parameter menu option.
	 * 
	 * @param name the name of the option
	 * @param value the value of the option
	 */
	void addJobParameterMenuOption(String name, String value) {
		if (jobParameterMenuOptions == null)
			jobParameterMenuOptions = new ArrayList<MenuOption>();
		jobParameterMenuOptions.add(new MenuOption(name, value));
	}

	/**
	 * Gets all titleConf entities from all levels of the specification.
	 * 
	 * @return all titleConf entities from all levels of the specification
	 */
	public List<TitleConf> getAllTitleConf() {
		List<TitleConf> allTitleConf = new ArrayList<TitleConf>();
		for (TitleConf titleConf : rootTitleConfs) {
			allTitleConf.add(titleConf);
			allTitleConf.addAll(titleConf.getAllDescendants());
		}
		return allTitleConf;
	}

	/**
	 * Gets the titleConf that has the given name at any level.
	 * 
	 * @param name the name of the titleConf to find
	 * @return the titleConf that has the given name; null, if not found
	 */
	public TitleConf getTitleConfByName(String name) {
		for (TitleConf tc : getAllTitleConf()) {
			if (tc.getName().equals(name)) {
				return tc;
			}
		}
		return null;
	}

	/**
	 * Gets the complete metadata of the specification.
	 * 
	 * @return the complete metadata of the specification.
	 */
	public List<Variable> getMetadata() {
		List<Variable> allVariables = new ArrayList<Variable>();
		for (TitleConf tc : getAllTitleConf()) {
			allVariables.addAll(tc.getMetadata());
		}
		return allVariables;
	}

	/**
	 * Creates an instance of {@link IMetadataConverter} implementation configured for this
	 * specification.
	 * 
	 * @return The metadata converter implementation
	 */
	public IMetadataConverter getMetadataConverter() {
		try {
			Class<?> clazz = this.classLoader.loadClass(this.converterClass);
			return IMetadataConverter.class.cast(clazz.newInstance());
		} catch (Exception e) {
			String msg = "Failed to create an instance of "
					+ IMetadataConverter.class.getSimpleName() + " implementation: "
					+ this.converterClass;
			throw new RuntimeException(msg, e);
		}
	}
	
	/**
	 * @return the specHandler
	 */
	public ISpecHandler getSpecHandler() {
		return specHandler;
	}

	/**
	 * @param specHandler the specHandler to set
	 */
	public void setSpecHandler(ISpecHandler specHandler) {
		this.specHandler = specHandler;
	}

	/**
	 * Returns the translator that converts to the format-specific DOM using the internal format
	 * DOM
	 * 
	 * @return the translator
	 */
	public ITranslator getTranslator() {
		if (specHandler instanceof SpecHandlerBase)
			return SpecHandlerBase.class.cast(specHandler).getFromTTV();
		
		return translator;
	}

	/**
	 * @param translator the translator to set
	 */
	public void setTranslator(ITranslator translator) {
		this.translator = translator;
	}

	/**
	 * Get the InputStream for the Table Configuration Resource file to use for the current
	 * specification.
	 * 
	 * @return The Input Stream to the table configuration file.
	 */
	public InputStream getTableExtensionConfiguration() {
		return this.classLoader.getResourceAsStream(this.tableConfigurationPath);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return ("Specification: " + name);
	}
}
