package sos.stacks.hobbit;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import sos.spooler.Job_impl;
import sos.spooler.Order;
import sos.spooler.Variable_set;

import sos.util.SOSDate;
import sos.xml.SOSXMLXPath;

/**
 @author andreas.pueschel@sos-berlin.com
 *
 
 * see job documentation in the package jobdoc for details
 */
public class JobSchedulerHobbitMessageJob extends Job_impl {

    /** optional name for this job, defaults to scheduler job name */
    protected String hobbitJob = "JobSchedulerHobbitMessageJob";

    /** required URL of the hobbit server to which this job is reporting */
    protected String hobbitUrl = "";

    /** optional name or IP address of the host for which this job is reporting, default: job scheduler host */
    protected String hobbitHost = "";

    /** optional name of the test in the hobbit server console, default: scheduler */
    protected String hobbitTest = "";
    
    /** optional message type: currently "reset", "status" and "notify" are supported, default: status */
    protected String messageType = "";
    
    /** required message color: green, yellow, red */
    protected String messageColor = "";
    
    /** optional lifetime of a message */
    protected String messageLifetime = "";
    
    /** required message content */
    protected String messageContent = "";
    
    /** persistent messages are repeatedly processed */
    protected boolean isMessagePersistent = true;
    
    protected Vector messageVector      = new Vector();
    protected Iterator messageIterator  = null;
    protected Vector messageVariables  = new Vector();
    

    /**
     * initialize message items
     */
    
    protected void initMessageItems() {
        
        this.setHobbitUrl("http://" + spooler.hostname() "/hobbit-cgi/bbmessage.sh");
        this.setHobbitHost(spooler.hostname());
        this.setHobbitTest("scheduler");
        this.setMessageType("status");
        this.setMessageLifetime("");
        this.setMessageColor("green");
        this.setMessageContent("");
    }

    
    /**
     * move message items to hashmap
     */
    
    protected HashMap putMessageItem(String url, String host, String test, String type, String lifetime, String color, String content) {
        
        HashMap message = new HashMap();
        
        message.put("url", url);
        message.put("host", host);
        message.put("test", test);
        message.put("type", type);
        message.put("lifetime", lifetime);
        message.put("color", color);
        message.put("content", content);

        return message;
    }
    
    
    /**
     * move message items from hashmap to classs variables
     */
    
