/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.uima.ruta.testing.ui.handlers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.apache.uima.UIMAFramework;
import org.apache.uima.analysis_engine.AnalysisEngine;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.CASException;
import org.apache.uima.cas.FSIterator;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.impl.XmiCasDeserializer;
import org.apache.uima.cas.impl.XmiCasSerializer;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.cas.text.AnnotationIndex;
import org.apache.uima.jcas.tcas.Annotation;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.impl.ResourceManager_impl;
import org.apache.uima.resource.metadata.FsIndexDescription;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.ruta.addons.RutaAddonsPlugin;
import org.apache.uima.ruta.engine.RutaEngine;
import org.apache.uima.ruta.ide.core.builder.RutaProjectUtils;
import org.apache.uima.ruta.ide.launching.RutaLaunchConfigurationConstants;
import org.apache.uima.ruta.ide.launching.RutaLaunchConstants;
import org.apache.uima.ruta.testing.evaluator.ICasEvaluator;
import org.apache.uima.ruta.testing.preferences.TestingPreferenceConstants;
import org.apache.uima.ruta.testing.ui.views.TestCasData;
import org.apache.uima.ruta.testing.ui.views.TestPageBookView;
import org.apache.uima.ruta.testing.ui.views.TestViewPage;
import org.apache.uima.ruta.testing.ui.views.evalDataTable.TypeEvalData;
import org.apache.uima.ruta.testing.ui.views.util.EvalDataProcessor;
import org.apache.uima.ruta.type.EvalAnnotation;
import org.apache.uima.util.CasCreationUtils;
import org.apache.uima.util.FileUtils;
import org.apache.uima.util.InvalidXMLException;
import org.apache.uima.util.XMLInputSource;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.commands.IHandlerListener;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.handlers.HandlerUtil;
import org.xml.sax.SAXException;

public class RerunActionHandler implements IHandler {

  private class DebugJobChangeAdapter extends JobChangeAdapter {

    private TestViewPage page;

    DebugJobChangeAdapter(TestViewPage page) {
      super();
      this.page = page;
    }

    @Override
    public void done(IJobChangeEvent event) {
      if (event.getResult().isOK()) {
        page.getControl().getDisplay().asyncExec(new Runnable() {
          @Override
          public void run() {
            page.updateInfoPanel();
          }
        });
      }
    }
  }

  /**
   * Handler that runs the script to be tested and updates the GUI.
   */
  private class RerunHandlerJob extends Job {
    ExecutionEvent event;

    private final String viewCasName;

    private List<String> excludedTypes;

    private List<String> includedTypes;

    private boolean debug;

    RerunHandlerJob(ExecutionEvent event, String scriptName, String viewCasName,
            List<String> excludedTypes, List<String> includedTypes, boolean debug) {
      super("Testing " + scriptName + "...");
      this.event = event;
      this.viewCasName = viewCasName;
      this.excludedTypes = excludedTypes;
      this.includedTypes = includedTypes;
      this.debug = debug;
      setUser(true);
    }

