/////////////////////////////////////////////////////////////////////////
//
// © 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 :		Nikolay Terziev
//	Created Date :		2004/11/05
//	Created for Project:	GEMSS
//
////////////////////////////////////////////////////////////////////////
//
// Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//	Last commit info:	$Author: niki $
//					$Date: 2004/11/25 15:31:03 $
//					$Revision: 1.1 $
//
/////////////////////////////////////////////////////////////////////////

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


import java.io.*;
import java.rmi.RemoteException;
import java.text.SimpleDateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.ArrayList;


public class MauiUtil {

    private static Logger logger = null;
    private static String nodeName = null;

    private ArrayList listOut;
    private ArrayList listErr;
    private SimpleDateFormat dateFormat;
    public MauiUtil() {

        logger = Logger.getLogger("at.ac.univie.iss.crm.maui.util.MauiUtil");

        listOut = new ArrayList();
        listErr = new ArrayList();
        dateFormat = new SimpleDateFormat();
        dateFormat.applyPattern("HH:mm:ss_MM/dd/yy");
    }

    public String getTempReservation(int nodes, Date start, Date stop) {

        boolean  status;
        Iterator iterator;
        String   strExec;
        String resID = null;
        boolean OK = false;

        strExec = "setres -u gemss -s " + dateFormat.format(start) + " -e " + dateFormat.format(stop) + " TASKS=="
                + nodes;
   
        logger.log(Level.FINE, " getTempReservation():Exec " + strExec);

        status = exeCommand(strExec);
        if (status) {
            iterator = listOut.iterator();

            if (iterator.hasNext()) {

                String output = ((String) iterator.next()).trim();

                logger.log(Level.FINE, " getTempReservation(): " + output);
                if (output.compareTo("reservation created") == 0) {


                    while (iterator.hasNext()) {

                        output = ((String) iterator.next()).trim();
                   
                        if (output.startsWith("reservation")) {

                            String[] splited = output.split("'");

                            return splited[1];
                         
                        }

                        logger.log(Level.FINE, " getTempReservation(): " + output);

                    }
                }
            }
            logger.log(Level.WARNING, " getTempReservation(): Reservation not Created");

        } else {

            iterator = listErr.iterator();
            while (iterator.hasNext()) {

                logger.log(Level.SEVERE, (String) iterator.next());
            }
        }

        return resID;
    }

    public boolean cancelReservation(String resID) {

        boolean  status = false;
        Iterator iterator;
        String   strExec;

        strExec = "releaseres " + resID;

        logger.log(Level.FINE, "cancelReservation():Exec " + strExec);

        status = exeCommand(strExec);
        if (status) {
            iterator = listOut.iterator();
            String output;

            while (iterator.hasNext()) {

                output = ((String) iterator.next()).trim();

                logger.log(Level.FINE, " cancelReservation(): " + output);

                if (output.startsWith("released User reservation")) {

                    return true;
                }
            }
            logger.log(Level.WARNING, " cancelReservation(): Reservation not released");

        } else {

            iterator = listErr.iterator();
            while (iterator.hasNext()) {
                logger.log(Level.SEVERE, (String) iterator.next());
            }
        }

        return status;
    }