    protected void getMessageItem(HashMap message) {
        
        if (message.containsKey("url"&& message.get("url"!= nullthis.setHobbitUrl(message.get("url").toString());
        if (message.containsKey("host"&& message.get("host"!= nullthis.setHobbitHost(message.get("host").toString());
        if (message.containsKey("test"&& message.get("test"!= nullthis.setHobbitTest(message.get("test").toString());
        if (message.containsKey("type")&& message.get("type"!= nullthis.setMessageType(message.get("type").toString());
        if (message.containsKey("lifetime")&& message.get("lifetime"!= nullthis.setMessageLifetime(message.get("lifetime").toString());
        if (message.containsKey("color")&& message.get("color"!= nullthis.setMessageColor(message.get("color").toString());
        if (message.containsKey("content")&& message.get("content"!= nullthis.setMessageContent(message.get("content").toString());
    }
    

    /**
     * serialize message items to String
     */
    
    protected String serializeMessageItem(String id, String url, String host, String test, String type, String lifetime, String color, String content) {
        
        StringBuffer message = new StringBuffer();
        
        message.append("<message id=\"" + id + "\">");
          message.append("<url>" this.normalizeItem(url"</url>");
          message.append("<host>" this.normalizeItem(host"</host>");
          message.append("<test>" this.normalizeItem(test"</test>");
          message.append("<type>" this.normalizeItem(type"</type>");
          message.append("<lifetime>" this.normalizeItem(lifetime"</lifetime>");
          message.append("<color>" this.normalizeItem(color"</color>");
          message.append("<content>" this.normalizeItem(content"</content>");
        message.append("</message>");

        return message.toString();
    }
    
    
    /**
     * normalize message item
     */
    
    protected String normalizeItem(String item) {
        
        item = item.replaceAll("&""&amp;");
        item = item.replaceAll("<""&lt;");
        item = item.replaceAll(">""&gt;");

        return item;
    }

    
    /**
     * get job parameters from job configuration (scheduler.xml)
     */
    
    protected void getJobParameters() throws Exception {
    
        try // to fetch default job parameters
            if (spooler_task.params().var("job"!= null && spooler_task.params().var("job").length() 0) {
                this.setHobbitJob(spooler_task.params().var("job"));
                spooler_log.debug1(".. job parameter [job]: " this.getHobbitJob());
            else if (this.getHobbitJob() == null || this.getHobbitJob().length() == 0){
                this.setHobbitJob(spooler_job.name());
            }
            
            if (spooler_task.params().var("url"!= null && spooler_task.params().var("url").length() 0) {
                this.setHobbitUrl(spooler_task.params().var("url"));
                spooler_log.debug1(".. job parameter [url]: " this.getHobbitUrl());
            }
            
            if (spooler_task.params().var("host"!= null && spooler_task.params().var("host").length() 0) {
                this.setHobbitHost(spooler_task.params().var("host"));
                spooler_log.debug1(".. job parameter [host]: " this.getHobbitHost());
            }
            
            if (spooler_task.params().var("test"!= null && spooler_task.params().var("test").length() 0) {
                this.setHobbitTest(spooler_task.params().var("test"));
                spooler_log.debug1(".. job parameter [test]: " this.getHobbitTest());
            }

            if (spooler_task.params().var("type"!= null && spooler_task.params().var("type").length() 0) {
                this.setMessageType(spooler_task.params().var("type"));
                spooler_log.debug1(".. job parameter [type]: " this.getMessageType());
            }

            if (spooler_task.params().var("lifetime"!= null && spooler_task.params().var("lifetime").length() 0) {
                this.setMessageLifetime(spooler_task.params().var("lifeteime"));
                spooler_log.debug1(".. job parameter [lifetime]: " this.getMessageLifetime());
            }

            if (spooler_task.params().var("persistent"!= null && spooler_task.params().var("persistent").length() 0) {
                if (spooler_task.params().var("persistent").equals("0"
                    || spooler_task.params().var("persistent").equalsIgnoreCase("false")
                    || spooler_task.params().var("persistent").equalsIgnoreCase("no")) {
                    this.setMessagePersistent(false);
                    spooler_log.debug1(".. job parameter [persistent]: " this.isMessagePersistent());
                else {
                    this.setMessagePersistent(true);
                }
            }
        catch (Exception e) {
            throw new Exception("error occurred processing job parameters: " + e.getMessage());
        }
    }                            
    
    
    /**
     * check scheduler log file for warnings and errors
     */
    
    public boolean spooler_open() {
        
        long logFilepointer = 0;
        String logFilename = "";
        String logLine = "";
        File logFile = null;
        File prevLogFile = null;
        RandomAccessFile checkFile = null;
        RandomAccessFile checkPrevFile = null;
        
        try {
            this.messageVector = new Vector();
            this.initMessageItems();
            this.getJobParameters();

            if (this.getMessageType().equalsIgnoreCase("reset")) {
                spooler.set_var(this.getHobbitJob() ".messages""");
                spooler_log.info("message stack has been reset");
                this.messageVector.add(this.putMessageItem(this.getHobbitUrl()this.getHobbitHost()this.getHobbitTest()
                    "status"this.getMessageLifetime()"green", SOSDate.getCurrentTimeAsString() ": warnings and errors were reset"));
            }
            
            try // to retrieve persistent messages from scheduler variables
                if (this.isMessagePersistent() ) {
                    String messageSet = spooler.var(this.getHobbitJob() ".messages")
                    if (messageSet != null && messageSet.length() 0) { 
                        messageSet = messageSet.replaceAll(String.valueOf((char)254)"<").replaceAll(String.valueOf((char)255)">");
                        SOSXMLXPath xpath = new SOSXMLXPath(new StringBuffer(messageSet));
                        NodeList nodelist = xpath.selectNodeList("//messages/*");
                        for (int i=0; i < nodelist.getLength(); i++) {
                            Node node = nodelist.item(i);
                            if (node == nullcontinue;
                            NodeList itemlist = xpath.selectNodeList(node, "*");
                            String curUrl = this.getHobbitUrl();
                            String curHost = this.getHobbitHost();
                            String curTest = this.getHobbitTest();
                            String curType = this.getMessageType();
                            String curLifetime = this.getMessageLifetime();
                            String curColor = this.getMessageColor();
                            String curContent = "";
                            for (int j=0; j<itemlist.getLength(); j++) {
                                Node item = itemlist.item(j);
                                if (item == nullcontinue;
                                if (item.getNodeName().equals("url"&& item.getFirstChild() != null) {
                                    curUrl = item.getFirstChild().getNodeValue();
                                else if (item.getNodeName().equals("host"&& item.getFirstChild() != null) {
                                    curHost = item.getFirstChild().getNodeValue();
                                else if (item.getNodeName().equals("test"&& item.getFirstChild() != null) {
                                    curTest = item.getFirstChild().getNodeValue();
                                else if (item.getNodeName().equals("type"&& item.getFirstChild() != null) {
                                    curType = item.getFirstChild().getNodeValue();
                                else if (item.getNodeName().equals("lifetime"&& item.getFirstChild() != null) {
                                    curLifetime = item.getFirstChild().getNodeValue();
                                else if (item.getNodeName().equals("color"&& item.getFirstChild() != null) {
                                    curColor = item.getFirstChild().getNodeValue();
                                else if (item.getNodeName().equals("content"&& item.getFirstChild() != null) {
                                    curContent = item.getFirstChild().getNodeValue();
                                }
                            }
                            this.messageVector.add(this.putMessageItem(curUrl, curHost, curTest, 
                                    curType, curLifetime, curColor, curContent));
                        }
                   }
                }
            catch (Exception e) { // ignore all errors when processing persistent messages
                spooler_log.info("persistent messages processed with errors: " + e.getMessage());
            }

            
            try // to process possibly partially checked previous log files
                String logFilepointerVariable = spooler.var(this.getHobbitJob() ".filepointer");
                String logFilenameVariable    = spooler.var(this.getHobbitJob() ".filename");

                if (logFilepointerVariable != null && logFilepointerVariable.length() 0logFilepointer = Long.parseLong(logFilepointerVariable);
                if (logFilenameVariable != null && logFilenameVariable.length() 0logFilename = logFilenameVariable;

                // on previous execution of this job a different log file may have been partially checked
                if (logFilename != null && logFilename.length() && !logFilename.equals(new File(spooler.log().filename()).getCanonicalPath() )) {
                    prevLogFile = new File(logFilename);
                    if (!prevLogFile.exists()) {
                        spooler_log.info("log file from previous job execution not found: " + prevLogFile.getCanonicalPath() );
                    }

                    checkPrevFile = new RandomAccessFile(prevLogFile, "r");
                    if (logFilepointer > 0) {
                        checkPrevFile.seek(logFilepointer);
                        spooler_log.debug3("starting to check previous log file [" + prevLogFile.getCanonicalPath() "] from position: " + logFilepointer);
                    }
                    
                    while((logLine = checkPrevFile.readLine()) != null) {
                        if (logLine.indexOf("[WARN]"!= -1) {
                            this.messageVector.add(this.putMessageItem(this.getHobbitUrl()this.getHobbitHost()this.getHobbitTest()
                                    this.getMessageType()this.getMessageLifetime()"yellow", logLine));
                        else if (logLine.indexOf("[ERROR]"!= -1) {
                            this.messageVector.add(this.putMessageItem(this.getHobbitUrl()this.getHobbitHost()this.getHobbitTest()
                                    this.getMessageType()this.getMessageLifetime()"red", logLine));
                        }
                    }

                    spooler_log.debug3("previous log file [" + prevLogFile.getCanonicalPath() "] processed");
                    logFilepointer = 0;
                    logFilename = "";
                }
            catch (Exception e) { // ignore all errors when processing previous log files (log files may have been gzipped or removed)
                spooler_log.info("previous log file [" ((prevLogFile != null? prevLogFile.getCanonicalPath() """] processed with errors: " + e.getMessage());
                logFilepointer = 0;
                logFilename = "";
            finally {
                if (checkPrevFile != null try checkPrevFile.close()catch (Exception ex) {} // gracefully ignore this error
            }

            
            try {
                logFile = new File(spooler.log().filename());
                if (!logFile.exists()) {
                    throw new Exception("log file not found: " + logFile.getCanonicalPath() );
                }

                checkFile = new RandomAccessFile(logFile, "r");
                if (logFilepointer > 0) {
                    checkFile.seek(logFilepointer);
                    spooler_log.debug3("starting to check current log file [" + logFile.getCanonicalPath() "] from position: " + logFilepointer);
                }
            
                while((logLine = checkFile.readLine()) != null) {
                    if (logLine.matches"^\\d{4}-\\d{1,2}-\\d{1,2} +\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d{1,3})? +\\[WARN\\] +.*" )) {
                        this.messageVector.add(this.putMessageItem(this.getHobbitUrl()this.getHobbitHost()this.getHobbitTest()
                                this.getMessageType()this.getMessageLifetime()"yellow", logLine));
                    else if (logLine.matches"^\\d{4}-\\d{1,2}-\\d{1,2} +\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d{1,3})? +\\[ERROR\\] +.*" )) {
                        this.messageVector.add(this.putMessageItem(this.getHobbitUrl()this.getHobbitHost()this.getHobbitTest()
                                this.getMessageType()this.getMessageLifetime()"red", logLine));
                    }
                }

                spooler.set_var(this.getHobbitJob() ".filename",    logFile.getCanonicalPath());
                spooler.set_var(this.getHobbitJob() ".filepointer", Long.toString(checkFile.getFilePointer()));
                spooler_log.debug3("current log file [" + logFile.getCanonicalPath() "] processed to position: " + Long.toString(checkFile.getFilePointer()));

                // send a green message if no errors and warnings are to be reported
                if (this.messageVector.isEmpty()) {
                    this.messageVector.add(this.putMessageItem(this.getHobbitUrl()this.getHobbitHost()this.getHobbitTest()
                        "status"this.getMessageLifetime()"green", SOSDate.getCurrentTimeAsString() ": job scheduler is up and running"));
                }
                
                this.messageIterator = this.messageVector.iterator();
                return this.messageIterator.hasNext();

            catch (Exception e) {
                throw new Exception(e);
            finally {
                if (checkFile != null try checkFile.close()catch (Exception ex) {} // gracefully ignore this error
            }
            
        catch (Exception e){
            spooler_log.warn("failed to check messages in log file [" + spooler.log().filename() "]: " + e.getMessage());
            return false;
        }
    }
    
    
    /**
     * process message from scheduler log file
     */
    
    public boolean spooler_process() {

        Order order = null;
        Variable_set orderData = null;
        int messageCounter = 0;
        boolean rc = true;

        try {
            this.initMessageItems();
            this.getJobParameters();

            try // to fetch parameters from orders that have precedence to job parameters
                if (spooler_task.job().order_queue() != null) {
                    order = spooler_task.order();

                    if (spooler_task.order().payload() == null
                        throw new Exception("no payload was given for this order: " + order.id());
                    orderData = (Variable_setspooler_task.order().payload();
                    
                    if orderData.var("url"!= null && orderData.var("url").toString().length() 0) {
                        this.setHobbitUrl(orderData.var("url").toString());
                        spooler_log.debug1(".. order parameter [url]: " this.getHobbitUrl());
                    }
                    
                    if orderData.var("host"!= null && orderData.var("host").toString().length() 0) {
                        this.setHobbitHost(orderData.var("host").toString());
                        spooler_log.debug1(".. order parameter [host]: " this.getHobbitHost());
                    }
                    
                    if orderData.var("test"!= null && orderData.var("test").toString().length() 0) {
                        this.setHobbitTest(orderData.var("test").toString());
                        spooler_log.debug1(".. order parameter [test]: " this.getHobbitTest());
                    }

                    if orderData.var("type"!= null && orderData.var("type").toString().length() 0) {
                        this.setMessageType(orderData.var("type").toString());
                        spooler_log.debug1(".. order parameter [type]: " this.getMessageType());
                    }
                    
                    if orderData.var("lifetime&