    @Override
    public IStatus run(IProgressMonitor monitor) {

      // handle GUI
      final TestPageBookView testPageView = (TestPageBookView) HandlerUtil.getActivePart(event);
      final TestViewPage debugPage = (TestViewPage) testPageView.getCurrentPage();
      testPageView.showBusy(true);

      // init variables
      IResource r = debugPage.getResource();
      final IFile fScript = (IFile) r;
      final IProject project = r.getProject();
      IPath engineDescriptorPath = null;
      IPath typeSystemDescriptorPath = null;
      try {
        ClassLoader classLoader = RutaProjectUtils.getClassLoader(project);
        engineDescriptorPath = RutaProjectUtils.getAnalysisEngineDescriptorPath(r.getLocation(),
                project, classLoader);
        typeSystemDescriptorPath = RutaProjectUtils
                .getTypeSystemDescriptorPath(fScript.getLocation(), project, classLoader);
      } catch (CoreException e) {
        RutaAddonsPlugin.error(e);
      }
      // show message
      @SuppressWarnings({ "unchecked", "rawtypes" })
      ArrayList<TestCasData> testCasData = (ArrayList) debugPage.getViewer().getInput();
      int numFiles = testCasData.size();
      monitor.beginTask("Running evaluation, please wait", numFiles);

      // switch usage mode: old (only Ruta) vs. new (supports java/uimaFIT AEs)
      IPreferenceStore store = RutaAddonsPlugin.getDefault().getPreferenceStore();
      boolean javaSupportMode = store.getBoolean(TestingPreferenceConstants.EXTEND_CLASSPATH);
      if (!javaSupportMode) {
        // only Ruta mode (classpath NOT expanded)
        IStatus status = evalRutaOnlyScript(monitor, testPageView, debugPage, fScript, project,
                engineDescriptorPath, testCasData);
        if (status.getSeverity() == IStatus.ERROR) {
          Display current = Display.getCurrent();
          if (current != null) {
            Shell shell = current.getActiveShell();
            MessageDialog.openWarning(shell, "Error", status.getMessage());
          }
        }
        return status;
      } else {
        // * write clean run files into a temp directory, remember file names
        // TODO this approach (may) causes problems, when Java/uimaFIT engines have their own
        // typesystems...
        IStatus status = evalRutaWithClassPathScript(monitor, testPageView, debugPage, fScript,
                project, typeSystemDescriptorPath, testCasData);
        return status;
      }
    }

    private IStatus evalRutaWithClassPathScript(IProgressMonitor monitor,
            final TestPageBookView testPageView, final TestViewPage debugPage, final IFile fScript,
            final IProject project, IPath typeSystemDescriptorPath,
            ArrayList<TestCasData> testCasData) {
      final IPath cleanInputPath = project.getLocation()
              .append(RutaProjectUtils.getDefaultTestLocation())
              .append(RutaProjectUtils.getDefaultCleanTestLocation());
      clearFolder(project, cleanInputPath);
      writeCleanInputFiles(testCasData, project, typeSystemDescriptorPath, cleanInputPath, monitor);
      if (monitor.isCanceled()) {
        // TODO call monitor.done()
        return Status.CANCEL_STATUS;
      }

      // * apply script to the clean run files
      final IPath runTestPath = project.getLocation()
              .append(RutaProjectUtils.getDefaultTestLocation())
              .append(RutaProjectUtils.getDefaultTempTestLocation());
      clearFolder(project, runTestPath);
      runWithJVM(monitor, fScript, cleanInputPath, runTestPath);

      try {
        // * for each (goldFile, runFile)-pair:
        XMLInputSource in = new XMLInputSource(typeSystemDescriptorPath.toPortableString());
        TypeSystemDescription tsd = UIMAFramework.getXMLParser().parseTypeSystemDescription(in);
        ClassLoader classLoader = RutaProjectUtils.getClassLoader(project);
        ResourceManager resourceManager = new ResourceManager_impl(classLoader);
        tsd.resolveImports(resourceManager);
        CAS runCas = CasCreationUtils.createCas(tsd, null, null);
        CAS goldCas = CasCreationUtils.createCas(tsd, null, null);
        for (TestCasData td : testCasData) {
          // init etc
          runCas.reset();
          goldCas.reset();
          // deserialize CASes
          IPath path2RunFile = runTestPath.append(td.getPath().toFile().getName());
          String runFilePath = path2RunFile.toPortableString();
          File runFile = new File(runFilePath);
          deserializeCASs(goldCas, td, runCas, runFile);
          runCas = runCas.getView(viewCasName);
          goldCas = goldCas.getView(viewCasName);
          // ** create TP, FP, FN annotations
          // ** collect results and gather eval data
          evalLogicAndUpdateGUI(monitor, testPageView, debugPage, fScript, project, runCas, goldCas,
                  td);
          if (monitor.isCanceled()) {
            return Status.CANCEL_STATUS;
          }
        }
      } catch (Exception e) {
        RutaAddonsPlugin.error(e);
        monitor.done();
        testPageView.showBusy(false);
        return new Status(Status.ERROR, RutaAddonsPlugin.PLUGIN_ID,
                "Error during testing. See Error View for details.");
      }
      monitor.done();
      return Status.OK_STATUS;
    }

