/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2004-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.
 */
package org.geotools.data.vpf;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.AbstractSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.net.URI;

import org.geotools.data.vpf.file.VPFFile;
import org.geotools.data.vpf.file.VPFFileFactory;
import org.geotools.data.vpf.ifc.FCode;
import org.geotools.data.vpf.ifc.FileConstants;
import org.geotools.data.vpf.ifc.VPFCoverageIfc;
import org.geotools.feature.Feature;
import org.geotools.feature.SchemaException;

/**
 * A VPF coverage. This class constructs and contains both feature
 * classes and feature types. 
 * @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/VPFCoverage.java $
 */
public class VPFCoverage implements FCode, FileConstants, VPFCoverageIfc {
    /**
     * The description attribute of the coverage
     */
    private final String description;

    /** List of feature classes part of this coverage */
    private final List featureClasses = new Vector();

    /** List of feature types part of this coverage */
    private final List featureTypes = new Vector(25);

    /** The owning library */
    private final VPFLibrary library;
    /** Path name for the directory containing this coverage */
    private final String pathName;
    /**
     * The topology level (0-3)
     */
    private final int topologyLevel;

    /**
     * The namespace to create features with.
     */
    private final URI namespace;

    /**
     * Constructor
     * 
     * @param cLibrary 
     * @param feature 
     * @param cDirectoryName  path to directory containing coverage
     *
     * @throws IOException if the directory does not contain a valid FCS file
     * @throws SchemaException For problems making one of the feature classes as a FeatureType.
     */
    public VPFCoverage(VPFLibrary cLibrary, Feature feature, String cDirectoryName)
        throws IOException, SchemaException {
	this(cLibrary, feature, cDirectoryName, null);
    }

    /**
     * Constructor with namespace
     * 
     * @param cLibrary 
     * @param feature
     * @param cDirectoryName path to directory containing coverage
     * @param namespace
     *
     * @throws IOException if the directory does not contain a valid FCS file
     * @throws SchemaException For problems making one of the feature classes as a FeatureType.
     */
    public VPFCoverage(VPFLibrary cLibrary, Feature feature, 
                       String cDirectoryName, URI namespace)
        throws IOException, SchemaException {
        topologyLevel = Short.parseShort(feature.getAttribute(FIELD_LEVEL).toString());
        library = cLibrary;
        description = feature.getAttribute(VPFCoverageIfc.FIELD_DESCRIPTION).toString();
	this.namespace = namespace;
        pathName = cDirectoryName.concat(File.separator).concat(feature.getAttribute(FIELD_COVERAGE_NAME).toString());
        discoverFeatureClasses();
        discoverFeatureTypes();
    }

    /**
     * Builds feature classes for the coverage
     *
     * @throws SQLException
     */
    private void discoverFeatureClasses() throws IOException, SchemaException{
        VPFFeatureClass featureClass = null;
        boolean hasFeatureClass;
        String fcsFileName = pathName + File.separator + TABLE_FCS;
        AbstractSet featureClassNames = new HashSet();
        String featureClassName;

        // We need to record all of the possible files 
        // for each of the feature classes in this coverage
        VPFFile file =  VPFFileFactory.getInstance().getFile(fcsFileName);

        // We might want to grab the FCS list and pass it to the feature class
        // constructor just to save time.
        Iterator iter = file.readAllRows().iterator();
        while (iter.hasNext()) {
            Feature row = (Feature) iter.next();
            featureClassName = row.getAttribute("feature_class").toString().trim();
            featureClass = new VPFFeatureClass(this, featureClassName,
                    pathName, namespace);
            featureClasses.add(featureClass);
        }
    }

    /**
     * The point of this block of code is 
     * to scan the CHAR.VDT file for FACC codes.  
     * There is a one to one relationship 
     * between FACC codes and feature types, 
     * but there is a one to many relationship 
     * between feature classes 
     * and feature types/FACC codes.  
     * Since objects are stored 
     * in the file system by feature class, 
     * this mechanism allows us 
     * to separate features 
     * of different feature types 
     * in the same feature class. 
     * 
     * Some coverages do not have a CHAR.VDT file.
     * In these cases, there is a 1:1:1 relationship
     * between the coverage, feature class, 
     * and feature type.
     */
    private void discoverFeatureTypes() {
        try {
            Iterator charVDTIter = getCharVDT().readAllRows().iterator();

            while (charVDTIter.hasNext()) {
                // Figure out which featureClass owns it
                Feature row = (Feature) charVDTIter.next();
                String attr = row.getAttribute("attribute").toString().trim().toLowerCase();

                if (!ALLOWED_FCODE_ATTRIBUTES_LIST.contains(attr))
                    continue;
                
                String tableFileName = row.getAttribute("table").toString().trim();

                // We need to go through all of this
                // so that entries match what is in FCS
                String featureClassName = tableFileName.substring(0, tableFileName.indexOf("."));
                Iterator featureClassIter = featureClasses.iterator();

                while (featureClassIter.hasNext()) {
                    VPFFeatureClass featureClass = (VPFFeatureClass) featureClassIter.next();

                    if (featureClassName.equals(featureClass.getTypeName())) {
                        VPFFeatureType featureType = new VPFFeatureType(featureClass, row);
                        featureTypes.add(featureType);

                        break;
                    }
                }
            }
        } catch (IOException exc) {
            // If there is no char.vdt, 
            // we can assume there is only one feature type 
            // and only one feature class
            VPFFeatureClass coverageClass = (VPFFeatureClass) featureClasses
                .get(0);
            VPFFeatureType featureType = new VPFFeatureType(coverageClass);
            featureTypes.add(featureType);
        }
    }

    /**
     * Look for a char.vdt
     *
     * @return a TableInputStream for the char.vdt for this coverage
     *
     * @throws IOException on any IO problems, particularly not being able to find the char.vdt file
     */
    private VPFFile getCharVDT() throws IOException {
        VPFFile charvdtInputStream = null;
        String charvdtFileName = pathName + File.separator
            + CHARACTER_VALUE_DESCRIPTION_TABLE;
        charvdtInputStream = VPFFileFactory.getInstance()
                                           .getFile(charvdtFileName);

        return charvdtInputStream;
    }

    /**
     * Returns the feature classes in the coverage
     *
     * @return the feature classes in the coverage
     */
    public List getFeatureClasses() {
        return featureClasses;
    }

    /**
     * Returns the feature types for this coverage
     *
     * @return a <code>List</code> of the feature types
     */
    public List getFeatureTypes() {
        return featureTypes;
    }

    /**
     * Returns the owning Module (When refactored this will be the
     * VPFDataSource))
     *
     * @return the owning Module
     */
    public VPFLibrary getLibrary() {
        return library;
    }

    /**
     * Returns the coverage name
     *
     * @return the coverage name
     */
    public String getName() {
        String result = pathName.substring(pathName.lastIndexOf(
                    File.separator) + 1);

        return result;
    }

    /**
     * Returns the name of the path of the directory containing the coverage
     *
     * @return the path of the directory containing the coverage
     */
    public String getPathName() {
        return pathName;
    }
    /**
     * @return Returns the topologyLevel.
     */
    public int getTopologyLevel() {
        return topologyLevel;
    }
    /*
     *  (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return "VPF Coverage " + getName() + ". " + description + "\n" + 
               "Topology level " + topologyLevel;
    }
}
