/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package usda.weru.gis;


import com.vividsolutions.jts.geom.Geometry;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.data.DataStore;
import org.geotools.data.DefaultRepository;
import org.geotools.feature.NameImpl;
import org.opengis.feature.Feature;
import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.type.Name;



/**
 *
 * @author Joseph A. Levin <joelevin@weru.ksu.edu>
 */
public class GISData extends DefaultRepository {
    private static final Logger LOGGER = Logger.getLogger(GISData.class.getName());
    private static GISData c_instance;
    private List<GISLookup> c_lookups;
    private Map <Name, Integer> c_sortLayers;

    private GISData() {
        //TODO: actually use a config file to load gis data
        try {
            MapData.prepareGISData(this);
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Unable to initilize GISData", e);            
        }

    }

    public static synchronized GISData getInstance() {
        if (c_instance == null) {
            c_instance = new GISData();
        }

        return c_instance;
    }

    public <T> GISLookup<T> getLookup(Class<T> c) {
        synchronized (c) {
            List<GISLookup<T>> temp = new LinkedList<GISLookup<T>>();
            
            for (GISLookup lookup : lookups()) {
                if (lookup.supports(c)) {
                    temp.add(lookup);
                }
            }

            if (temp.size() == 1) {
                return temp.get(0);
            } else if (temp.size() > 1) {
                return new MergedGISLookup<T>(c, temp.toArray(new GISLookup[temp.size()]));
            } else {
                return null;
            }
            
        }
    }

    private synchronized void initLookups() {
        c_lookups = new LinkedList<GISLookup>();
        ServiceLoader<GISLookup> loader = ServiceLoader.load(GISLookup.class);
        Iterator<GISLookup> i = loader.iterator();
        while (i.hasNext()) {
            c_lookups.add(i.next());
        }
    }

    private synchronized List<GISLookup> lookups() {
        if (c_lookups == null) {
            initLookups();
        }
        return c_lookups;
    }

    @Override
    public Set<Name> getNames() {
        Set<Name> names = super.getNames();
        List <Name> temp = new ArrayList<Name>();
        
        temp.addAll(0, names);
        Collections.sort(temp, new LayerSorter());
        return new LinkedHashSet<Name>(temp);
    }
    
    public static <O> Geometry getGeometry(O o) throws IllegalArgumentException{
        if(o == null){
            return null;
        }
        
        GISLookup<O> lookup = (GISLookup<O>) getInstance().getLookup(o.getClass());
        
        if(lookup == null){
            throw new IllegalArgumentException(String.format("No GISLookup implementations found for class: %s", o.getClass()));
        }
        
        List<Feature> features = lookup.lookup(o);
        if(features == null || features.isEmpty()){
            return null;
        }
        
        Feature firstFeature = features.get(0);
        
        GeometryAttribute attribute = firstFeature.getDefaultGeometryProperty();          
        if(attribute == null){
            return null;
        }
        
        Object value = attribute.getValue();
        if(value == null){
            return null;
        }
        
        if(value instanceof Geometry){
            return (Geometry) value;
        }        
        
        
        return null;
                
    }


    public void register(String id, DataStore dataStore) throws IOException {
        register(id, dataStore, -1);
    }

    public void register(String id, DataStore dataStore, int sortLayer) throws IOException {
        if(c_sortLayers == null){
            c_sortLayers = new HashMap<Name, Integer>();
        }
        if(dataStore != null){
            c_sortLayers.put(new NameImpl(id), sortLayer);
        }
        else{
            c_sortLayers.remove(new NameImpl(id));
        }
        
        super.register(id, dataStore);
        for (GISLookup lookup : lookups()) {
            lookup.register(id, dataStore);
        }
    }

    private class LayerSorter implements Comparator<Name>{

        @Override
        public int compare(Name o1, Name o2) {
            int i1 = c_sortLayers.get(o1);
            int i2 = c_sortLayers.get(o2);

            return i1 - i2;
        }

    }
}