    /**
     * This method assumes that gold annotations have already been removed from the files. It just
     * applies the script to the files.
     * 
     * @param monitor
     * @param scriptFile
     * @param cleanInputPath
     */
    private void runWithJVM(IProgressMonitor monitor, IFile scriptFile, IPath cleanInputPath,
            IPath runOutputPath) {
      monitor.setTaskName(
              String.format("Processing script \"%s\" [w classpatch ext.].", scriptFile.getName()));

      IProject project = scriptFile.getProject();

      // init args
      String inputDirPath = null;
      String outputDirPath = null;
      if (cleanInputPath != null) {
        inputDirPath = cleanInputPath.toFile().getAbsolutePath();
      } else {
        // TODO throw exception
        return;
      }
      if (runOutputPath != null) {
        outputDirPath = runOutputPath.toFile().getAbsolutePath();
      } else {
        // TODO throw exception
        return;
      }

      try {
        ClassLoader classLoader = RutaProjectUtils.getClassLoader(project);
        IPath descriptorPath = RutaProjectUtils
                .getAnalysisEngineDescriptorPath(scriptFile.getLocation(), project, classLoader);
        String descriptorAbsolutePath = descriptorPath.toFile().getAbsolutePath();
        ILaunchManager mgr = DebugPlugin.getDefault().getLaunchManager();
        ILaunchConfigurationType type = mgr
                .getLaunchConfigurationType(RutaLaunchConfigurationConstants.ID_RUTA_SCRIPT);

        ILaunchConfigurationWorkingCopy copy = type.newInstance(null,
                scriptFile.getName() + ".Testing");
        // do not use RutaLaunchConstants.ARG_INPUT_FOLDER here
        copy.setAttribute(RutaLaunchConstants.INPUT_FOLDER, inputDirPath);
        // do not use RutaLaunchConstants.ARG_OUTPUT_FOLDER here
        copy.setAttribute(RutaLaunchConstants.OUTPUT_FOLDER, outputDirPath);
        copy.setAttribute(RutaLaunchConstants.DESCRIPTOR, descriptorAbsolutePath);
        copy.setAttribute(RutaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName());
        ILaunchConfiguration lc = copy.doSave();

        String mode = ILaunchManager.RUN_MODE;
        if (debug) {
          mode = ILaunchManager.DEBUG_MODE;
        }
        ILaunch launch = new Launch(lc, mode, null);

        ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();

        ILaunch launched = launchConfiguration.launch(ILaunchManager.RUN_MODE, monitor);

        while (!launched.isTerminated()) {
          try {
            Thread.sleep(100);
          } catch (InterruptedException e) {
            RutaAddonsPlugin.error(e);
          }
        }

      } catch (CoreException e) {
        RutaAddonsPlugin.error(e);
      }
    }

    private void clearFolder(final IProject project, final IPath folderPath) {
      FileUtils.deleteAllFiles(folderPath.toFile()); // clear folder
      try {
        project.getFolder(folderPath.makeRelativeTo(project.getLocation()))
                .refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
      } catch (CoreException e1) {
        e1.printStackTrace();
      }
    }

