/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    Created on Aug 3, 2004
 */
package org.geotools.data.vpf.file;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import org.geotools.data.AbstractDataStore;
import org.geotools.data.FeatureReader;
import org.geotools.feature.FeatureType;
import org.geotools.feature.SchemaException;


/**
 * A data store for VPF files. Any file can be retrieved from here.
 * If you want joins (for example features with their geometries), 
 * you will have to look elsewhere.
 * Hopefully some one will take advantage of this class to provide 
 * the full functionality.
 * @author <a href="mailto:jeff@ionicenterprise.com">Jeff Yutzler</a>
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/vpf/src/main/java/org/geotools/data/vpf/file/VPFFileStore.java $
 */
public class VPFFileStore extends AbstractDataStore {
    /**
     * A collection of files which are available
     * Don't ask me how/when to close them!
     */
    private Map files;

    /**
     * Default constructor. Nothing special
     *
     */
    public VPFFileStore() {
        files = new HashMap();
    }

    /* (non-Javadoc)
     * @see org.geotools.data.AbstractDataStore#getTypeNames()
     */
    public String[] getTypeNames() {
        String[] result = new String[files.size()];
        int counter = 0;
        VPFFile currentFile;
        Iterator iter = files.keySet().iterator();

        while (iter.hasNext()) {
            currentFile = (VPFFile) iter.next();
            result[counter] = currentFile.getTypeName();
        }

        return result;
    }

    /* (non-Javadoc)
     * @see org.geotools.data.AbstractDataStore#getSchema(java.lang.String)
     */
    public FeatureType getSchema(String pathName) throws IOException {
        FeatureType result = null;

        if (files.containsKey(pathName)) {
            result = (FeatureType) files.get(pathName);
        } else {
            try {
                result = findFile(pathName);
            } catch (SchemaException exc) {
                throw new IOException("Schema error in path: " + pathName
                    + "\n" + exc.getMessage());
            }

            files.put(pathName, result);
        }

        return result;
    }

    // How on earth does one get from the query to this method?
    /* (non-Javadoc)
     * @see org.geotools.data.AbstractDataStore#getFeatureReader(java.lang.String)
     */
    protected FeatureReader getFeatureReader(String pathName)
        throws IOException {
        return new VPFFileFeatureReader((VPFFile) getSchema(pathName));
    }
    /**
     * Closes all of the opoen files and removes them from the collection of
     * open files.
     *
     */
    public void reset(){
        Iterator iter = files.values().iterator();
        VPFFile file = null;
        while(iter.hasNext()){
            try {
                file = (VPFFile)iter.next();
                file.close();
            } catch (Exception exc) {
                // No idea why this might happen
                exc.printStackTrace();
            }
        }
        files.clear();
    }

    /**
     * This does basically a case independent file search
     * through the pathName to try to find the file.
     * This is necessary due to many problems seen with VPF
     * data disks where the file names specified in the VPF files
     * do not match the case of the files on disk.  
     **/
    private VPFFile findFile(String pathName)
        throws IOException, SchemaException {

        if (new File(pathName).exists())
            return new VPFFile(pathName);

        ArrayList matches = new ArrayList();
        matches.add("");  // Need to start with something in the list
        StringTokenizer st = new StringTokenizer(pathName, File.separator);
        while (st.hasMoreTokens() && !matches.isEmpty()) {
            String curr = st.nextToken();
            String currUpper = curr.toUpperCase();
            String currLower = curr.toLowerCase();
            boolean useUpper = !curr.equals(currUpper);
            boolean useLower = !curr.equals(currLower);
            ArrayList newMatches = new ArrayList();
            
            for(Iterator it = matches.iterator(); it.hasNext(); ) {
                String match = (String)it.next();
                String tmp = match + File.separator + curr;

                if (new File(tmp).exists())
                    newMatches.add(tmp);
                else {
                    // For performance reasons only do the upper and lower case checks
                    // if the "as-is" check fails.  
                    if (useUpper) {
                        tmp = match + File.separator + currUpper;
                        if (new File(tmp).exists())
                            newMatches.add(tmp);
                    }
                
                    if (useLower) {
                        tmp = match + File.separator + currLower;
                        if (new File(tmp).exists())
                            newMatches.add(tmp);
                    }
                }
            }
            matches = newMatches;
        }

        if (matches.isEmpty()) {
            throw new FileNotFoundException("Could not find file: " + pathName);
        }
        
        return new VPFFile((String)matches.get(0));
    }
}
