package hudson.plugins.performance;

import hudson.Extension;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;
import hudson.util.IOException2;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.kohsuke.stapler.DataBoundConstructor;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Parser for JMeter.
 *
 * @author Kohsuke Kawaguchi
 */
public class JMeterParser extends PerformanceReportParser {

  @Extension
  public static class DescriptorImpl extends PerformanceReportParserDescriptor {
    @Override
    public String getDisplayName() {
      return "JMeter";
    }
  }

  @DataBoundConstructor
  public JMeterParser(String glob) {
    super(glob);
  }

  @Override
  public String getDefaultGlobPattern() {
    return "**/*.jtl";
  }

  private PerformanceReport getPerfCache(File f) {
	  PerformanceReport pr = null;
	  File cf = new File(f.getName() + ".prep");

      if (cf.canRead()) {
        XMLDecoder decoder;
		try {
			decoder = new XMLDecoder(new FileInputStream(cf.getName()));
		} catch (FileNotFoundException e) {
			return (null);
		}
        try {
          pr = (PerformanceReport)decoder.readObject();
        } finally {
          decoder.close();
        }
     }

     return (pr);
  }

  private void savePerfCache(File f, PerformanceReport r ) {
	  File cf = new File(f.getName() + ".prep");
	  XMLEncoder encoder = null;
	try {
		encoder = new XMLEncoder(new FileOutputStream(cf.getName()));
        encoder.writeObject(r);
        encoder.flush();
	} catch (FileNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
     } finally {
    	 if (encoder != null)
           encoder.close();
      }
  }

  @Override
  public Collection<PerformanceReport> parse(AbstractBuild<?, ?> build,
      Collection<File> reports, TaskListener listener) throws IOException {
    List<PerformanceReport> result = new ArrayList<PerformanceReport>();

    SAXParserFactory factory = SAXParserFactory.newInstance();
    factory.setValidating(false);
    factory.setNamespaceAware(false);
    PrintStream logger = listener.getLogger();
    for (File f : reports) {

      try {
    	final PerformanceReport r = getPerfCache(f);

    	if (r == null) {

	        SAXParser parser = factory.newSAXParser();
	        r.setReportFileName(f.getName());
	        logger.println("Performance: Parsing JMeter report file " + f.getName());
	        parser.parse(f, new DefaultHandler() {
	          HttpSample currentSample;
	          int counter = 0;

	          /**
	           * Performance XML log format is in
	           * http://jakarta.apache.org
	           * /jmeter/usermanual/listeners.html
	           *
	           * There are two different tags which delimit jmeter
	           * samples: httpSample for http samples sample for non http
	           * samples
	           *
	           * There are also two different XML formats which we have to
	           * handle: v2.0 = "label", "timeStamp", "time", "success"
	           * v2.1 = "lb", "ts", "t", "s"
	           *
	           */
	          @Override
	          public void startElement(String uri, String localName, String qName,
	              Attributes attributes) throws SAXException {
	            if ("httpSample".equalsIgnoreCase(qName)
	                || "sample".equalsIgnoreCase(qName)) {
	              HttpSample sample = new HttpSample();
	              sample.setDate(new Date(
	                  Long.valueOf(attributes.getValue("ts") != null
	                      ? attributes.getValue("ts")
	                      : attributes.getValue("timeStamp"))));
	              sample.setDuration(Long.valueOf(attributes.getValue("t") != null
	                  ? attributes.getValue("t") : attributes.getValue("time")));
	              sample.setSuccessful(Boolean.valueOf(attributes.getValue("s") != null
	                  ? attributes.getValue("s") : attributes.getValue("success")));
	              sample.setUri(attributes.getValue("lb") != null
	                  ? attributes.getValue("lb") : attributes.getValue("label"));
	              if (counter == 0) {
	                currentSample = sample;
	              }
	              counter++;
	            }
	          }

	          @Override
	          public void endElement(String uri, String localName, String qName) {
	            if ("httpSample".equalsIgnoreCase(qName)
	                || "sample".equalsIgnoreCase(qName)) {
	              if (counter == 1) {
	                try {
	                  r.addSample(currentSample);
	                } catch (SAXException e) {
	                  e.printStackTrace();
	                }
	              }
	              counter--;
	            }
	          }

	        });

	        savePerfCache(f, r);
    	}
        result.add(r);
      } catch (ParserConfigurationException e) {
        throw new IOException2("Failed to create parser ", e);
      } catch (SAXException e) {
        logger.println("Performance: Failed to parse " + f + ": "
            + e.getMessage());
      }
    }
    return result;
  }
}