    private void deserializeCASs(CAS tdCas, TestCasData td, CAS casA, File fileA)
            throws FileNotFoundException, SAXException, IOException {
      if (!fileA.exists()) {
        throw new FileNotFoundException(fileA.getAbsolutePath());
      }
      FileInputStream inputStream = null;
      try {
        inputStream = new FileInputStream(new File(td.getPath().toPortableString()));
        XmiCasDeserializer.deserialize(inputStream, tdCas, true);
      } finally {
        if (inputStream != null) {
          inputStream.close();
        }
      }
      try {
        inputStream = new FileInputStream(fileA);
        XmiCasDeserializer.deserialize(inputStream, casA, true);
      } finally {
        if (inputStream != null) {
          inputStream.close();
        }
      }
    }

    private void writeCleanInputFiles(List<TestCasData> testCasData, IProject project,
            IPath tsDescriptorPath, IPath cleanInputPath, IProgressMonitor monitor) {
      try {
        // create CAS:
        XMLInputSource in = new XMLInputSource(tsDescriptorPath.toPortableString());
        TypeSystemDescription tsd = UIMAFramework.getXMLParser().parseTypeSystemDescription(in);
        ClassLoader classLoader = RutaProjectUtils.getClassLoader(project);
        ResourceManager resourceManager = new ResourceManager_impl(classLoader);
        tsd.resolveImports(resourceManager);
        CAS cleanCas = getEmptyCas(tsd);

        for (TestCasData td : testCasData) {
          // init etc
          cleanCas.reset(); // clean
          // deserialize CASes
          FileInputStream inputStreamRun = null;
          try {
            inputStreamRun = new FileInputStream(new File(td.getPath().toPortableString()));
            XmiCasDeserializer.deserialize(inputStreamRun, cleanCas, true);
          } finally {
            if (inputStreamRun != null) {
              inputStreamRun.close();
            }
          }
          cleanCas = cleanCas.getView(viewCasName);
          prepareCas(cleanCas);
          // store clean CAS
          IPath path2CleanFile = computeCleanPath(cleanInputPath, td);
          String fPath = path2CleanFile.toPortableString();
          File cleanFile = new File(fPath);
          writeXmi(cleanCas, cleanFile);

          td.setResultPath(path2CleanFile);

          if (monitor.isCanceled()) {
            return;
          }
        }

        cleanCas.release();
        project.getFolder(cleanInputPath.makeRelativeTo(project.getLocation()))
                .refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
      } catch (Exception e) {
        RutaAddonsPlugin.error(e);
      }
    }

    private IPath computeCleanPath(IPath cleanInputPath, TestCasData td) {
      return cleanInputPath.append(td.getPath().removeFileExtension().lastSegment() + ".xmi");
    }

