package org.jbehave.core.io;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;

/**
 * Loads story resources from relative file paths that are
 * traversal to a given location.
 * 
 * StoryLoader loader = new
 * LoadFromRelativeFile(codeLocationFromClass(YourStory.class));
 * 
 * By default, it uses traversal directory
 * 'target/test-classes' with source dir in 'src/test/java'.
 * 
 * Other traversal locations can be specified via the varargs constructor:
 * 
 * StoryLoader loader = new
 * LoadFromRelativeFile(codeLocationFromClass(YourStory.class),
 * mavenModuleTestStoryFilePath("src/test/java"),
 * intellijProjectTestStoryFilePath("src/test/java"));
 * 
 * Convenience methods : {@link LoadFromRelativeFile#mavenModuleStoryFilePath},
 * {@link LoadFromRelativeFile#mavenModuleTestStoryFilePath}
 * {@link LoadFromRelativeFile#intellijProjectStoryFilePath}
 * {@link LoadFromRelativeFile#intellijProjectTestStoryFilePath}
 * 
 * @see {@link CodeLocations#codeLocationFromClass(Class)}
 * 
 */
public class LoadFromRelativeFile implements ResourceLoader, StoryLoader {

    private final StoryFilePath[] traversals;
    private final URL location;

    public LoadFromRelativeFile(URL location) {
        this(location, mavenModuleStoryFilePath("src/test/java"));
    }

    public LoadFromRelativeFile(URL location, StoryFilePath... traversals) {
        this.traversals = traversals;
        this.location = location;
    }
    
    public String loadResourceAsText(String resourcePath) {
        List<String> traversalPaths = new ArrayList<String>();
        String locationPath = new File(CodeLocations.getPathFromURL(location)).getAbsolutePath();
        for (StoryFilePath traversal : traversals) {
            String filePath = locationPath.replace(traversal.toRemove, traversal.relativePath) + "/" + resourcePath;
            File file = new File(filePath);
            if (file.exists()) {
                return loadContent(filePath);
            } else {
                traversalPaths.add(filePath);
            }
        }
        throw new StoryResourceNotFound(resourcePath, traversalPaths);
    }

    public String loadStoryAsText(String storyPath) {
        List<String> traversalPaths = new ArrayList<String>();
        String locationPath = new File(CodeLocations.getPathFromURL(location)).getAbsolutePath();
        for (StoryFilePath traversal : traversals) {
            String filePath = locationPath.replace(traversal.toRemove, traversal.relativePath) + "/" + storyPath;
            File file = new File(filePath);
            if (file.exists()) {
                return loadContent(filePath);
            } else {
                traversalPaths.add(filePath);
            }
        }
        throw new StoryResourceNotFound(storyPath, traversalPaths);
    }

    protected String loadContent(String path) {
        try {
            return IOUtils.toString(new FileInputStream(new File(path)));
        } catch (Exception e) {
            throw new InvalidStoryResource(path, e);
        }
    }

    /**
     * For use the the varargs constructor of {@link LoadFromRelativeFile}, to
     * allow a range of possibilities for locating Story file paths
     */
    public static class StoryFilePath {
        private final String toRemove;
        private final String relativePath;

        public StoryFilePath(String toRemove, String relativePath) {
            this.toRemove = toRemove.replace('\\', '/');
            this.relativePath = relativePath.replace('\\', '/');
        }
    }

    /**
     * Maven by default, has its PRODUCTION classes in target/classes. This
     * story file path is relative to that.
     * 
     * @param relativePath
     *            the path to the stories' base-dir inside the module
     * @return the resulting StoryFilePath
     */
    public static StoryFilePath mavenModuleStoryFilePath(String relativePath) {
        return new StoryFilePath("target/classes", relativePath);
    }

    /**
     * Maven by default, has its TEST classes in target/test-classes. This story
     * file path is relative to that.
     * 
     * @param relativePath
     *            the path to the stories' base-dir inside the module
     * @return the resulting StoryFilePath
     */
    public static StoryFilePath mavenModuleTestStoryFilePath(String relativePath) {
        return new StoryFilePath("target/test-classes", relativePath);
    }

    /**
     * Intellij by default, has its PRODUCTION classes in classes/production.
     * This story file path is relative to that.
     * 
     * @param relativePath
     *            the path to the stories' base-dir inside the module
     * @return the resulting StoryFilePath
     */
    public static StoryFilePath intellijProjectStoryFilePath(String relativePath) {
        return new StoryFilePath("classes/production", relativePath);
    }

    /**
     * Intellij by default, has its TEST classes in classes/test. This story
     * file path is relative to that.
     * 
     * @param relativePath
     *            the path to the stories' base-dir inside the module
     * @return the resulting StoryFilePath
     */
    public static StoryFilePath intellijProjectTestStoryFilePath(String relativePath) {
        return new StoryFilePath("classes/test", relativePath);
    }

}
@@ -1694,32 +1694,42 @@
  locationPath = 
+normalise(
 new File(CodeLoc
@@ -1770,32 +1770,33 @@
 etAbsolutePath()
+)
 ;%0A        for (S
@@ -3144,32 +3144,130 @@
        %7D%0A    %7D%0A%0A
+    private static String normalise(String path) %7B%0A        return path.replace('%5C%5C', '/');%0A    %7D%0A%0A
     /**%0A     * F
@@ -3637,34 +3637,26 @@
 e = 
-toRemove.replace('%5C%5C', '/'
+normalise(toRemove
 );%0A 
@@ -3686,16 +3686,26 @@
 ePath = 
+normalise(
 relative
@@ -3708,34 +3708,16 @@
 tivePath
-.replace('%5C%5C', '/'
 );%0A     
@@ -3713,32 +3713,33 @@
 ath);%0A        %7D%0A
+%0A
     %7D%0A%0A    /**%0A 

Normalise paths to use '/'