/*
 * $Id: AbstractDataStore.java,v 1.12 2005/03/24 12:57:04 lawern Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: AbstractDataStore.java,v $
 * Revision 1.12  2005/03/24 12:57:04  lawern
 * Umstellung des TDF-Formats auf Version 1.1 in UTF-8-Kodierung.
 *
 * Revision 1.11  2004/11/10 15:02:49  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.10  2004/08/11 11:29:19  marbobo
 * - Indirekter Zugriff auf statische Methode entfernt
 *
 * Revision 1.9  2004/08/09 11:43:08  koppy
 * Falls der Aufräumzyklus innerhalb der Backup-Zeit gestartet wird,
 * so wird er nocheinmal nach Backup-Ende gescheduled.
 *
 * Revision 1.8  2004/06/24 23:30:18  marbobo
 * Quellcode maintenance
 *
 * Revision 1.7  2004/01/16 14:27:14  koppy
 * Log message umbenannt
 *
 * Revision 1.6  2003/12/24 10:12:03  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.5  2003/11/21 14:47:36  koppy
 * Um die Möglichkeit, "Archivleichen" zu entfernen, erweitert
 *
 * Revision 1.4  2003/05/26 15:50:01  koppy
 * SS um getFile( id:string ) erweitert
 *
 * Revision 1.3  2003/04/22 12:33:03  koppy
 * + getReader( String id )
 *
 * Revision 1.2  2002/11/12 15:33:01  koppy
 * Behandlung von Streams hinzugefügt
 *
 * Revision 1.1  2002/10/21 14:36:13  bigmac
 * *** empty log message ***
 *
 * Revision 1.2  2002/04/29 14:51:13  koppy
 * Verwendung der RWLock-Klasse
 *
 * Revision 1.1  2002/04/05 15:35:19  koppy
 * initial revision
 *
 */

package vkc.server.archive;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;

import vkc.db.serializer.VKCSysParameterSerializer;
import vkc.exception.DBException;
import vkc.misc.VKCLogManager;

public abstract class AbstractDataStore implements DataStore {

	protected RWLock rwLock = new RWLock();		

	public AbstractDataStore() {
			
	}

	/**
	 * Öffnet das "Datenlager"
	 *
	 * @author koppy
	 */
	public void open() throws DataStoreException{
		openDataStore();
	}
	
	protected abstract void openDataStore() throws DataStoreException;

	/**
	 * Schließt das "Datenlager"
	 *
	 * @author koppy
	 */
	public void close() throws DataStoreException{
		closeDataStore();
	}
	
	protected abstract void closeDataStore( ) throws DataStoreException;

	/**
	 * Setzt die Url für das "Datenlager"
	 *
	 * @param   storageUrl - unter dieser Url verbirgt sich das "Datenlager"
	 * @exception   DataStoreException
	 * @author koppy
	 */
	public abstract void setStorageUrl(String storageUrl)
		throws DataStoreException;

	/**
	 * Liefert die Url für das "Datenlager"
	 *
	 * @return   storageUrl - unter dieser Url verbirgt sich das "Datenlager"
	 * @author koppy
	 */
	public abstract String getStorageUrl();

	/**
	 * Speichert ein Byte-Array unter gegebener ID
	 *
	 * @param   data - ein Byte-Array
	 * @param   id
	 * @exception   DataStoreException
	 * @author koppy
	 */
	public void store(byte[] data, String id) throws DataStoreException {

		rwLock.forWriting(id);

		try {
			storeData(data, id);
		} finally {
			rwLock.releaseWriting(id);
		}
	}

	/**
	 * Speichert ein Byte-Array und gibt eine ID zurueck
	 *
	 * @param   data  - ein Byte-Array
	 * @return id
	 * @exception   DataStoreException
	 * @author koppy
	 */
	public String store(byte[] data) throws DataStoreException {

		// ID generieren
		String id = DataStoreShare.getUniqueID();
		store(data, id);
		return id;
	}

	/**
	 * Speichert einen InputStream und gibt eine ID zurueck
	 *
	 * @param   inputStream  ein InputStream
	 * @param id auto-generierte ID
	 * @exception   DataStoreException
	 * @author koppy
	 */
	public void store(InputStream inputStream, String id)
		throws DataStoreException {

		rwLock.forWriting(id);

		try {
			storeInputStream(inputStream, id);
		} finally {
			rwLock.releaseWriting(id);
		}

	}

