/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.io.uri.impl;

import net.ranides.assira.collection.lists.ImmutableList;
import net.ranides.assira.io.uri.URIHandle;
import net.ranides.assira.io.uri.URIHandler;
import net.ranides.assira.io.uri.URIResolver;
import net.ranides.assira.io.uri.URIUtils;
import net.ranides.assira.text.StringUtils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;

/**
 * This URIHandler loads local files from specified directory
 *
 * Please note, that CDirHandler only converts uri into physical locations.
 * I/O operations will be delegated to internal instance of CFileHandler
 *
 * Security considerations:
 * Handler won't allow access to any file located outside specified directory.
 * In such case #resolve method will throw FileNotFoundException
 *
 * Please note:
 * This URIHandler should not be constructed directly, but connected with some instance of URIResolver.
 * This URIHandler does not declare default constructor and it is not detected by service discovery.
 *
 * There is a few inner subclasses of CDirHandler.
 * These classes ARE detected by service discovery.
 *
 * Supportes schemes:
 *      test    - project directory "src/test/resources"
 *      home    - system user home directory
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class CDirHandler implements URIHandler {

    private final CFileHandler handler;
    private final Collection<String> schemes;
    private final Path base;

    /**
     * Creates new instance of CDirHandler, which searches inside specified directory.
     * This instance will run for schemes passed as argument.
     * Schemes argument is a string with values separated by space of one of special chars {@code ,;:|}
     *
     * @param resolver resolver
     * @param schemes schemes
     * @param directory directory
     */
    protected CDirHandler(URIResolver resolver, String schemes, String directory) {
        this.handler = new CFileHandler(resolver);
        this.schemes = new ImmutableList<>(schemes.split("[,;:| ]+"));
        this.base = Paths.get(directory).toAbsolutePath().normalize();
    }

    @Override
    public URIHandle resolve(URI uri) throws IOException {
        String path = StringUtils.ltrim(URIUtils.getFile(uri), '/');
        Path resolved = base.resolve(path).normalize();

        if(!resolved.startsWith(base)) {
            throw new FileNotFoundException(uri.toString());
        }
        return handler.resolve(resolved);
    }

    @Override
    public Collection<String> schemes() {
        return schemes;
    }

    /**
     * This is special subclass of CDirHandler, which points to project's test resources
     */
    public static class CTestHandler extends CDirHandler {

        /**
         * Creates default handler. It is used by default URIResolver
         */
        public CTestHandler() {
            this(URIResolver.DEFAULT);
        }

        /**
         * Creates handler connected with specified resolver.
         *
         * @param resolver resolver
         */
        public CTestHandler(URIResolver resolver) {
            super(resolver, "test", "src/test/resources");
        }

    }

    /**
     * This is special subclass of CDirHandler, which points to user's home directory
     */
    public static class CHomeHandler extends CDirHandler {

        /**
         * Creates default handler. It is used by default URIResolver
         */
        public CHomeHandler() {
            this(URIResolver.DEFAULT);
        }

        /**
         * Creates handler connected with specified resolver.
         *
         * @param resolver resolver
         */
        public CHomeHandler(URIResolver resolver) {
            super(resolver, "home", System.getProperty("user.home"));
        }

    }

}
