/*
 * @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();

    static {
        for (URIHandler h : ServiceLoader.load(URIHandler.class)) {
            DEFAULT.register(h.getClass());
        }
    }

    private final MergeStore store = new MergeStore();
    
    private final List<IClass<? extends URIHandler>> types;
    private final Map<String, URIHandler> handlers;

    public URIResolver() {
        types = new RTList<>();
        handlers = new HashMap<>();
    }

    public URIResolver(URIResolver source) {
        types = new RTList<>(source.types);
        handlers = new HashMap<>();
        for (IClass<? extends URIHandler> type : types) {
            register0(type.construct(this));
        }
    }
    
    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(Class<? extends URIHandler> type) {
        IClass<? extends URIHandler> itype = IClass.typeinfo(type);
        URIHandler handler = itype.construct(this);
        types.add(itype);
        register0(handler);
        return this;
    }

    private void register0(URIHandler handler) {
        for(String scheme : handler.schemes()) {
            handlers.put(scheme, handler);
        }
    }

    public URIResolver users(UserStore store) {
        this.store.add(store);
        return this;
    }
    
    public UserStore users() {
        return store;
    }
    
    private static final class MergeStore implements UserStore {
        
        private final List<UserStore> stores = new RTList<>(new UserStore[]{new ExtractStore()});

        @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 ExtractStore 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();
        }
    
    }

}