	/**
	 * Speichert einen InputStream und gibt eine ID zurueck
	 *
	 * @param   inputStream  ein InputStream
	 * @return id - auto-generierte ID
	 * @exception   DataStoreException
	 * @author koppy
	 */
	public String store(InputStream inputStream) throws DataStoreException {

		// ID generieren
		String id = DataStoreShare.getUniqueID();
		store(inputStream, id);
		return id;
	}

	/**
	 * Fuegt ein Byte-Array einem vorhandenen hinzu
	 *
	 * @param   data - ein Byte-Array
	 * @param   id
	 * @exception   DataStoreException
	 * @author koppy
	 */
	public void append(byte[] data, String id) throws DataStoreException {

		rwLock.forWriting(id);

		try {
			appendData(data, id);
		} finally {
			rwLock.releaseWriting(id);
		}

	}

	/**
	 * Speichert ein Byte-Array unter gegebener ID
	 *
	 * @param   data - ein Byte-Array
	 * @param   id
	 * @exception   DataStoreException
	 * @author koppy
	 */
	protected abstract void storeData(byte[] data, String id)
		throws DataStoreException;

	/**
	 * Speichert einen InputStream unter gegebener ID
	 *
	 * @param   inputStream der InputStream
	 * @param   id
	 * @exception   DataStoreException
	 * @author koppy
	 */
	protected abstract void storeInputStream(InputStream data, String id)
		throws DataStoreException;

	/**
	 * Fuegt ein Byte-Array einem vorhandenen hinzu
	 *
	 * @param   data - ein Byte-Array
	 * @param   id
	 * @exception   DataStoreException
	 * @author koppy
	 */
	protected abstract void appendData(byte[] data, String id)
		throws DataStoreException;

	/**
	 * Lädt das Byte-Array einer gegebenen ID
	 *
	 * @param   id
	 * @return ein Byte-Array
	 * @exception   DataStoreException
	 */
	public byte[] load(String id) throws DataStoreException {

		rwLock.forReading(id);

		try {
			return loadData(id);
		} finally {
			rwLock.releaseReading(id);
		}

	}

	/**
	 * Lädt das unter ID gespeicherte Datum und gibt es als
	 * InputStream zurück
	 *
	 * @param   id
	 * @return den InputStream
	 * @exception  DataStoreException
	 * @author koppy
	 */
	public InputStream getInputStream(String id) throws DataStoreException {

		rwLock.forReading(id);

		try {
			return loadDataAsInputStream(id);
		} finally {
			rwLock.releaseReading(id);
		}
	}

	/**
	 * Lädt das unter ID gespeicherte Datum und gibt es als
	 * Reader zurück
	 *
	 * @param   id
	 * @return den Reader
	 * @exception  DataStoreException
	 */
	public Reader getReader(String id) throws DataStoreException {

		rwLock.forReading(id);

		try {
			try {
				return new BufferedReader(
					new InputStreamReader(this.loadDataAsInputStream(id), "UTF-8"));
			} catch (UnsupportedEncodingException e) {
				return null;
			}
		} finally {
			rwLock.releaseReading(id);
		}

	}


	/**
	 * Lädt das unter ID gespeicherte Datum und gibt es
	 * als File - Objekt zurück
	 *
	 * @param   id
	 * @return das File-Objekt
	 * @exception  DataStoreException
	 */
	public File getFile( String id )
		throws DataStoreException{
	    rwLock.forReading(id);

		try {
			return loadDataAsFile(id);
		} finally {
			rwLock.releaseReading(id);
		}
	}

	/**
	 * Lädt das unter ID gespeicherte Datum und gibt es
	 * als File - Objekt zurück
	 *
	 * @param   id
	 * @return das File-Objekt
	 * @exception  DataStoreException
	 */
	protected abstract File loadDataAsFile(String id)
		throws DataStoreException;

	/**
	 * Lädt das unter ID gespeicherte Datum und gibt es als
	 * InputStream zurück
	 *
	 * @param   id
	 * @return den InputStream
	 * @exception  DataStoreException
	 */
	protected abstract InputStream loadDataAsInputStream(String id)
		throws DataStoreException;

