/**
 * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
package org.python.pydev.debug.pyunit;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Assert;
import org.python.pydev.core.ICompletionCache;
import org.python.pydev.core.IDefinition;
import org.python.pydev.core.IModule;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.actions.PyOpenAction;
import org.python.pydev.editor.codecompletion.revisited.CompletionCache;
import org.python.pydev.editor.codecompletion.revisited.CompletionStateFactory;
import org.python.pydev.editor.model.ItemPointer;
import org.python.pydev.editor.refactoring.PyRefactoringFindDefinition;
import org.python.pydev.editorinput.PySourceLocatorBase;
import org.python.pydev.parser.fastparser.FastDefinitionsParser;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.visitors.NodeUtils;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.shared_core.io.FileUtils;
import org.python.pydev.shared_core.string.FastStringBuffer;

public class PyUnitTestResult {

    public final String status;
    public final String location;
    public final String test;
    public final String capturedOutput;
    public final String errorContents;
    public final String time;
    private WeakReference<PyUnitTestRun> testRun;

    public final String STATUS_OK = "ok";
    public final String STATUS_OK_SKIPPED = "ok (skipped)";
    public final String STATUS_SKIP = "skip";
    public final String STATUS_FAIL = "fail";
    public final String STATUS_ERROR = "error";
    public final String index;

    public PyUnitTestResult(PyUnitTestRun testRun, String status, String location, String test, String capturedOutput,
            String errorContents, String time) {
        Assert.isNotNull(capturedOutput);
        Assert.isNotNull(errorContents);
        Assert.isNotNull(time);
        Assert.isNotNull(test);
        Assert.isNotNull(location);
        Assert.isNotNull(status);
        Assert.isNotNull(testRun);
        //note that the parent has a strong reference to the children.
        this.testRun = new WeakReference<PyUnitTestRun>(testRun);
        this.status = status;
        this.location = location;
        this.test = test;
        this.capturedOutput = capturedOutput;
        this.errorContents = errorContents;
        this.time = time;
        this.index = testRun.getNextTestIndex();
    }

    public PyUnitTestRun getTestRun() {
        return this.testRun.get();
    }

    public boolean isOk() {
        return STATUS_OK.equals(this.status);
    }

    public boolean isSkip() {
        return STATUS_SKIP.equals(this.status) || STATUS_OK_SKIPPED.equals(this.status);
    }

    /**
     * Note that this string is used for the tooltip in the tree (so, be careful when changing it, as the information
     * presentation is based on its format to add a different formatting).
     */
    @Override
    public String toString() {
        int fixedContentsLen = 50;
        FastStringBuffer buf = new FastStringBuffer(this.test.length() + this.status.length() + this.time.length()
                + this.location.length() + this.errorContents.length() + this.capturedOutput.length()
                + fixedContentsLen);

        return buf.append(this.test).append(" Status: ").append(this.status).append(" Time: ").append(this.time)
                .append("\n\n").append("File: ").append(this.location).append("\n\n").append(this.errorContents)
                .append("\n\n").append(this.capturedOutput).append("\n\n").toString();
    }

    public void open() {
        File file = new File(this.location);
        if (file.exists()) {
            PyOpenAction openAction = new PyOpenAction();
            String fileContents = FileUtils.getFileContents(file);
            String thisTest = this.test;
            int i = thisTest.indexOf('['); // This happens when parameterizing pytest tests.
            if (i != -1) {
                thisTest = thisTest.substring(0, i).trim();
            }
            i = thisTest.indexOf('('); // This happens with unittest subtests.
            if (i != -1) {
                thisTest = thisTest.substring(0, i).trim();
            }
            ItemPointer itemPointer = getItemPointer(file, fileContents, thisTest);
            openAction.run(itemPointer);
        }
    }

    public static ItemPointer getItemPointer(File file, String fileContents, String testPath) {
        SimpleNode testNode = null;
        if (fileContents != null) {
            SimpleNode node = FastDefinitionsParser.parse(fileContents, "", file);
            if (testPath != null && testPath.length() > 0) {
                testNode = NodeUtils.getNodeFromPath(node, testPath);
            }
        }

        ItemPointer itemPointer;
        if (testNode != null) {
            itemPointer = new ItemPointer(file, testNode);
        } else {
            //Ok, it's not defined directly here (it's probably in a superclass), so, let's go on and 
            //do an actual (more costly) find definition.
            try {
                PySourceLocatorBase locator = new PySourceLocatorBase();
                IFile workspaceFile = locator.getWorkspaceFile(file, null);
                if (workspaceFile != null && workspaceFile.exists()) {
                    IProject project = workspaceFile.getProject();
                    if (project != null && project.exists()) {
                        PythonNature nature = PythonNature.getPythonNature(project);
                        String moduleName = nature.resolveModule(file);
                        if (moduleName != null) {
                            IModule mod = nature.getAstManager().getModule(moduleName, nature, true);
                            if (mod != null) {
                                ICompletionCache completionCache = new CompletionCache();
                                IDefinition[] definitions = mod.findDefinition(CompletionStateFactory
                                        .getEmptyCompletionState(testPath, nature, completionCache), -1, -1, nature);

                                if (definitions != null && definitions.length > 0) {
                                    List<ItemPointer> pointers = new ArrayList<ItemPointer>();
                                    PyRefactoringFindDefinition.getAsPointers(pointers, definitions);
                                    if (pointers.size() > 0) {
                                        return pointers.get(0);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                Log.log(e);
            }
            //if we couldn't actually get the definition line, at least open the file we had (although that may not really
            //be the place where it's defined if it's a test in a superclass).
            itemPointer = new ItemPointer(file);
        }
        return itemPointer;
    }

}
