/*
 * This file is part of the DITA Open Toolkit project hosted on
 * Sourceforge.net. See the accompanying license.txt file for
 * applicable licenses.
 */

/*
 * (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved.
 */
package org.dita.dost.platform;

import static org.dita.dost.util.Constants.*;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;

import org.dita.dost.util.FileUtils;

/**
 * Collection of features.
 * @author Zhang, Yuan Peng
 */
final class Features {

    private String id;
    private final File location;
    private final File ditaDir;
    private final Map<String, ExtensionPoint> extensionPoints;
    private final Hashtable<String,String> featureTable;
    private final List<PluginRequirement> requireList;
    private final Hashtable<String,String> metaTable;
    private final List<String> templateList;

    /**
     * Default constructor.
     * 
     * @deprecated use {@link #Features(File, File)} instead
     */
    @Deprecated
    public Features() {
        this(null, null);
    }

    /**
     * Constructor init location.
     * @param location location
     * @param ditaDir base directory
     */
    public Features(final File location, final File ditaDir) {
        this.location = location;
        this.ditaDir = ditaDir;
        extensionPoints= new HashMap<String, ExtensionPoint>();
        featureTable = new Hashtable<String,String>(INT_16);
        requireList = new ArrayList<PluginRequirement>(INT_8);
        metaTable = new Hashtable<String,String>(INT_16);
        templateList = new ArrayList<String>(INT_8);
    }

    /**
     * Return the feature location.
     * @return location
     */
    public File getLocation(){
        return location;
    }

    /**
     * Get DITA-OT base directory
     * @return base directory
     */
    public File getDitaDir() {
        return ditaDir;
    }

    void setPluginId(final String id) {
        this.id = id;
    }

    String getPluginId() {
        return id;
    }

    Map<String, ExtensionPoint> getExtensionPoints() {
        return Collections.unmodifiableMap(extensionPoints);
    }

    /**
     * Return the feature name by id.
     * @param id feature id
     * @return feature name
     */
    public String getFeature(final String id){
        return featureTable.get(id);
    }

    /**
     * Return the set of all features.
     * @return features
     */
    public Set<Map.Entry<String,String>> getAllFeatures(){
        return featureTable.entrySet();
    }

    void addExtensionPoint(final ExtensionPoint extensionPoint) {
        extensionPoints.put(extensionPoint.id, extensionPoint);
    }

    /**
     * Add feature to the feature table.
     * @param id feature id
     * @param value feature value
     * @param type feature type, may be {@code null}
     * @deprecated use {@link #addFeature(String, Attributes)} instead
     */
    @Deprecated
    public void addFeature(final String id, final String value, final String type) {
        final AttributesImpl atts = new AttributesImpl();
        atts.addAttribute("", "value", "value", "CDATA", value);
        if (type != null) {
            atts.addAttribute("", "type", "type", "CDATA", type);
        }
        addFeature(id, atts);
    }

    /**
     * Add feature to the feature table.
     * @param id feature id
     * @param attributes configuration element attributes
     */
    public final void addFeature(final String id, final Attributes attributes){
        boolean isFile;
        String value = attributes.getValue("file");
        if (value != null) {
            isFile = true;
        } else {
            value = attributes.getValue("value");
            isFile = "file".equals(attributes.getValue("type"));
        }
        final StringTokenizer valueTokenizer = new StringTokenizer(value, Integrator.FEAT_VALUE_SEPARATOR);
        final StringBuffer valueBuffer = new StringBuffer();
        if (featureTable.containsKey(id)) {
            valueBuffer.append(featureTable.get(id));
            valueBuffer.append(Integrator.FEAT_VALUE_SEPARATOR);
        }
        while(valueTokenizer.hasMoreElements()){
            final String valueElement = valueTokenizer.nextToken();
            if(valueElement!=null && valueElement.trim().length() != 0){
                if(isFile && !FileUtils.isAbsolutePath(valueElement)){
                    valueBuffer.append(location).append(File.separatorChar);
                }
                valueBuffer.append(valueElement.trim());
                if(valueTokenizer.hasMoreElements()){
                    valueBuffer.append(Integrator.FEAT_VALUE_SEPARATOR);
                }
            }
        }
        featureTable.put(id, valueBuffer.toString());
    }

    /**
     * Add the required feature id.
     * @param id feature id
     */
    public void addRequire(final String id){
        final PluginRequirement requirement = new PluginRequirement();
        requirement.addPlugins(id);
        requireList.add(requirement);
    }

    /**
     * Add the required feature id.
     * @param id feature id
     * @param importance importance
     */
    public void addRequire(final String id, final String importance){
        final PluginRequirement requirement = new PluginRequirement();
        requirement.addPlugins(id);
        if (importance != null) {
            requirement.setRequired(importance.equals("required"));
        }
        requireList.add(requirement);
    }

    /**
     * Get the iterator of required list.
     * @return iterator
     */
    public Iterator<PluginRequirement> getRequireListIter(){
        return requireList.iterator();
    }

    /**
     * Add meta info to meta table.
     * @param type type
     * @param value value
     */
    public void addMeta(final String type, final String value){
        metaTable.put(type, value);
    }

    /**
     * Return meat info specifying type.
     * @param type type
     * @return meat info
     */
    public String getMeta(final String type){
        return metaTable.get(type);
    }

    /**
     * Add a template.
     * @param file file name
     */
    public void addTemplate(final String file){
        templateList.add(file);
    }
    /**
     * get all templates.
     * @return templates list
     */
    public List<String> getAllTemplates(){
        return templateList;
    }
}
