/////////////////////////////////////////////////////////////////////////
//
// © Institute for Software Science, University of Vienna, 2004
//
// Copyright in this software belongs to Institute for Software Science, 
// University of Vienna, Nordbergstrasse 15/C/3, 1090 Vienna, Austria
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//	Created By :		Gerhard Engelbrecht
//	Created Date :		2004/05/20
//	Created for Project:	GEMSS
//
////////////////////////////////////////////////////////////////////////
//
// Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//	Last commit info:	$Author: gerry $
//					$Date: 2004/10/11 12:24:33 $
//					$Revision: 1.12 $
//
/////////////////////////////////////////////////////////////////////////

package at.ac.univie.iss.crm.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.rmi.RemoteException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;


import at.ac.univie.iss.crm.*;
import at.ac.univie.iss.service.global.ServiceInfo;

public class DummyComputeResourceManager implements ComputeResourceManager {

	private final static long MIN_TIME_TO_RESERVATION_IN_SECS = 240; // 2 min
    private final static SimpleDateFormat dateAndTimeFormatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss,S z"); 

	private static Logger logger = null;
	
	private Hashtable reservations;
	private Hashtable canceledReservations;

	public DummyComputeResourceManager() {
		
		logger = Logger.getLogger("logging.crm.test");
		reservations = new Hashtable();
		canceledReservations = new Hashtable();
	}
	
	private synchronized JobInfo dbHandling(String cmd, long jobId, JobInfo jobInfo) throws Exception {
		
		if (cmd.equals("put")) {
			
			reservations.remove(new Long(jobInfo.jobId));
			reservations.put(new Long(jobInfo.jobId), jobInfo);
			return null;
		}
		else if (cmd.equals("remove")) {
			
			JobInfo dbJobInfo = (JobInfo)reservations.remove(new Long(jobId));
			
			if (dbJobInfo != null) {
				canceledReservations.put(new Long(jobId), dbJobInfo);
			}
			return dbJobInfo;
			
		}
		else if (cmd.equals("get")) {
			
			return (JobInfo)reservations.get(new Long(jobId));
		}
		else if (cmd.equals("getInfo")) {
			
			StringBuffer logMsg = new StringBuffer();
			logMsg.append("CRM DB (reservations):\n");

			Iterator res = reservations.entrySet().iterator();
			
			while (res.hasNext()) {
				logMsg.append(res.next() + "\n");
			}
			
			logMsg.append("CRM DB (removed reservations):\n");

			Iterator cres = canceledReservations.entrySet().iterator();
			
			while (cres.hasNext()) {
				logMsg.append(cres.next() + "\n");
			}

			logger.log(Level.FINEST, logMsg.toString());
		}
		return null;
	}
	

	public long getTempReservation(int nodes,int minutes, Date start, Date stop, Date validity) throws Exception {

		logger.log(Level.FINE, "Started.\n");
		logger.log(Level.FINER, "Input: [Nodes: " + nodes + ", Start: " + start + ", Stop: " + stop + ", Validity: " + validity + "].\n");
		
		JobInfo jobInfo = new JobInfo();

		if (start.getTime() < (System.currentTimeMillis() + MIN_TIME_TO_RESERVATION_IN_SECS)) {
			
			jobInfo.startTime = new Date((System.currentTimeMillis()/1000 + MIN_TIME_TO_RESERVATION_IN_SECS)*1000);
			
			logger.log(Level.WARNING, "Auto-Corrected start time of reservation from " + dateAndTimeFormatter.format(start)
									+ " to " + dateAndTimeFormatter.format(jobInfo.startTime) 
									+ " (Current time is: " + dateAndTimeFormatter.format(new Date(System.currentTimeMillis())) + ")\n");
		}
		else {
			jobInfo.startTime = start;
		}

		if ((jobInfo.startTime.getTime() + minutes*60*1000) > (stop.getTime())) {

			logger.log(Level.WARNING, "Requested time (" + minutes + " mins) exceeds interval!\nBegin Interval: "
									+ dateAndTimeFormatter.format(jobInfo.startTime) + "\nEnd Interval: "
									+ dateAndTimeFormatter.format(jobInfo.startTime) + "\n");

			return -1L;
		}

		jobInfo.jobId = System.currentTimeMillis();
		jobInfo.user = "nobody";
		jobInfo.status = 3; //"scheduled"
		jobInfo.confirm = -1;
		jobInfo.numNodes = nodes;
		jobInfo.endTime = new Date(jobInfo.startTime.getTime() + minutes*60*1000);
		jobInfo.expirationTime = validity;

		this.dbHandling("put", jobInfo.jobId, jobInfo);

		logger.log(Level.FINER, "Returning: " + jobInfo + "\n");
		
		return jobInfo.jobId;
	}

