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

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import net.ranides.assira.collection.lists.RTList;
import net.ranides.assira.collection.maps.GenericHashMap;
import net.ranides.assira.collection.maps.GenericMap;
import net.ranides.assira.collection.maps.HashMap;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.CQueryBuilder;
import net.ranides.assira.credentials.UserProperty;
import net.ranides.assira.credentials.UserStore;
import net.ranides.assira.reflection.IClass;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class URIResolver {
    
    public static final URIResolver DEFAULT = new URIResolver(ServiceLoader.load(URIHandler.class));
    
    private final MStore store = new MStore();
    
    private final Map<String, URIHandler> handlers;
    
    public URIResolver() {
        this.handlers = new HashMap<>(DEFAULT.handlers);
    }
    
    public URIResolver(Iterable<URIHandler> handlers) {
        this.handlers = new HashMap<>();
        for(URIHandler handler : handlers) {
            this.register(handler);
        }
    }
    
    public URIHandle resolve(String location) throws IOException {
        try {
            return resolve(new URI(location));
        } catch (URISyntaxException cause) {
            throw new IOException(cause);
        }
    }
    
    public URIHandle resolve(File location) throws IOException{
        return resolve(location.toURI());
    }
    
    public URIHandle resolve(URI location) throws IOException {
        String scheme = location.getScheme();
        URIHandler handler = handlers.get(scheme);
        if(null == handler) {
            throw new IOException("Unsupported scheme: " + scheme);
        }
        return handler.resolve(location);
    }
    
    public URIResolver register(URIHandler handler) {
        for(String s : handler.schemes()) {
            handlers.put(s, handler);
        }
        return this;
    }
    
    public URIResolver register(Class<? extends URIHandler> handler) {
        return register(IClass.typeinfo(handler).construct(this));
    }
    
    public URIResolver users(UserStore store) {
        this.store.add(store);
        return this;
    }
    
    public UserStore users() {
        return store;
    }
    
    private static final class MStore implements UserStore {
        
        private final List<UserStore> stores = new RTList<>(new UserStore[]{new CStore()});

        @Override
        public CQuery<GenericMap<String>> match(URI uri) {
            return CQueryBuilder.fromCollection(stores).unfold(s -> s.match(uri));
        }

        @Override
        public CQuery<GenericMap<String>> find(String user) {
            return CQueryBuilder.fromCollection(stores).unfold(s -> s.find(user));
        }
        
        public void add(UserStore store) {
            stores.add(store);
        }
        
    }
    
    private static final class CStore implements UserStore {
        
        @Override
        public CQuery<GenericMap<String>> match(URI uri) {
            GenericMap<String> a = new GenericHashMap<>();
            a.put(UserProperty.LOGIN, URIUtils.getLogin(uri));
            a.put(UserProperty.PASSWORD, URIUtils.getPassword(uri));
            return CQueryBuilder.of(a);
        }

        @Override
        public CQuery<GenericMap<String>> find(String user) {
            return CQueryBuilder.empty();
        }
    
    }

}