	/**
	 * Lädt das Byte-Array einer gegebenen ID
	 *
	 * @param   id
	 * @return ein Byte-Array
	 * @exception   DataStoreException
	 */
	protected abstract byte[] loadData(String id) throws DataStoreException;

	
	public boolean isBackupTime() {

		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis( System.currentTimeMillis());
		
		int h = calendar.get( Calendar.HOUR_OF_DAY );
		int seconds = h*3600
						+calendar.get( Calendar.MINUTE )*60
						+calendar.get( Calendar.SECOND );
		
		int backupStart = this.getBackupStart();
		int backupEnd = this.getBackupEnd();
		
		if(!isValidBackupInterval(backupStart, backupEnd)){
		    VKCLogManager.errorLog(this, "", "Invalid interval for backup: " +backupStart+ "-" + backupEnd);
		    VKCLogManager.errorLog(this, "", "Hours must be in range 0-23 and not equal!");
		    VKCLogManager.infoLog(this, "Returning isBackupTime() --> false");
		    return false;
		}
		
		if( backupStart < backupEnd ){
			if( seconds >= backupStart*3600 && seconds<=backupEnd*3600 )
				return true;
		}else{
			int shiftU = 24-backupStart;
			int newSeconds = ((h+shiftU)%24)*3600+calendar.get( Calendar.MINUTE )*60
									+calendar.get( Calendar.SECOND );
			int newBackupEnd = (backupEnd+shiftU)%24;
			if( newSeconds>= 0 && newSeconds<=newBackupEnd*3600 )
				return true;
			
		}
		return false;
	}
	
	/**
	 * Löscht die unter id gespeicherten Daten
	 *
	 * @param   id
	 * @exception   DataStoreException
	 */
	public void delete(String id) throws DataStoreException {
		
		if( isBackupTime() ){
			VKCLogManager.infoLog( this, "Löschoperation nicht durchgeführt. It's backup time!");
			return;
		}
			
			
		rwLock.forWriting(id);

		try {
			deleteData(id);
		} finally {
			rwLock.releaseWriting(id);
		}

	}

	/**
	 * Löscht die unter id gespeicherten Daten
	 *
	 * @param   id
	 * @exception   DataStoreException
	 */
	protected abstract void deleteData(String id) throws DataStoreException;
	

	/**
	 * Überprüft, ob das Interval gültig ist
	 * @return true, falls start und end zwischen 0 und 23 liegen
	 * und nicht gleich sind. sonst false
	 */
	protected boolean isValidBackupInterval(int backupStart, int backupEnd) {
		
		if( !isHourOfDay( backupStart ) )
			return false;
			
		if( !isHourOfDay( backupEnd ) )
			return false;
					
		if( backupStart == backupEnd )
			return false;
			
		return true;
	}
	
	
	/**
	 * Überprüft, ob die übergebene Zahl
	 * im Interval 0-23 liegt
	 * 
	 * @param hour
	 * @return
	 */
	protected boolean isHourOfDay( int hour ){
		return (hour >=0 && hour <24);
	}

	/** 
	 * (non-Javadoc)
	 * @see vkc.server.archive.DataStore#getBackupEnd()
	 */
    public int getBackupEnd() {
        int defaultBackupEndHour = 3;
        
        try {
            return VKCSysParameterSerializer.retrieveIntValue( "ARCHIVE", "EndBackupHour", defaultBackupEndHour); 
        } catch (DBException e) {
            VKCLogManager.errorLog(this, "", "Error while retrieving sysparameter: [ARCHIVE] EndBackupHour", e);
            VKCLogManager.infoLog(this, "Returning default backuptEndHour = " + defaultBackupEndHour);
            return defaultBackupEndHour;
        }
    }
    
    /** 
     * (non-Javadoc)
     * @see vkc.server.archive.DataStore#getBackupStart()
     */
    public int getBackupStart(){
        int defaultBackupStartHour = 0;
        
        try {
            return VKCSysParameterSerializer.retrieveIntValue( "ARCHIVE", "StartBackupHour", defaultBackupStartHour);
        } catch (DBException e) {
            VKCLogManager.errorLog(this, "", "Error while retrieving sysparameter: [ARCHIVE] StartBackupHour", e);
            VKCLogManager.infoLog(this, "Returning default backuptStartHour = " + defaultBackupStartHour);
            return defaultBackupStartHour;
        }
    }
}
