/*
 * RHQ Management Platform
 * Copyright (C) 2010 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program 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 General Public License for more details.
 * * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.bundle.ant.type;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;

import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.configuration.definition.PropertySimpleType;

/**
 * An Ant type that defines a basic property that the user provides as input for deployment of a bundle. Typically, the
 * property will be substituted into configuration files from the bundle during deployment - see ReplaceTask.
 *
 * If the deployment script is invoked from the GUI, the user will be prompted for values for any input properties that
 * are defined via this type. If the script is invoked from the command line, the properties must be passed using the -D
 * and/or -propertyfile options.
 *
 * @author Ian Springer
 */
public class InputPropertyType extends AbstractBundleType {
    private String name;
    private String description;
    private boolean required = true;
    private String defaultValue;
    private String type = PropertySimpleType.STRING.xmlName();

    public void init() throws BuildException {
        validateAttributes();

        ConfigurationDefinition configDef = getProject().getConfigurationDefinition();
        PropertySimpleType propType = PropertySimpleType.fromXmlName(this.type);
        PropertyDefinitionSimple propDef = new PropertyDefinitionSimple(this.name, this.description, this.required,
            propType);
        propDef.setDefaultValue(this.defaultValue);
        configDef.put(propDef);

        String value = getProject().getProperty(this.name);
        if (value == null) {
            value = this.defaultValue;
            getProject().setProperty(this.name, value);
        }

        boolean parseOnly = getProject().isParseOnly();
        if (!parseOnly) {
            if (value == null && this.required) {
                throw new BuildException("No value was specified for required input property '" + this.name
                    + "', and no default is defined for the property.");
            }
            validateValue(value, propType);
            String valueString = (value != null) ? "'" + value + "'" : "<null>";
            log("Initializing input property '" + this.name + "' with value " + valueString + "...");
        }

        Configuration config = getProject().getConfiguration();
        PropertySimple prop = new PropertySimple(this.name, value);
        config.put(prop);

        return;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDefaultValue() {
        return defaultValue;
    }

    public void setDefaultValue(String defaultValue) {
        this.defaultValue = defaultValue;
    }

    public Boolean getRequired() {
        return required;
    }

    public void setRequired(Boolean required) {
        this.required = required;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    /**
     * Ensure we have a consistent and legal set of attributes, and set
     * any internal flags necessary based on different combinations
     * of attributes.
     *
     * @throws org.apache.tools.ant.BuildException if an error occurs
     */
    protected void validateAttributes() throws BuildException {
        if (this.name == null) {
            throw new BuildException("The 'name' attribute is required.");
        }
        if (this.name.length() == 0) {
            throw new BuildException("The 'name' attribute must have a non-empty value.");
        }
        PropertySimpleType propType;
        try {
            propType = PropertySimpleType.fromXmlName(this.type);
        } catch (IllegalArgumentException e) {
            throw new BuildException("Illegal value for 'type' attribute: " + this.type);
        }
        if (this.defaultValue == null) {
            if (!this.required) {
                log("No default value was specified for optional input property '" + this.name + "'.", Project.MSG_WARN);
            }
        } else {
            // Make sure the default value is valid according to the property's type.
            try {
                validateValue(this.defaultValue, propType);
            } catch (RuntimeException e) {
                throw new BuildException("Default value '" + this.defaultValue
                    + "' is not valid according to 'type' attribute: " + this.type, e);
            }
        }
    }

    private void validateValue(String value, PropertySimpleType propType) {
        if (value != null) {
            try {
                switch (propType) {
                case BOOLEAN:
                    if (!value.equals(Boolean.TRUE.toString()) && !value.equals(Boolean.FALSE.toString())) {
                        throw new RuntimeException(
                            "Illegal value for boolean property - value must be 'true' or 'false'." + value);
                    }
                    break;
                case DOUBLE:
                    Double.valueOf(value);
                    break;
                case FLOAT:
                    Float.valueOf(value);
                    break;
                case INTEGER:
                    Integer.valueOf(value);
                    break;
                case LONG:
                    Long.valueOf(value);
                    break;
                }
            } catch (RuntimeException e) {
                throw new BuildException("'" + value + "' is not a legal value for input property '" + this.name
                    + "', which has type '" + this.type + "'.", e);
            }
        }
    }
}