    public long startQsub(int nodes, long minutes, String script, String resID) {
        long qsubID = -1;
        int count = 0;
        boolean status;
        Iterator iterator;
        String strExec;

        script = script + " " + nodes;

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

        String digit = (new Integer(Integer.MAX_VALUE)).toString();
        String newScriptLocation = chageScript(script);

        if (newScriptLocation != null) {

            String minsStr;
            String hoursStr;
            long mins  = (minutes % 60);
            long hours = (minutes / 60);

            if ( mins < 10)
               minsStr = "0" + mins;
             else
               minsStr = (new Long(mins)).toString();

            if ( hours < 10)
               hoursStr = "0" + hours;
            else
               hoursStr = (new Long(hours)).toString();

            String walltime = hoursStr + ":" + minsStr + ":00";


            if (resID != null) {
             //   strExec = "qsub -l nodes=" + nodes + ",walltime=" + walltime + " -W x=FLAGS:ADVRES:" + resID + " " + newScriptLocation;
               strExec = "qsub -l nodes=" + nodes + " -W x=FLAGS:ADVRES:" + resID + " " + newScriptLocation;
            } else {
             
                strExec = "qsub -l nodes=" + nodes + ",walltime=" + walltime + " " + newScriptLocation;
            //   strExec = "qsub -l nodes=" + nodes + " " + newScriptLocation;
            }

            if (splited.length >= 2) {

                  strExec = "qsub -e " + splited[1] + " -o " + splited[1] + " -l nodes=" + nodes + ",walltime=" + walltime  + " " + newScriptLocation;
              //  strExec = "qsub -e " + splited[1] + " -o " + splited[1] + " -l nodes=" + nodes + " " + newScriptLocation;

                if (resID != null) {
                   strExec = "qsub -e " + splited[1] + " -o " + splited[1] + " -l nodes=" + nodes + ",walltime=" + walltime +
                              " -W x=FLAGS:ADVRES:" + resID + ";NACCESSPOLICY:SINGLEJOB " + newScriptLocation;
                //   strExec = "qsub -e " + splited[1] + " -o " + splited[1] + " -l nodes=" + nodes +
                //              " -W x=FLAGS:ADVRES:" + resID + " " + newScriptLocation;
                }

            }

            logger.log(Level.INFO, "startQsub(): " + strExec);

            status = exeCommand(strExec);
            if (status) {
                iterator = listOut.iterator();

                if (iterator.hasNext()) {

                    char[] cchar = ((String) iterator.next()).toCharArray();

                    for (int i = 0; i < cchar.length; i = i + 1) {
                        if (Character.isDigit(cchar[i])) {
                            count++;
                        } else {
                            break;
                        }
                    }

                    digit = new String(cchar, 0, count);

                    return Integer.parseInt(digit);
                }

            } else {

                iterator = listErr.iterator();
                while (iterator.hasNext()) {
                    logger.log(Level.SEVERE, (String) iterator.next());
                }
            }
        }
        return qsubID;
    }

    public int getQsubStatus(long qsubJobId, String workingDir) {
        int status = 7; // UNKNOWN
        boolean OK;
        Iterator iterator;

        String strExec = "qstat " + qsubJobId;

        logger.log(Level.INFO, "Requesting status for qsubJobID : " + qsubJobId);

        if (qsubJobId == -1) {

            logger.log(Level.WARNING, "The job is not started!");

            return 7; // UNKNOWN
        }

        OK = exeCommand(strExec);

        if (OK) {
            iterator = listOut.iterator();

            if (iterator.hasNext()) {
                iterator.next();

                if (iterator.hasNext()) {
                    iterator.next();
                }

                if (iterator.hasNext()) {

                    String[] splited = ((String) iterator.next()).split(" ");

                    if (splited.length >= 2) {
                        switch (splited[splited.length - 2].charAt(0)) {

                        case 'Q': {
                            status = 0; // QUEUED
                            break;
                        }

                        case 'W': {
                            status = 2; // WAITING
                            break;
                        }

                        case 'H': {
                            status = 2; // WAITING
                            break;
                        }

                        case 'R': {
                            status = 4; // RUNNING
                            break;
                        }

                        case 'E': {
                            status = 4; // RUNNING
                            break;
                        }
                        }
                    }

                }

            }

        } else {

            status = 6; // "Released"

            iterator = listErr.iterator();

            while (iterator.hasNext()) {

                String log = ((String) iterator.next());

                logger.log(Level.WARNING, log);
            }

            if (workingDir == null) {

                status = 7; // "UNKNOWN"
               
                logger.log(Level.WARNING, "Can not determine the status of " + qsubJobId + ".gsrfs.ER file!");
            } else {
                try {

                    File errorFile = new File(workingDir + qsubJobId + ".gsrfs.ER");

                    if (errorFile.length() > 0) {
                        status = 8; // "ERROR"

                        logger.log(Level.WARNING,
                                " There is an ERROR by executing the script.Please see the messages in " + workingDir
                                + qsubJobId + ".gsrfs.ER file!");

                    }
                } catch (SecurityException se) {
                    logger.log(Level.SEVERE, "", se);

                } catch (Exception  e) {
                    logger.log(Level.SEVERE, "", e);

                }

            }

        }
        return status;
    }