	public boolean confirmReservation(long jobId) throws Exception {
	       
		logger.log(Level.FINER, "Started.\n");

		JobInfo jobInfo = this.dbHandling("get", jobId, null);
	       
		if ( jobInfo != null) {
			
			logger.log(Level.FINEST, "Given JobId found in CRM DB.\n");
	
			jobInfo.confirm = 1;
			jobInfo.status = 3;  
	        
	        this.dbHandling("put", jobInfo.jobId, jobInfo);
	        
			logger.log(Level.FINEST, "Confirmation of this job in CRM DB successful.\n");
		    return true;	
		}
		else {
			logger.log(Level.WARNING, "Given JobId not found in CRM DB.\n");
			this.dbHandling("getInfo", jobId, null);
		}
		return false;
	}
	
	public boolean cancelReservation(long jobId) throws Exception {
	     
		logger.log(Level.FINER, "Started.\n");

		this.dbHandling("remove", jobId, null);

		return true;
	}

	public boolean insertBatchScript(long jobId, String script) throws Exception { 

		logger.log(Level.FINER, "Started.\n");

		JobInfo jobInfo = this.dbHandling("get", jobId, null);
	       
		if (jobInfo != null) {
			
			if (jobInfo.status == 4) {
	                
				logger.log(Level.WARNING, "The job is already running. It can not be started again!");
	             
				return false;
			} 
			jobInfo.status = 3; // Scheduled
			this.dbHandling("put", jobInfo.jobId, jobInfo);
			   
			long currentTime = System.currentTimeMillis(); 
			long startTime =  jobInfo.startTime.getTime();  
			long diffTime =  startTime - currentTime; 
	         
			if (diffTime < 0) {

				diffTime = 0L;
			}

			(new ExecuteBatchScriptThread(diffTime, script + " " + jobInfo.numNodes, jobInfo)).start();

			logger.log(Level.FINER, "The Exceute-Batch-Script-Thread has been started!");

			return true;
        }

		logger.log(Level.WARNING, "The job does not exist. It can not be started!");
      
        return false;
	}

	public long makeSubmission(int nodes, int minutes, String script) throws Exception {
		
		JobInfo jobInfo = new JobInfo();

		jobInfo.jobId = System.currentTimeMillis();
		jobInfo.user = "nobody";
		jobInfo.status = 3; //"scheduled"
		jobInfo.confirm = -1;
		jobInfo.numNodes = nodes;
		jobInfo.startTime = new Date(System.currentTimeMillis());
		jobInfo.endTime = new Date(System.currentTimeMillis() + minutes*60*1000);
		jobInfo.expirationTime = jobInfo.startTime;

		this.dbHandling("put", jobInfo.jobId, jobInfo);

		(new ExecuteBatchScriptThread(0, script + " " + jobInfo.numNodes, jobInfo)).start();

		logger.log(Level.FINER, "Job sumission successful, return jobId: " + jobInfo.jobId + "\n");

		return jobInfo.jobId;
	}
		
	public JobInfo getJobInfo(long jobId) throws Exception {
			
		return this.dbHandling("get", jobId, null);
	}

/*
	public static void main(String args[]){
	     try{
	         DummyComputeResourceManager dummy = new DummyComputeResourceManager();
 
	         long id =    dummy.getTempReservation(1,4,new Date(System.currentTimeMillis()  +10000),new Date(System.currentTimeMillis() + 10000000) , new Date(System.currentTimeMillis() + 10000) );
	         dummy.confirmReservation(id);
	         dummy.insertBatchScript(id," path 1 "); 
        
	          id =    dummy.getTempReservation(1,4,new Date(System.currentTimeMillis()  +5000),new Date(System.currentTimeMillis() + 10000000) , new Date(System.currentTimeMillis() + 10000) );
	         dummy.confirmReservation(id); 
	         dummy.insertBatchScript(id," path 2  ");
	        dummy.insertBatchScript(id," path 2  ");
	    }catch (Exception e){
	        
	        System.out.println(" Exception " + e);
	    }
	 }
*/	
	
}