    private IStatus evalRutaOnlyScript(IProgressMonitor monitor,
            final TestPageBookView testPageView, final TestViewPage debugPage, IFile fScript,
            final IProject project, final IPath engineDescriptorPath,
            ArrayList<TestCasData> testCasData) {
      try {
        // create AE:
        XMLInputSource in = new XMLInputSource(engineDescriptorPath.toPortableString());
        ResourceSpecifier specifier = UIMAFramework.getXMLParser().parseResourceSpecifier(in);
        AnalysisEngine ae = UIMAFramework.produceAnalysisEngine(specifier);

        if (debug) {
          ae.setConfigParameterValue(RutaEngine.PARAM_DEBUG, true);
          ae.setConfigParameterValue(RutaEngine.PARAM_DEBUG_WITH_MATCHES, true);
          ae.setConfigParameterValue(RutaEngine.PARAM_PROFILE, true);
          ae.setConfigParameterValue(RutaEngine.PARAM_STATISTICS, true);
          ae.setConfigParameterValue(RutaEngine.PARAM_CREATED_BY, true);
        }
        ae.reconfigure();

        // create (empty) CAS objects:
        String desc = null;
        desc = engineDescriptorPath.toPortableString();
        XMLInputSource in2 = new XMLInputSource(desc);
        Object descriptor = UIMAFramework.getXMLParser().parse(in2);
        CAS runCas = getEmptyCas(descriptor);
        CAS testCas = getEmptyCas(descriptor);

        for (TestCasData td : testCasData) {
          // init td etc
          runCas.reset();
          testCas.reset();
          String elementName = fScript.getLocation().lastSegment();
          monitor.setTaskName("Processing [w/o classpatch ext.] " + td.getPath().lastSegment());
          int lastIndexOf = elementName.lastIndexOf(RutaEngine.SCRIPT_FILE_EXTENSION);
          if (lastIndexOf != -1) {
            elementName = elementName.substring(0, lastIndexOf);
          }
          // deserialize CASes
          FileInputStream inputStreamTest = null;
          try {
            inputStreamTest = new FileInputStream(new File(td.getPath().toPortableString()));
            XmiCasDeserializer.deserialize(inputStreamTest, testCas, true);
          } finally {
            if (inputStreamTest != null) {
              inputStreamTest.close();
            }
          }
          FileInputStream inputStreamRun = null;
          try {
            inputStreamRun = new FileInputStream(new File(td.getPath().toPortableString()));
            XmiCasDeserializer.deserialize(inputStreamRun, runCas, true);
          } finally {
            if (inputStreamRun != null) {
              inputStreamRun.close();
            }
          }
          testCas = testCas.getView(viewCasName);
          runCas = runCas.getView(viewCasName);

          // gather uima eval-types
          prepareCas(runCas);

          // process run cas and evaluate it
          ae.process(runCas);
          evalLogicAndUpdateGUI(monitor, testPageView, debugPage, fScript, project, runCas, testCas,
                  td);
          if (monitor.isCanceled()) {
            ae.destroy();
            return Status.CANCEL_STATUS;
          }
        }
        ae.destroy();
      } catch (Exception e) {
        RutaAddonsPlugin.error(e);
        monitor.done();
        testPageView.showBusy(false);
        return new Status(Status.ERROR, RutaAddonsPlugin.PLUGIN_ID,
                "Error during testing. See Error View for details.");
      }

      monitor.done();
      testPageView.showBusy(false);
      return Status.OK_STATUS;
    }

    private void prepareCas(CAS cas) {
      if (!includedTypes.isEmpty()) {
        // exclude all other types if there are some included types
        excludedTypes = new ArrayList<String>();
        List<Type> types = cas.getTypeSystem().getProperlySubsumedTypes(cas.getAnnotationType());
        for (Type type : types) {
          if (!includedTypes.contains(type.getName())) {
            excludedTypes.add(type.getName());
          }
        }
      }
      if (includedTypes.isEmpty() && excludedTypes.isEmpty()) {
        // remove all annotation in default settings
        String documentText = cas.getDocumentText();
        cas.reset();
        cas.setDocumentText(documentText);
      } else {
        List<AnnotationFS> toRemove = new LinkedList<AnnotationFS>();
        AnnotationIndex<AnnotationFS> annotationIndex = cas.getAnnotationIndex();
        for (AnnotationFS annotationFS : annotationIndex) {
          Type type = annotationFS.getType();
          String typeName = type.getName();
          if (includedTypes.contains(typeName) || !excludedTypes.contains(typeName)) {
            toRemove.add(annotationFS);
          }
        }
        for (AnnotationFS each : toRemove) {
          if (!cas.getDocumentAnnotation().equals(each)) {
            cas.removeFsFromIndexes(each);
          }
        }
      }
    }

