/*
 * Copyright (C) 2013 by MHC SoftWare GmbH, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package com.MHC.propertyManager;
import xdev.util.auth.EncryptedPassword;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Properties;
/**
 * Generische Properties
 * 
 * 
 * Verwendung in Singelton der die Properties für die Verwendung in der
 * Applikation definiert und MhcProperties initialisiert auf Basis der
 * abstrakten Klasse "MhcAppProperties"
 * 
 * @see MhcAppProperties
 * 
 * @author MHC SoftWare GmbH
 */
public class MhcProperties
{
	private HashMap>	pList		= new HashMap>();
	private HashMap		dList		= new HashMap();
	private Properties					properties	= new Properties();
	private String						fileName	= null;
	
	/**
	 * MhcProperties Construktor 
	 * @param pFileName		Dateiname ohne Endung und Pfad
	 */
	public MhcProperties(String pFileName)
	{
		fileName = pFileName + ".properties";
	}
	
	/**
	 * Private Methode die prüft ob eine Property vorhanden ist und deren Wer als String zurück gibt.
	 * Wird von allen anderen "get"-Methoden benutzt.
	 * 
	 * @return String Wert der Proptery
	 */
	private String getProperty(String name) throws PropertyNotFoundException
	{
		if(!pList.containsKey(name))
		{
			throw new PropertyNotFoundException(name);
		}
		return properties.getProperty(name,dList.get(name));
	}
	
	/**
	 * Prüft ob die Proptery vom Typ String ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isStringProptery(String name)
	{
		return pList.get(name) == String.class;
	}
	
	/**
	 * Prüft ob die Proptery vom Typ Integer ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isIntegerProptery(String name)
	{
		return pList.get(name) == Integer.class;
	}
	
	/**
	 * Prüft ob die Proptery vom Typ Short ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isShortProptery(String name)
	{
		return pList.get(name) == Short.class;
	}
	
	/**
	 * Prüft ob die Proptery vom Typ Long ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isLongProptery(String name)
	{
		return pList.get(name) == Long.class;
	}
	
	/**
	 * Prüft ob die Proptery vom Typ Float ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isFloatProptery(String name)
	{
		return pList.get(name) == Float.class;
	}
	
	/**
	 * Prüft ob die Proptery vom Typ Double ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isDoubleProptery(String name)
	{
		return pList.get(name) == Double.class;
	}
	
	/**
	 * Prüft ob die Proptery vom Typ Boolean ist
	 * @param name Proptertyname
	 * @return true / false
	 */
	public boolean isBooleanProptery(String name)
	{
		return pList.get(name) == Boolean.class;
	}
	
	/**
	 * Lädt die Propterist aus der Datei
	 *  
	 * @throws PropertiesFileNotLoades
	 */
	public void load() throws PropertiesFileNotLoaded
	{
		try
		{
			InputStream in = new FileInputStream(fileName);
			properties.load(in);
			in.close();
			
		}
		catch(FileNotFoundException e)
		{
			throw new PropertiesFileNotLoaded(e);
		}
		catch(IOException e)
		{
			throw new PropertiesFileNotLoaded(e);
		}
	}
	
	/**
	 * Prüft ob die Propertisdatei existiert
	 * @return true / false
	 */
	public boolean pFileExists()
	{
		return new File(fileName).isFile();
	}
	
	/**
	 * Speichert die Properties in der vorgegebenen Datei
	 * 
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public void save() throws FileNotFoundException, IOException
	{
		OutputStream out = new FileOutputStream(fileName);
		properties.store(out,null);
		out.close();
	}
	
	/**
	 * Definiert eine neue Property. Bevor auf eine Property zugegriffen
	 * werden kann muss diese definiert werden
	 * 
	 * @param name			Name der Property 
	 * @param cls			Type der Property, z.B. String.class
	 * @param defaultVlaue	Defaultwert
	 */
	public void add(String name, Class> cls, Object defaultVlaue)
	{
		pList.put(name,cls);
		dList.put(name,defaultVlaue.toString());
	}
	
	/**
	 * Liefert die Anzahl der definierten Properties
	 * 
	 * @return Anzahl der definierten Properties 
	 */
	public int getPropertiesCount()
	{
		return pList.size();
	}
	