    private boolean exeCommand(String s) {

        try {
            Process process = Runtime.getRuntime().exec(s);

            process.waitFor();
            java.io.InputStream inputstream = process.getInputStream();
            BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(inputstream));
            java.io.InputStream inputstream1 = process.getErrorStream();
            BufferedReader bufferedreader1 = new BufferedReader(new InputStreamReader(inputstream1));
            String s1;

            while ((s1 = bufferedreader.readLine()) != null) {
                listOut.add(s1);
            }
            while ((s1 = bufferedreader1.readLine()) != null) {
                listErr.add(s1);
            }
            bufferedreader.close();
            bufferedreader1.close();
            process.destroy();

        } catch (IOException e) {
            logger.log(Level.SEVERE, "MAUIUtil.exeCommand(): " + s, e);
            // throw new RemoteException("",e);
        } catch (NullPointerException e) {
            logger.log(Level.SEVERE, "MAUIUtil.exeCommand(): " + s, e);
            // throw new RemoteException("MAUIUtil.exeCommand(): " + cmd, e);
        } catch (IllegalArgumentException e) {
            logger.log(Level.SEVERE, "MAUIUtil.exeCommand(): " + s, e);
            // throw new RemoteException("MAUIUtil.exeCommand(): " + cmd, e);
        } catch (IndexOutOfBoundsException e) {
            logger.log(Level.SEVERE, "MAUIUtil.exeCommand(): " + s, e);
            // throw new RemoteException("MAUIUtil.exeCommand(): " + cmd, e);
        } catch (Exception exception) {
            logger.log(Level.SEVERE, "MAUIUtil.exeCommand(): " + s, exception);
        }
        return listErr.size() == 0;
    }

    private String chageScript(String script) {

        FileWriter fw = null;
        FileReader fr = null;
        BufferedReader br = null;
        BufferedWriter bw = null;
        File source = null;

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

        try {

            if (splited.length >= 2) {

                if (splited[1].endsWith("/")) {
                    result = splited[1] + "newQsubScript";
                } else {
                    result = splited[1] + "/newQsubScript";
                }
            } else {
                result = "/tmp/newQsubScript";
            }

            fr = new FileReader(splited[0]);
            fw = new FileWriter(result);
            br = new BufferedReader(fr);
            bw = new BufferedWriter(fw);

            source = new File(splited[0]);

            int fileLength = (int) source.length();

            char charBuff[] = new char[fileLength];

            while (br.read(charBuff, 0, fileLength) != -1) {

                String strReplaced = new String(charBuff);

                // System.out.println("------input : " + splited.length);

                if (splited.length >= 6) {
                    strReplaced = strReplaced.replaceAll("\\$1", splited[1]);
                    strReplaced = strReplaced.replaceAll("\\$2", splited[2]);
                    strReplaced = strReplaced.replaceAll("\\$3", splited[3]);
                    strReplaced = strReplaced.replaceAll("\\$4", splited[4]);
                    strReplaced = strReplaced.replaceAll("\\$5", splited[5]);
                } else {

                    if (splited.length >= 2) {
                        strReplaced = strReplaced.replaceAll("\\$1", splited[1]);
                    }
                    if (splited.length >= 3) {
                        strReplaced = strReplaced.replaceAll("\\$2", splited[2]);
                    }
                    if (splited.length >= 4) {
                        strReplaced = strReplaced.replaceAll("\\$3", splited[3]);
                    }
                    if (splited.length >= 5) {
                        strReplaced = strReplaced.replaceAll("\\$4", splited[4]);
                    }
                    if (splited.length >= 6) {
                        strReplaced = strReplaced.replaceAll("\\$5", splited[5]);
                    }
                }

                bw.write(strReplaced.toCharArray(), 0, strReplaced.length());
            }
        } catch (FileNotFoundException fnfe) {
            logger.log(Level.SEVERE, "", fnfe);
            return null;

        } catch (IOException ioe) {

            logger.log(Level.SEVERE, "Error reading/writing file!", ioe);
            return null;
        }
        finally {

            try {
                if (br != null) {
                    br.close();
                }

                if (bw != null) {
                    bw.close();
                }
            } catch (IOException ioe) {
                logger.log(Level.SEVERE, "", ioe);
            }
        }
        return result;
    }

}
