//
// © 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, Nikolay Terziev
//	Created Date :		2004/11/04
//	Created for Project:	GEMSS
//
////////////////////////////////////////////////////////////////////////
//
// Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//	Last commit info:	$Author: niki $
//					$Date: 2004/11/25 12:24:33 $
//					$Revision: 1.2 $
//
/////////////////////////////////////////////////////////////////////////

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


import at.ac.univie.iss.crm.ComputeResourceManager;
import at.ac.univie.iss.crm.JobInfo;

import java.io.*;
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 java.util.ArrayList;
import java.util.Random;
import at.ac.univie.iss.crm.maui.util.*;


// Referenced classes of package eu.gemss.crm:
// JobInfo

public class MauiCRMImpl
    implements ComputeResourceManager {

    private static Logger logger = null;

    public MauiCRMImpl() {
        logger = Logger.getLogger("at.ac.univie.iss.crm.maui.MauiCRMImpl");

    }

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

        Random random = new Random();
        String mauiResID = null;
        MauiUtil mauiUtil = new MauiUtil();
        long duration = 11;
        long diffTime;
        long interval;

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

        Date newStart = new Date(start.getTime() + 30000);

        if (stop.before(newStart)) {

            logger.log(Level.WARNING, " The time interval is not correct!");

            return -1;
        }

        diffTime = stop.getTime() - newStart.getTime();

        if (minutes > duration) {
            duration = minutes;
        }

        interval = duration * 60000 >= diffTime ? 1000 : duration * 6000;

        int nextInt;

        while (newStart.before(stop) && (mauiResID == null)) {

            nextInt = 4000; // random.nextInt(5000);

            if (nextInt > 3500) {


                if ( stop.before( new Date(newStart.getTime() + 60000*duration + 60000)) ) {

                  logger.log(Level.WARNING, "Duration is later than the stop time!");

                  return -1;
                }

                mauiResID = mauiUtil.getTempReservation(nodes, newStart, new Date(newStart.getTime() + 60000*duration + 60000));

                break;
            }

            newStart.setTime(newStart.getTime() + interval);
        }

        if ( (mauiResID == null) ) {

            logger.log(Level.WARNING, "There is no reservation made for this request!");

            return -1;
        }

        MAUIJobInfo jobInfo = new MAUIJobInfo();

        jobInfo.startTime = new Date(newStart.getTime());
        jobInfo.endTime = new Date(newStart.getTime() + duration * 60000);

        jobInfo.jobId = System.currentTimeMillis();
        jobInfo.qsubJobId = -1;
        jobInfo.MauiReservationID = mauiResID;
        jobInfo.user = "nobody";

        jobInfo.status = 7; // "UNKNOWN"

        jobInfo.confirm = -1;
        jobInfo.numNodes = nodes;

        jobInfo.expirationTime = validity;

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

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

        System.out.println(jobInfo);

        return jobInfo.jobId;

    }

    public boolean confirmReservation(long jobId) throws Exception {

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

        MAUIJobInfo jobInfo = MauiDB.dbHandling("get", jobId, null);

        if (jobInfo != null) {

            logger.log(Level.FINEST, "Given JobId found in CRM DB.\n");

            jobInfo.confirm = 1;
            jobInfo.status = 3;

            MauiDB.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");
            MauiDB.dbHandling("getInfo", jobId, null);
        }
        return false;
    }

    public boolean cancelReservation(long jobId) throws Exception {

        MauiUtil mauiUtil = new MauiUtil();

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

        MAUIJobInfo jobInfo = MauiDB.dbHandling("get", jobId, null);

        if (jobInfo != null) {

            if (mauiUtil.cancelReservation(jobInfo.MauiReservationID)) {

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

                return true;
            }
        }

        logger.log(Level.WARNING, "There is no reservation found in CRM DB for " + jobId);

        return false;
    }

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

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

        MAUIJobInfo jobInfo = MauiDB.dbHandling("get", jobId, null);

        if (jobInfo != null) {

            if (jobInfo.confirm == -1) {

                logger.log(Level.WARNING, "The reservation for this job is not confirmed. The Job can not be started!");

                return false;
            }

            if ((jobInfo.status == 0) || (jobInfo.status == 2) || (jobInfo.status == 4) ) { // "Running"

                logger.log(Level.WARNING, "The job is already running. It can not be started again!");

                return false;
            }

            jobInfo.status = 3; // Scheduled
            MauiDB.dbHandling("put", jobInfo.jobId, jobInfo);

            (new ExecuteBatchScriptThread(0, script, jobInfo)).start();

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

            return true;
        }

        logger.log(Level.WARNING, "The job does not exist in CRM DB. It can not be started!");

        return false;
    }

    public long makeSubmission(int nodes, int minutes, String script) throws Exception {

        MAUIJobInfo jobInfo = new MAUIJobInfo();
        long jid = 1;
        MauiUtil mauiUtil = new MauiUtil();

        jid = mauiUtil.startQsub(nodes, minutes, script, null);

        if (jid > 0) {

            jobInfo.jobId = jid;
            jobInfo.qsubJobId = jid;
            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;

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

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

            return jobInfo.jobId;
        }
        return -1;
    }

    public JobInfo getJobInfo(long jobId) throws Exception {

        MAUIJobInfo jobInfo = MauiDB.dbHandling("get", jobId, null);
        MauiUtil mauiUtil = new MauiUtil();

        if (jobInfo == null) {

            jobInfo = new MAUIJobInfo();

            jobInfo.jobId = -1;
            jobInfo.qsubJobId = -1;
            jobInfo.user = "nobody";
            jobInfo.status = 8; // "ERROR"
            jobInfo.confirm = -1;
            jobInfo.numNodes = -1;
            jobInfo.startTime = new Date(System.currentTimeMillis());

            jobInfo.endTime = new Date(System.currentTimeMillis());

            jobInfo.expirationTime = jobInfo.startTime;

            logger.log(Level.WARNING, "The job does not exist in CRM DB!");

            return jobInfo;
        }

        jobInfo.status = mauiUtil.getQsubStatus(jobInfo.qsubJobId, jobInfo.workingDir);

        return  jobInfo;
    }
}


class ExecuteBatchScriptThread extends Thread {

    private static Logger logger = null;

    private MauiUtil mauiUtil;

    private String script;
    private long sleep;
    private MAUIJobInfo jobInfo;
    String  workingDir = null;

    public ExecuteBatchScriptThread(long sleep, String script, MAUIJobInfo jobInfo) {

        super();

        logger = Logger.getLogger("at.ac.univie.iss.crm.maui.ExecuteBatchScriptThread");
        this.script = script;
        this.sleep = sleep;
        this.jobInfo = jobInfo;
        logger.log(Level.FINER, "ExecuteBatchScriptThread: " + sleep);
        mauiUtil = new MauiUtil();

        String[] splited = script.split(" ");

        if (splited.length >= 2) {
            workingDir = splited[1];

            if (!splited[1].endsWith("/")) {
                workingDir += "/";
            }
        }
    }

    public void run() {

     long minutes = ( jobInfo.endTime.getTime() - jobInfo.startTime.getTime()) / 60000;

            try {

                long qsubID = mauiUtil.startQsub(jobInfo.numNodes, minutes, script, jobInfo.MauiReservationID);

                if (qsubID != -1) {

                    jobInfo.qsubJobId = qsubID;
                    jobInfo.workingDir = workingDir;

                    MauiDB.dbHandling("put", jobInfo.jobId, jobInfo);
                }

            } catch (java.lang.InterruptedException ie) {

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

}