class ExecuteBatchScriptThread extends Thread {
 
	private static Logger logger = null;
	
	private String script;
	private long sleep;
	private JobInfo jobInfo;
	
    public ExecuteBatchScriptThread(long sleep, String script, JobInfo jobInfo) {
			
		super(); 

		logger = Logger.getLogger("logging.crm.test");
		this.script = script;
		this.sleep = sleep;
		this.jobInfo = jobInfo;
		logger.log(Level.FINER, "ExecuteBatchScriptThread: " + sleep);
    }

	public void run() { 

		if (sleep >= 0) {

			logger.log(Level.FINER, "The job will start in " + sleep + " milliseconds.\n");

			try {
				this.sleep(sleep);
			}
			catch (java.lang.InterruptedException ie) {

				logger.log(Level.WARNING, "Waiting for job has been interrupted!\n", ie);
			}

			try {
				Process process = this.executeCmd(script, true, false);
				
				jobInfo.status = 4; //"running"

				logger.log(Level.FINER, "The job is started (" + script + ")\n");
				
				process.waitFor();	        

				jobInfo.status = 6; //"Released"
				
			}
			catch (Exception exception) {

				jobInfo.status = 8; //"ERROR"
				logger.log(Level.SEVERE, "An error occured (see details below)!\n", exception);
			}
		}
		else {
			jobInfo.status = 8; //"ERROR"
			logger.log(Level.WARNING, "Interestingly sleep time is smaller than zero!\n");
		}
	}

	private Process executeCmd(String cmd, boolean output, boolean waitForTermination) throws RemoteException {
	
		Process process = null;
	
		try {	// try to execute given command line 
		
			process = Runtime.getRuntime().exec(cmd);
		} catch (IOException e) {
			logger.log(Level.SEVERE, "ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
			throw new RemoteException("",e);
		} catch (NullPointerException e) {
			logger.log(Level.SEVERE, "ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
			throw new RemoteException("ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
		} catch (IllegalArgumentException e) {
			logger.log(Level.SEVERE, "ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
			throw new RemoteException("ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
		} catch (IndexOutOfBoundsException e) {
			logger.log(Level.SEVERE, "ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
			throw new RemoteException("ExecuteBatchScriptThread.executeCmd(): " + cmd, e);
		}

		if (output) {	// if standard output & error should be logged get corresponding
						// streams and start an appropriate logging stream
			Output out = null;
			            
			try {
				out = new Output(process.getInputStream(), process.getErrorStream());
			} catch (RemoteException e){
	
				logger.log(Level.SEVERE, "",e);
				throw new RemoteException("",e);
			}   
			out.start();
		}
		
		return process;
	}
}

class Output extends Thread {
         
     private static Logger logger = null;
     
     private BufferedReader stdInput = null;
     private BufferedReader stdError = null;
     
     Output(InputStream inputStream, InputStream errorStream) throws RemoteException {
        
		try {
			logger = ServiceInfo.getLogger("scripts.job");
		}
		catch(Exception e) {
			if(logger == null) { e.printStackTrace(); }
			else { logger.log(Level.SEVERE, "",e); }

			throw new RemoteException("OutputThread()",e);
		}	
         
		stdInput = new BufferedReader(new InputStreamReader(inputStream));

		stdError = new BufferedReader(new InputStreamReader(errorStream));
     }

	public void run() {
       
		String s;
       
		try{
			while ((s = stdInput.readLine()) != null) {
				logger.log(Level.INFO, " - OUT: " + s + "\n");
			}
		}
		catch (IOException e){
			e.printStackTrace();
		}
	
		try {
			while ((s = stdError.readLine()) != null) {
				logger.log(Level.INFO, " - ERR: " + s + "\n");
			}
		}
		catch (IOException e){
			e.printStackTrace();
		}
	}
}