    private void evalLogicAndUpdateGUI(IProgressMonitor monitor,
            final TestPageBookView testPageView, final TestViewPage debugPage, IFile fScript,
            final IProject project, CAS runCas, CAS goldCas, TestCasData td)
            throws AnalysisEngineProcessException, CASException, IOException, SAXException,
            CoreException {
      // memento for prefs
      IPreferenceStore store = RutaAddonsPlugin.getDefault().getPreferenceStore();
      String factoryName = store.getString(TestingPreferenceConstants.EVALUATOR_FACTORY);
      ICasEvaluator evaluator = RutaAddonsPlugin.getCasEvaluatorFactoryById(factoryName)
              .createEvaluator();
      boolean includeSubtypes = store.getBoolean(TestingPreferenceConstants.INCLUDE_SUBTYPES);
      boolean useAllTypes = store.getBoolean(TestingPreferenceConstants.ALL_TYPES);

      CAS resultCas = evaluator.evaluate(goldCas, runCas, excludedTypes, includeSubtypes,
              useAllTypes);

      // store results
      IPath path2Test = td.getPath().removeLastSegments(1);

      monitor.setTaskName("Actually evaluating " + td.getPath().lastSegment());

      IPath estimatedTestPath = project.getFullPath()
              .append(RutaProjectUtils.getDefaultTestLocation());
      IPath path2recource = fScript.getFullPath();
      IPath projectRelativePath2Script = path2recource.removeFirstSegments(2);
      IPath estimatedTestFolderPath = estimatedTestPath
              .append(projectRelativePath2Script.removeFileExtension());

      IPath path2Result = path2Test.append(TestCasData.RESULT_FOLDER);
      IPath path2ResultFile = path2Result
              .append(td.getPath().removeFileExtension().lastSegment() + ".result.xmi");

      if (!path2Test.toOSString().contains(estimatedTestFolderPath.toOSString())) {
        path2Result = project.getLocation().append(RutaProjectUtils.getDefaultTestLocation())
                .append(RutaProjectUtils.getDefaultTempTestLocation());
        path2ResultFile = path2Result
                .append(td.getPath().removeFileExtension().lastSegment() + ".result.xmi");
      }

      File resultFile = new File(path2ResultFile.toPortableString());
      writeXmi(resultCas, resultFile);

      td.setResultPath(path2ResultFile);

      // finally, calculate eval data and show it in the GUI
      EvalDataProcessor.calculateEvaluatData(td, resultCas);

      testPageView.getDefaultPage().getControl().getDisplay().asyncExec(new Runnable() {
        @Override
        public void run() {
          debugPage.getViewer().refresh();
        }
      });
      monitor.worked(1);
      project.getFolder(path2Result.makeRelativeTo(project.getLocation()))
              .refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
      runCas.release();
      goldCas.release();
      resultCas.release();
    }

    private CAS getEmptyCas(Object descriptor)
            throws ResourceInitializationException, InvalidXMLException {
      CAS testCas = null;
      if (descriptor instanceof AnalysisEngineDescription) {
        testCas = CasCreationUtils.createCas((AnalysisEngineDescription) descriptor);
      } else if (descriptor instanceof TypeSystemDescription) {
        TypeSystemDescription tsDesc = (TypeSystemDescription) descriptor;
        tsDesc.resolveImports();
        testCas = CasCreationUtils.createCas(tsDesc, null, new FsIndexDescription[0]);
      }
      return testCas;
    }
  }

  @Override
  public void addHandlerListener(IHandlerListener handlerListener) {

  }

  @Override
  public void dispose() {

  }

  @Override
  public Object execute(ExecutionEvent event) throws ExecutionException {
    TestPageBookView debugView = (TestPageBookView) HandlerUtil.getActivePart(event);
    if (!(debugView.getCurrentPage() instanceof TestViewPage)) {
      return Status.CANCEL_STATUS;
    }
    TestViewPage debugPage = (TestViewPage) debugView.getCurrentPage();
    // set debug by preference
    boolean debug = true;
    String viewCasName = debugPage.getSelectedViewCasName();
    String scriptName = debugPage.getResource().getLocation().lastSegment();
    RerunHandlerJob job = new RerunHandlerJob(event, scriptName, viewCasName,
            debugPage.getExcludedTypes(), debugPage.getIncludedTypes(), debug);

    job.addJobChangeListener(new DebugJobChangeAdapter(debugPage) {
    });

    job.schedule();

    return null;
  }