	/**
	 * Liefert den Wert einer Propterty als String unabhägig vom Typ
	 * 
	 * @param name			Name der Propterty
	 * 
	 * @return Proptertywert als String
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public String get(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(!pList.containsKey(name))
		{
			throw new PropertyNotFoundException(name);
		}
		
		return getProperty(name);
	}
	
	/**
	 * Liefert Passwort als Klartext
	 * 
	 * @param name			Name der Propterty
	 * 
	 * @return Proptertywert als String
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public String getDecrypted(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(!pList.containsKey(name))
		{
			throw new PropertyNotFoundException(name);
		}
		
		return new EncryptedPassword(getProperty(name)).getPlainText();
	}
	
	/**
	 * Liefert den Wert einer Property als String. Der Typ der Property muss vom Typ String
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als String
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public String getString(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isStringProptery(name))
		{
			return getProperty(name);
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Liefert den Wert einer Property als Integer. Der Typ der Property muss vom Typ Integer
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als Integer
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public int getInt(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isIntegerProptery(name))
		{
			return Integer.parseInt(getProperty(name));
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Liefert den Wert einer Property als Long. Der Typ der Property muss vom Typ Long
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als Long
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public long getLong(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isLongProptery(name))
		{
			return Long.parseLong(getProperty(name));
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Liefert den Wert einer Property als Short. Der Typ der Property muss vom Typ Short
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als Short
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public short getShort(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isShortProptery(name))
		{
			return Short.parseShort(getProperty(name));
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Liefert den Wert einer Property als Float. Der Typ der Property muss vom Typ Float
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als Float
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public float getFloat(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isFloatProptery(name))
		{
			return Float.parseFloat(getProperty(name));
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Liefert den Wert einer Property als Doubble. Der Typ der Property muss vom Typ Doubble
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als Doubble
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public double getDoubble(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isDoubleProptery(name))
		{
			return Double.parseDouble(getProperty(name));
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Liefert den Wert einer Property als Boolean. Der Typ der Property muss vom Typ Boolean
	 * sein, ansosten wir die IlligalTypeException geworfen.
	 * 
	 * @param name			Name der Property
	 *  
	 * @return Proptertywert als Boolean
	 *  
	 * @throws PropertyNotFoundException
	 * @throws IlligalTypeException
	 */
	public boolean getBoolean(String name) throws PropertyNotFoundException, IlligalTypeException
	{
		if(isBooleanProptery(name))
		{
			return Boolean.parseBoolean(getProperty(name));
		}
		throw new IlligalTypeException();
	}
	
	/**
	 * Setzt den Wert einer Property. Falls dert Typ der Variablen des übergebenen Wertes
	 * nicht mit dem defineirten Typ der Poperty übereinstimmt wird eine IlligalTypeException
	 * geworfen. Wenn die Propterty nicht definiert ist wird eine PropertyNotFoundException
	 * geworfen.
	 *  
	 * @param name			Name der Property 
	 * @param value 		Wert der Property in einer dem Typ entsprechenden Varilablen.
	 * 
	 * @throws IlligalTypeException
	 * @throws PropertyNotFoundException
	 */
	public void set(String name, Object value) throws IlligalTypeException,
			PropertyNotFoundException
	{
		if(!pList.containsKey(name))
		{
			throw new PropertyNotFoundException();
		}
		if(value instanceof String && isStringProptery(name))
		{
			properties.put(name,value.toString());
		}
		else if(value instanceof Integer && isIntegerProptery(name))
		{
			properties.put(name,value.toString());
		}
		else if(value instanceof Long && isLongProptery(name))
		{
			properties.put(name,value.toString());
		}
		else if(value instanceof Short && isShortProptery(name))
		{
			properties.put(name,value.toString());
		}
		else if(value instanceof Float && isFloatProptery(name))
		{
			properties.put(name,value.toString());
		}
		else if(value instanceof Double && isDoubleProptery(name))
		{
			properties.put(name,value.toString());
		}
		else if(value instanceof Boolean && isBooleanProptery(name))
		{
			properties.put(name,value.toString());
		}
		else
		{
			throw new IlligalTypeException();
		}
	}
	
	/**
	 * Setzt den Wert einer Property als verschlüsselte Passwort.
	 *  
	 * @param name			Name der Property 
	 * @param value 		Wert der Property in einer dem Typ entsprechenden Varilablen.
	 * 
	 * @throws IlligalTypeException
	 * @throws PropertyNotFoundException
	 */
	public void setEncrypted(String name, Object value) throws IlligalTypeException,
			PropertyNotFoundException
	{
		if(!pList.containsKey(name))
		{
			throw new PropertyNotFoundException();
		}
		if(value instanceof String && isStringProptery(name))
		{
			properties.put(name,EncryptedPassword.encrypt(value.toString()));
		}
		else
		{
			throw new IlligalTypeException();
		}
	}
	/** Uncheckd RuntimeExceptions um die Methoden in default Construktoren verwendbar zu machen */
	
	/**
	 * RuntimeException: Wird geworfen wenn eine nicht vorhandene Property abgefragt wird
	 */
	public static class PropertyNotFoundException extends RuntimeException
	{
		public PropertyNotFoundException()
		{
			super();
		}
		
		public PropertyNotFoundException(Exception e)
		{
			super();
		}
		
		public PropertyNotFoundException(String s)
		{
			super(s);
		}
	}
	
	/**
	 * RuntimeException: Wird geworfen wenn der Typ des Werts nicht zum definierten Typ der Property passt 
	 */
	public static class IlligalTypeException extends RuntimeException
	{
		public IlligalTypeException()
		{
			super();
		}
		
		public IlligalTypeException(Exception e)
		{
			super();
		}
		
		public IlligalTypeException(String s)
		{
			super(s);
		}
	}
	
	/**
	 * RuntimeException: Wird geworfen wenn die Properties nicht geladen werden konnten (IOException/FileNoFoundExecption)
	 */
	public static class PropertiesFileNotLoaded extends RuntimeException
	{
		public PropertiesFileNotLoaded()
		{
			super();
		}
		
		public PropertiesFileNotLoaded(Exception e)
		{
			super();
		}
		
		public PropertiesFileNotLoaded(String s)
		{
			super(s);
		}
	}
	
}