  private static void writeXmi(CAS aCas, File file) throws IOException, SAXException {
    FileOutputStream out = null;
    try {
      file.getParentFile().mkdirs();
      out = new FileOutputStream(file);
      XmiCasSerializer.serialize(aCas, out);
    } catch (Exception e) {
      RutaAddonsPlugin.error(e);
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }

  @Override
  public boolean isEnabled() {
    return true;
  }

  @Override
  public boolean isHandled() {
    return true;
  }

  @Override
  public void removeHandlerListener(IHandlerListener handlerListener) {

  }

  /**
   * 
   * @param data
   *          will be filled with results
   * @param resultCas
   *          must contain TP, FP, FN annotations
   */
  public void calculateEvaluatData(TestCasData data, CAS resultCas) {
    data.setEvaluationStatus(true);
    TypeSystem ts = resultCas.getTypeSystem();
    Type falsePositiveType = ts.getType(ICasEvaluator.FALSE_POSITIVE);
    Type falseNegativeType = ts.getType(ICasEvaluator.FALSE_NEGATIVE);
    Type truePositiveType = ts.getType(ICasEvaluator.TRUE_POSITIVE);

    int falsePositiveCount = resultCas.getAnnotationIndex(falsePositiveType).size();
    int falseNegativeCount = resultCas.getAnnotationIndex(falseNegativeType).size();
    int truePositiveCount = resultCas.getAnnotationIndex(truePositiveType).size();

    data.setTruePositiveCount(truePositiveCount);
    data.setFalsePositiveCount(falsePositiveCount);
    data.setFalseNegativeCount(falseNegativeCount);

    HashMap<String, TypeEvalData> map = new HashMap<String, TypeEvalData>();

    AnnotationIndex<AnnotationFS> index = resultCas.getAnnotationIndex(truePositiveType);

    FSIterator<AnnotationFS> iter = index.iterator();

    while (iter.isValid()) {
      EvalAnnotation a = (EvalAnnotation) iter.next();
      Annotation original = a.getOriginal();
      Type originalType = original.getType();

      if (map.containsKey(originalType.getName())) {
        TypeEvalData element = map.get(originalType.getName());
        int oldCount = element.getTruePositives();
        element.setTruePositives(oldCount + 1);
      } else {
        TypeEvalData newData = new TypeEvalData(originalType.getName(), 1, 0, 0);
        map.put(originalType.getName(), newData);
      }
    }

    index = resultCas.getAnnotationIndex(falsePositiveType);
    iter = index.iterator();

    while (iter.isValid()) {
      EvalAnnotation a = (EvalAnnotation) iter.next();
      Annotation original = a.getOriginal();
      Type originalType = original.getType();

      if (map.containsKey(originalType.getName())) {
        TypeEvalData element = map.get(originalType.getName());
        int oldCount = element.getFalsePositives();
        element.setFalsePositives(oldCount + 1);
      } else {
        TypeEvalData newData = new TypeEvalData(originalType.getName(), 0, 1, 0);
        map.put(originalType.getName(), newData);
      }
    }

    index = resultCas.getAnnotationIndex(falseNegativeType);
    iter = index.iterator();

    while (iter.isValid()) {
      EvalAnnotation a = (EvalAnnotation) iter.next();
      Annotation original = a.getOriginal();
      Type originalType = original.getType();

      if (map.containsKey(originalType.getName())) {
        TypeEvalData element = map.get(originalType.getName());
        int oldCount = element.getFalseNegatives();
        element.setFalseNegatives(oldCount + 1);
      } else {
        TypeEvalData newData = new TypeEvalData(originalType.getName(), 0, 0, 1);
        map.put(originalType.getName(), newData);
      }
    }

    data.setTypeEvalData(map);
  }

}
