/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.app.xmlui.aspect.submission.submit;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Locale;

import javax.servlet.ServletException;

import org.dspace.app.util.DCInput;
import org.dspace.app.util.DCInputSet;
import org.dspace.app.util.DCInputsReader;
import org.dspace.app.util.DCInputsReaderException;
import org.dspace.app.xmlui.utils.UIException;
import org.dspace.app.xmlui.aspect.submission.AbstractSubmissionStep;
import org.dspace.app.xmlui.aspect.submission.FlowUtils;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.Body;
import org.dspace.app.xmlui.wing.element.CheckBox;
import org.dspace.app.xmlui.wing.element.Composite;
import org.dspace.app.xmlui.wing.element.Division;
import org.dspace.app.xmlui.wing.element.Field;
import org.dspace.app.xmlui.wing.element.Instance;
import org.dspace.app.xmlui.wing.element.List;
import org.dspace.app.xmlui.wing.element.PageMeta;
import org.dspace.app.xmlui.wing.element.Params;
import org.dspace.app.xmlui.wing.element.Radio;
import org.dspace.app.xmlui.wing.element.Select;
import org.dspace.app.xmlui.wing.element.Text;
import org.dspace.app.xmlui.wing.element.TextArea;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.DCDate;
import org.dspace.content.DCPersonName;
import org.dspace.content.DCSeriesNumber;
import org.dspace.content.DCValue;
import org.dspace.content.Item;
import org.dspace.content.authority.MetadataAuthorityManager;
import org.dspace.content.authority.ChoiceAuthorityManager;
import org.dspace.content.authority.Choice;
import org.dspace.content.authority.Choices;

import org.xml.sax.SAXException;

/**
 * This is a step of the item submission processes. The describe step queries
 * the user for various metadata items about the item. For the most part all the
 * questions queried are completely configurable via the input-sets.xml file.
 * This system allows for multiple pages to be defined so this step is different
 * from all other stages in that it may represent multiple stages within the
 * submission processes.
 *
 * @author Scott Phillips
 * @author Tim Donohue (updated for Configurable Submission)
 */
public class DescribeStep extends AbstractSubmissionStep
{
        /** Language Strings **/
    protected static final Message T_head =
        message("xmlui.Submission.submit.DescribeStep.head");
    protected static final Message T_unknown_field=
        message("xmlui.Submission.submit.DescribeStep.unknown_field");
    protected static final Message T_required_field=
        message("xmlui.Submission.submit.DescribeStep.required_field");
    protected static final Message T_last_name_help=
        message("xmlui.Submission.submit.DescribeStep.last_name_help");
    protected static final Message T_first_name_help=
        message("xmlui.Submission.submit.DescribeStep.first_name_help");
    protected static final Message T_year=
        message("xmlui.Submission.submit.DescribeStep.year");
    protected static final Message T_month=
        message("xmlui.Submission.submit.DescribeStep.month");
    protected static final Message T_day=
        message("xmlui.Submission.submit.DescribeStep.day");
    protected static final Message T_series_name=
        message("xmlui.Submission.submit.DescribeStep.series_name");
    protected static final Message T_report_no=
        message("xmlui.Submission.submit.DescribeStep.report_no");
        
        /**
     * A shared resource of the inputs reader. The 'inputs' are the
     * questions we ask the user to describe an item during the
     * submission process. The reader is a utility class to read
     * that configuration file.
     */
    private static DCInputsReader INPUTS_READER = null;
    
    /**
     * Ensure that the inputs reader has been initialized, this method may be
     * called multiple times with no ill-effect.
     */
    private static void initializeInputsReader() throws DCInputsReaderException
    {
        if (INPUTS_READER == null)
        {
            INPUTS_READER = new DCInputsReader();
        }
    }
    
    /**
     * Return the inputs reader. Note, the reader must have been
     * initialized before the reader can be accessed.
     *
     * @return The input reader.
     */
    private static DCInputsReader getInputsReader()
    {
        return INPUTS_READER;
    }
    

        /**
         * Establish our required parameters, abstractStep will enforce these.
         */
        public DescribeStep() throws ServletException
        {
                this.requireSubmission = true;
                this.requireStep = true;
                
                //Ensure that the InputsReader is initialized.
                try
                {
                    initializeInputsReader();
                }
                catch (DCInputsReaderException e)
                {
                    throw new ServletException(e);
                }
        }
        
        public void addPageMeta(PageMeta pageMeta) throws SAXException, WingException,
        UIException, SQLException, IOException, AuthorizeException
        {
            super.addPageMeta(pageMeta);
            int collectionID = submission.getCollection().getID();
            pageMeta.addMetadata("choice", "collection").addContent(String.valueOf(collectionID));

            String jumpTo = submissionInfo.getJumpToField();
            if (jumpTo != null)
            {
                pageMeta.addMetadata("page", "jumpTo").addContent(jumpTo);
            }
        }

        public void addBody(Body body) throws SAXException, WingException,
        UIException, SQLException, IOException, AuthorizeException
        {
                // Obtain the inputs (i.e. metadata fields we are going to display)
                Item item = submission.getItem();
                Collection collection = submission.getCollection();
                String actionURL = contextPath + "/handle/"+collection.getHandle() + "/submit/" + knot.getId() + ".continue";

                DCInputSet inputSet;
                DCInput[] inputs;
                try
                {
                        inputSet = getInputsReader().getInputs(submission.getCollection().getHandle());
                        inputs = inputSet.getPageRows(getPage()-1, submission.hasMultipleTitles(), submission.isPublishedBefore());
                }
                catch (DCInputsReaderException se)
                {
                        throw new UIException(se);
                }

                Division div = body.addInteractiveDivision("submit-describe",actionURL,Division.METHOD_POST,"primary submission");
                div.setHead(T_submission_head);
                addSubmissionProgressList(div);

                List form = div.addList("submit-describe",List.TYPE_FORM);
                form.setHead(T_head);

                // Iterate over all inputs and add it to the form.
                for(DCInput dcInput : inputs)
                {
                    String scope = submissionInfo.isInWorkflow() ? DCInput.WORKFLOW_SCOPE : DCInput.SUBMISSION_SCOPE;
                    boolean readonly = dcInput.isReadOnly(scope);
                    
                    // If the input is invisible in this scope, then skip it.
                        if (!dcInput.isVisible(scope) && !readonly)
                        {
                            continue;
                        }
                        
                        String schema = dcInput.getSchema();
                        String element = dcInput.getElement();
                        String qualifier = dcInput.getQualifier();

                        DCValue[] dcValues = item.getMetadata(schema, element, qualifier, Item.ANY);

                        String fieldName = FlowUtils.getFieldName(dcInput);
                        String inputType = dcInput.getInputType();

                        // if this field is configured as choice control and its
                        // presentation format is SELECT, render it as select field:
                        String fieldKey = MetadataAuthorityManager.makeFieldKey(schema, element, qualifier);
                        ChoiceAuthorityManager cmgr = ChoiceAuthorityManager.getManager();
                        if (cmgr.isChoicesConfigured(fieldKey) &&
                            Params.PRESENTATION_SELECT.equals(cmgr.getPresentation(fieldKey)))
                        {
                                renderChoiceSelectField(form, fieldName, collection, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("name"))
                        {
                                renderNameField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("date"))
                        {
                                renderDateField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("series"))
                        {
                                renderSeriesField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("twobox"))
                        {
                                // We don't have a twobox field, instead it's just a
                                // one box field that the theme can render in two columns.
                                renderOneboxField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("qualdrop_value"))
                        {
                                // Determine the real field's values. Since the qualifier is
                                // selected we need to search through all the metadata and see
                                // if any match for another field, if not we assume that this field
                                // should handle it.
                                DCValue[] unfiltered = item.getMetadata(dcInput.getSchema(), dcInput.getElement(), Item.ANY, Item.ANY);
                                ArrayList<DCValue> filtered = new ArrayList<DCValue>();
                                for (DCValue dcValue : unfiltered)
                                {
                                        String unfilteredFieldName = dcValue.element + "." + dcValue.qualifier;
                                        if ( ! inputSet.isFieldPresent(unfilteredFieldName) )
                                        {
                                                filtered.add( dcValue );
                                        }
                                }
                                
                                renderQualdropField(form, fieldName, dcInput, filtered.toArray(new DCValue[filtered.size()]), readonly);
                        }
                        else if (inputType.equals("textarea"))
                        {
                                renderTextArea(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("dropdown"))
                        {
                                renderDropdownField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("list"))
                        {
                                renderSelectFromListField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else if (inputType.equals("onebox"))
                        {
                                renderOneboxField(form, fieldName, dcInput, dcValues, readonly);
                        }
                        else
                        {
                                form.addItem(T_unknown_field);
                        }
                }

                // add standard control/paging buttons
        addControlButtons(form);
        }

    /**
     * Each submission step must define its own information to be reviewed
     * during the final Review/Verify Step in the submission process.
     * <P>
     * The information to review should be tacked onto the passed in
     * List object.
     * <P>
     * NOTE: To remain consistent across all Steps, you should first
     * add a sub-List object (with this step's name as the heading),
     * by using a call to reviewList.addList().   This sublist is
     * the list you return from this method!
     *
     * @param reviewList
     *      The List to which all reviewable information should be added
     * @return
     *      The new sub-List object created by this step, which contains
     *      all the reviewable information.  If this step has nothing to
     *      review, then return null!
     */
    public List addReviewSection(List reviewList) throws SAXException,
        WingException, UIException, SQLException, IOException,
        AuthorizeException
    {
        //Create a new list section for this step (and set its heading)
        List describeSection = reviewList.addList("submit-review-" + this.stepAndPage, List.TYPE_FORM);
        describeSection.setHead(T_head);
        
        //Review the values assigned to all inputs
        //on this page of the Describe step.
        DCInputSet inputSet = null;
        try
        {
            inputSet = getInputsReader().getInputs(submission.getCollection().getHandle());
        }
        catch (DCInputsReaderException se)
        {
            throw new UIException(se);
        }
        
        MetadataAuthorityManager mam = MetadataAuthorityManager.getManager();
        DCInput[] inputs = inputSet.getPageRows(getPage()-1, submission.hasMultipleTitles(), submission.isPublishedBefore());

        for (DCInput input : inputs)
        {
            // If the input is invisible in this scope, then skip it.
            String scope = submissionInfo.isInWorkflow() ? DCInput.WORKFLOW_SCOPE : DCInput.SUBMISSION_SCOPE;
            if (!input.isVisible(scope) && !input.isReadOnly(scope))
            {
                continue;
            }

            String inputType = input.getInputType();
            String pairsName = input.getPairsType();
            DCValue[] values;

            if (inputType.equals("qualdrop_value"))
            {
                values = submission.getItem().getMetadata(input.getSchema(), input.getElement(), Item.ANY, Item.ANY);
            }
            else
            {
                values = submission.getItem().getMetadata(input.getSchema(), input.getElement(), input.getQualifier(), Item.ANY);
            }

            if (values != null && values.length > 0)
            {
                for (DCValue value : values)
                {
                    String displayValue = null;
                    if (inputType.equals("date"))
                    {
                        DCDate date = new DCDate(value.value);
                        displayValue = date.toString();
                    }
                    else if (inputType.equals("dropdown"))
                    {
                        displayValue = input.getDisplayString(pairsName,value.value);
                    }
                    else if (inputType.equals("qualdrop_value"))
                    {
                        String qualifier = value.qualifier;
                        String displayQual = input.getDisplayString(pairsName,qualifier);
                        if (displayQual!=null && displayQual.length()>0)
                        {
                            displayValue = displayQual + ":" + value.value;
                        }
                    }
                    else
                    {
                        displayValue = value.value;
                    }

                    //Only display this field if we have a value to display
                    if (displayValue!=null && displayValue.length()>0)
                    {

                        describeSection.addLabel(input.getLabel());
                        if (mam.isAuthorityControlled(value.schema, value.element, value.qualifier))
                        {
                            String confidence = (value.authority != null && value.authority.length() > 0) ?
                                Choices.getConfidenceText(value.confidence).toLowerCase() :
                                "blank";
                            org.dspace.app.xmlui.wing.element.Item authItem =
                                describeSection.addItem("submit-review-field-with-authority", "ds-authority-confidence cf-"+confidence);
                            authItem.addContent(displayValue);
                        }
                        else
                        {
                            describeSection.addItem(displayValue);
                        }
                    }
                } // For each DCValue
            } // If values exist
        }// For each input
        
        //return this new "describe" section
        return describeSection;
    }
    
        
        /**
         * Render a Name field to the DRI document. The name field consists of two
         * text fields, one for the last name and the other for a first name (plus
         * all other names).
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderNameField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                // The name field is a composite field containing two text fields, one
                // for first name the other for last name.
                Composite fullName = form.addItem().addComposite(fieldName, "submit-name");
                Text lastName = fullName.addText(fieldName+"_last");
                Text firstName = fullName.addText(fieldName+"_first");

                // Setup the full name
                fullName.setLabel(dcInput.getLabel());
                fullName.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    fullName.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        fullName.addError(dcInput.getWarning());
                    }
                    else
                    {
                        fullName.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() && !readonly)
                {
                    fullName.enableAddOperation();
                }
                if ((dcInput.isRepeatable() || dcValues.length > 1)  && !readonly)
                {
                    fullName.enableDeleteOperation();
                }
                String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier());
                boolean isAuthorityControlled = MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey);
                if (isAuthorityControlled)
                {
                    fullName.setAuthorityControlled();
                    fullName.setAuthorityRequired(MetadataAuthorityManager.getManager().isAuthorityRequired(fieldKey));
                }
                if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey))
                {
                    fullName.setChoices(fieldKey);
                    fullName.setChoicesPresentation(ChoiceAuthorityManager.getManager().getPresentation(fieldKey));
                    fullName.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey));
                }

                // Setup the first and last name
                lastName.setLabel(T_last_name_help);
                firstName.setLabel(T_first_name_help);
                
                if (readonly)
                {
                    lastName.setDisabled();
                    firstName.setDisabled();
                    fullName.setDisabled();
                }
                
                // Setup the field's values
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        for (DCValue dcValue : dcValues)
                        {
                                DCPersonName dpn = new DCPersonName(dcValue.value);
                
                                lastName.addInstance().setValue(dpn.getLastName());
                                firstName.addInstance().setValue(dpn.getFirstNames());
                                Instance fi = fullName.addInstance();
                                fi.setValue(dcValue.value);
                                if (isAuthorityControlled)
                                {
                                    if (dcValue.authority == null || dcValue.authority.equals(""))
                                    {
                                        fi.setAuthorityValue("", "blank");
                                    }
                                    else
                                    {
                                        fi.setAuthorityValue(dcValue.authority, Choices.getConfidenceText(dcValue.confidence));
                                    }
                        }
                }
                }
                else if (dcValues.length == 1)
                {
                        DCPersonName dpn = new DCPersonName(dcValues[0].value);
                
                        lastName.setValue(dpn.getLastName());
                        firstName.setValue(dpn.getFirstNames());
                        if (isAuthorityControlled)
                        {
                            if (dcValues[0].authority == null || dcValues[0].authority.equals(""))
                            {
                                lastName.setAuthorityValue("", "blank");
                            }
                            else
                            {
                                lastName.setAuthorityValue(dcValues[0].authority, Choices.getConfidenceText(dcValues[0].confidence));
                            }
                }
        }
        }
        
        /**
         * Render a date field to the DRI document. The date field consists of
         * three component fields, a 4 character text field for the year, a select
         * box for the month, and a 2 character text field for the day.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderDateField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                // The date field consists of three primitive fields: a text field
                // for the year, followed by a select box of the months, follewed
                // by a text box for the day.
                Composite fullDate = form.addItem().addComposite(fieldName, "submit-date");
                Text year = fullDate.addText(fieldName+"_year");
                Select month = fullDate.addSelect(fieldName+"_month");
                Text day = fullDate.addText(fieldName+"_day");

                // Set up the full field
                fullDate.setLabel(dcInput.getLabel());
                fullDate.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    fullDate.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        fullDate.addError(dcInput.getWarning());
                    }
                    else
                    {
                        fullDate.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() && !readonly)
                {
                    fullDate.enableAddOperation();
                }
                if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly)
                {
                    fullDate.enableDeleteOperation();
                }

                if (readonly)
                {
                    year.setDisabled();
                    month.setDisabled();
                    day.setDisabled();
                }
                
                // Setup the year field
                year.setLabel(T_year);
                year.setSize(4,4);

                // Setup the month field
                month.setLabel(T_month);
                month.addOption(0,"");
                for (int i = 1; i < 13; i++)
                {
                        month.addOption(i,org.dspace.content.DCDate.getMonthName(i,Locale.getDefault()));
                }

                // Setup the day field
                day.setLabel(T_day);
                day.setSize(2,2);
                
                // Setup the field's values
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        for (DCValue dcValue : dcValues)
                        {
                                DCDate dcDate = new DCDate(dcValue.value);

                                year.addInstance().setValue(String.valueOf(dcDate.getYear()));
                                month.addInstance().setOptionSelected(dcDate.getMonth());
                                day.addInstance().setValue(String.valueOf(dcDate.getDay()));
                                fullDate.addInstance().setValue(dcDate.toString());
                        }
                }
                else if (dcValues.length == 1)
                {
                        DCDate dcDate = new DCDate(dcValues[0].value);

                        year.setValue(String.valueOf(dcDate.getYear()));
                        month.setOptionSelected(dcDate.getMonth());
                        
                        // Check if the day field is not specified, if so then just
                        // put a blank value in instead of the wiered looking -1.
                        if (dcDate.getDay() == -1)
                        {
                            day.setValue("");
                        }
                        else
                        {
                            day.setValue(String.valueOf(dcDate.getDay()));
                        }
                }
        }
        
        /**
         * Render a series field to the DRI document. The series field conist of
         * two component text fields. When interpreted each of these fields are
         * combined back together to be a single value joined together by a
         * semicolen. The primary use case is for the journal or report number
         * the left hand side is the journal and the right hand side in a
         * unique number within the journal.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderSeriesField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                // The seiries field consists of two parts, a series name (text field)
                // and report or paper number (also a text field).
                Composite fullSeries = form.addItem().addComposite(fieldName,"submit-"+dcInput.getInputType());
                Text series = fullSeries.addText(fieldName+"_series");
                Text number = fullSeries.addText(fieldName+"_number");

                // Setup the full field.
                fullSeries.setLabel(dcInput.getLabel());
                fullSeries.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    fullSeries.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        fullSeries.addError(dcInput.getWarning());
                    }
                    else
                    {
                        fullSeries.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() && !readonly)
                {
                    fullSeries.enableAddOperation();
                }
                if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly)
                {
                    fullSeries.enableDeleteOperation();
                }

                series.setLabel(T_series_name);
                number.setLabel(T_report_no);

                if (readonly)
                {
                    fullSeries.setDisabled();
                    series.setDisabled();
                    number.setDisabled();
                }
                
                // Setup the field's values
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        for (DCValue dcValue : dcValues)
                        {
                                DCSeriesNumber dcSeriesNumber = new DCSeriesNumber(dcValue.value);

                                series.addInstance().setValue(dcSeriesNumber.getSeries());
                                number.addInstance().setValue(dcSeriesNumber.getNumber());
                                fullSeries.addInstance().setValue(dcSeriesNumber.toString());
                        }
                        
                }
                else if (dcValues.length == 1)
                {
                        DCSeriesNumber dcSeriesNumber = new DCSeriesNumber(dcValues[0].value);

                        series.setValue(dcSeriesNumber.getSeries());
                        number.setValue(dcSeriesNumber.getNumber());
                }
        }
        
        /**
         * Render a qualdrop field to the DRI document. Qualdrop fields are complicated,
         * widget wise they are composed of two fields, a select and text box field.
         * The select field selects the metedata's qualifier and the text box is the
         * value. This means that that there is not just one metadata element that is
         * represented so the confusing part is that the name can change.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderQualdropField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                Composite qualdrop = form.addItem().addComposite(fieldName,"submit-qualdrop");
                Select qual = qualdrop.addSelect(fieldName+"_qualifier");
                Text value = qualdrop.addText(fieldName+"_value");

                // Setup the full field.
                qualdrop.setLabel(dcInput.getLabel());
                qualdrop.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    qualdrop.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        qualdrop.addError(dcInput.getWarning());
                    }
                    else
                    {
                        qualdrop.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() && !readonly)
                {
                    qualdrop.enableAddOperation();
                }
                // Update delete based upon the filtered values.
                if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly)
                {
                    qualdrop.enableDeleteOperation();
                }
                
                if (readonly)
                {
                    qualdrop.setDisabled();
                    qual.setDisabled();
                    value.setDisabled();
                }
                
                // Setup the possible options
                @SuppressWarnings("unchecked") // This cast is correct
                java.util.List<String> pairs = dcInput.getPairs();
                for (int i = 0; i < pairs.size(); i += 2)
                {
                        String display = pairs.get(i);
                        String returnValue = pairs.get(i+1);
                        qual.addOption(returnValue,display);
                }

                // Setup the field's values
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        for (DCValue dcValue : dcValues)
                        {
                                qual.addInstance().setOptionSelected(dcValue.qualifier);
                                value.addInstance().setValue(dcValue.value);
                                qualdrop.addInstance().setValue(dcValue.qualifier + ":" + dcValue.value);
                        }
                }
                else if (dcValues.length == 1)
                {
                        qual.setOptionSelected(dcValues[0].qualifier);
                        value.setValue(dcValues[0].value);
                }
        }
        
        /**
         * Render a Text Area field to the DRI document. The text area is a simple
         * multi row and column text field.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderTextArea(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                // Plain old Textarea
                TextArea textArea = form.addItem().addTextArea(fieldName,"submit-textarea");

                // Setup the text area
                textArea.setLabel(dcInput.getLabel());
                textArea.setHelp(cleanHints(dcInput.getHints()));
                String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier());
                boolean isAuth = MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey);
                if (isAuth)
                {
                    textArea.setAuthorityControlled();
                    textArea.setAuthorityRequired(MetadataAuthorityManager.getManager().isAuthorityRequired(fieldKey));
                }
                if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey))
                {
                    textArea.setChoices(fieldKey);
                    textArea.setChoicesPresentation(ChoiceAuthorityManager.getManager().getPresentation(fieldKey));
                    textArea.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey));
                }
                if (dcInput.isRequired())
                {
                    textArea.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        textArea.addError(dcInput.getWarning());
                    }
                    else
                    {
                        textArea.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() && !readonly)
                {
                    textArea.enableAddOperation();
                }
                if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly)
                {
                    textArea.enableDeleteOperation();
                }

                if (readonly)
                {
                    textArea.setDisabled();
                }
                
                // Setup the field's values
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        for (DCValue dcValue : dcValues)
                        {
                                Instance ti = textArea.addInstance();
                                ti.setValue(dcValue.value);
                                if (isAuth)
                                {
                                    if (dcValue.authority == null || dcValue.authority.equals(""))
                                    {
                                        ti.setAuthorityValue("", "blank");
                                    }
                                    else
                                    {
                                        ti.setAuthorityValue(dcValue.authority, Choices.getConfidenceText(dcValue.confidence));
                                    }
                        }
                }
                }
                else if (dcValues.length == 1)
                {
                        textArea.setValue(dcValues[0].value);
                        if (isAuth)
                        {
                            if (dcValues[0].authority == null || dcValues[0].authority.equals(""))
                            {
                                textArea.setAuthorityValue("", "blank");
                            }
                            else
                            {
                                textArea.setAuthorityValue(dcValues[0].authority, Choices.getConfidenceText(dcValues[0].confidence));
                            }
                }
        }
        }
        
        /**
         * Render a dropdown field for a choice-controlled input of the
         * 'select' presentation  to the DRI document. The dropdown field
         * consists of an HTML select box.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderChoiceSelectField(List form, String fieldName, Collection coll, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier());
                if (MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey))
                {
                    throw new WingException("Field " + fieldKey + " has choice presentation of type \"" + Params.PRESENTATION_SELECT + "\", it may NOT be authority-controlled.");
                }

                // Plain old select list.
                Select select = form.addItem().addSelect(fieldName,"submit-select");

                //Setup the select field
                select.setLabel(dcInput.getLabel());
                select.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    select.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        select.addError(dcInput.getWarning());
                    }
                    else
                    {
                        select.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        // Use the multiple functionality from the HTML
                        // widget instead of DRI's version.
                        select.setMultiple();
                        select.setSize(6);
                }
                else
                {
                    select.setSize(1);
                }

                if (readonly)
                {
                    select.setDisabled();
                }

                Choices cs = ChoiceAuthorityManager.getManager().getMatches(fieldKey, "", coll.getID(), 0, 0, null);
                if (dcValues.length == 0)
                {
                    select.addOption(true, "", "");
                }
                for (Choice c : cs.values)
                {
                    select.addOption(c.value, c.label);
                }

                // Setup the field's pre-selected values
                for (DCValue dcValue : dcValues)
                {
                        select.setOptionSelected(dcValue.value);
                }
        }

        /**
         * Render a dropdown field to the DRI document. The dropdown field consists
         * of an HTML select box.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderDropdownField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                // Plain old select list.
                Select select = form.addItem().addSelect(fieldName,"submit-select");

                //Setup the select field
                select.setLabel(dcInput.getLabel());
                select.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    select.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        select.addError(dcInput.getWarning());
                    }
                    else
                    {
                        select.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        // Use the multiple functionality from the HTML
                        // widget instead of DRI's version.
                        select.setMultiple();
                        select.setSize(6);
                }
                
                if (readonly)
                {
                    select.setDisabled();
                }
                
                // Setup the possible options
                @SuppressWarnings("unchecked") // This cast is correct
                java.util.List<String> pairs = dcInput.getPairs();
                for (int i = 0; i < pairs.size(); i += 2)
                {
                        String display = pairs.get(i);
                        String value   = pairs.get(i+1);
                        select.addOption(value,display);
                }
                
                // Setup the field's pre-selected values
                for (DCValue dcValue : dcValues)
                {
                        select.setOptionSelected(dcValue.value);
                }
        }
        
        /**
         * Render a select-from-list field to the DRI document.
         * This field consists of either a series of checkboxes
         * (if repeatable) or a series of radio buttons (if not repeatable).
         * <P>
         * Note: This is NOT the same as a List element
         * (org.dspace.app.xmlui.wing.element.List).  It's just unfortunately
         * similarly named.
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderSelectFromListField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                Field listField = null;
                
                //if repeatable, this list of fields should be checkboxes
                if (dcInput.isRepeatable())
                {
                        listField = form.addItem().addCheckBox(fieldName);
                }
                else //otherwise this is a list of radio buttons
                {
                        listField = form.addItem().addRadio(fieldName);
                }
                
                if (readonly)
                {
                    listField.setDisabled();
                }
                
                //      Setup the field
                listField.setLabel(dcInput.getLabel());
                listField.setHelp(cleanHints(dcInput.getHints()));
                if (dcInput.isRequired())
                {
                    listField.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        listField.addError(dcInput.getWarning());
                    }
                    else
                    {
                        listField.addError(T_required_field);
                    }
                }

        
                //Setup each of the possible options
                java.util.List<String> pairs = dcInput.getPairs();
                for (int i = 0; i < pairs.size(); i += 2)
                {
                        String display = pairs.get(i);
                        String value   = pairs.get(i+1);
                        
                        if(listField instanceof CheckBox)
                        {
                                ((CheckBox)listField).addOption(value, display);
                        }
                        else if(listField instanceof Radio)
                        {
                                ((Radio)listField).addOption(value, display);
                        }
                }
                
                // Setup the field's pre-selected values
                for (DCValue dcValue : dcValues)
                {
                        if(listField instanceof CheckBox)
                        {
                                ((CheckBox)listField).setOptionSelected(dcValue.value);
                        }
                        else if(listField instanceof Radio)
                        {
                                ((Radio)listField).setOptionSelected(dcValue.value);
                        }
                }
        }
        
        /**
         * Render a simple text field to the DRI document
         *
         * @param form
         *                      The form list to add the field too
         * @param fieldName
         *                      The field's name.
         * @param dcInput
         *                      The field's input deffinition
         * @param dcValues
         *                      The field's pre-existing values.
         */
        private void renderOneboxField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException
        {
                // Both onebox and twobox consist a free form text field
                // that the user may enter any value. The difference between
                // the two is that a onebox should be rendered in one column
                // as twobox should be listed in a two column format. Since this
                // decision is not something the Aspect can effect we merely place
                // as a render hint.
                Text text = form.addItem().addText(fieldName,"submit-text");

                // Setup the select field
                text.setLabel(dcInput.getLabel());
                text.setHelp(cleanHints(dcInput.getHints()));
                String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier());
                boolean isAuth = MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey);
                if (isAuth)
                {
                    text.setAuthorityControlled();
                    text.setAuthorityRequired(MetadataAuthorityManager.getManager().isAuthorityRequired(fieldKey));
                }
                if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey))
                {
                    text.setChoices(fieldKey);
                    text.setChoicesPresentation(ChoiceAuthorityManager.getManager().getPresentation(fieldKey));
                    text.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey));
                }

                if (dcInput.isRequired())
                {
                    text.setRequired();
                }
                if (isFieldInError(fieldName))
                {
                    if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0)
                    {
                        text.addError(dcInput.getWarning());
                    }
                    else
                    {
                        text.addError(T_required_field);
                    }
                }
                if (dcInput.isRepeatable() && !readonly)
                {
                    text.enableAddOperation();
                }
                if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly)
                {
                    text.enableDeleteOperation();
                }

                if (readonly)
                {
                    text.setDisabled();
                }
                
                // Setup the field's values
                if (dcInput.isRepeatable() || dcValues.length > 1)
                {
                        for (DCValue dcValue : dcValues)
                        {
                                Instance ti = text.addInstance();
                                ti.setValue(dcValue.value);
                                if (isAuth)
                                {
                                    if (dcValue.authority == null || dcValue.authority.equals(""))
                                    {
                                        ti.setAuthorityValue("", "blank");
                                    }
                                    else
                                    {
                                        ti.setAuthorityValue(dcValue.authority, Choices.getConfidenceText(dcValue.confidence));
                                    }
                        }
                }
                }
                else if (dcValues.length == 1)
                {
                        text.setValue(dcValues[0].value);
                        if (isAuth)
                        {
                            if (dcValues[0].authority == null || dcValues[0].authority.equals(""))
                            {
                                text.setAuthorityValue("", "blank");
                            }
                            else
                            {
                                text.setAuthorityValue(dcValues[0].authority, Choices.getConfidenceText(dcValues[0].confidence));
                            }
                }
        }
        }
        
        
        
        /**
         * Check if the given fieldname is listed as being in error.
         *
         * @param fieldName
         * @return
         */
        private boolean isFieldInError(String fieldName)
        {
            return (this.errorFields.contains(fieldName));
        }
        
        /**
         * There is a problem with the way hints are handled. The class that we use to
         * read the input-forms.xml configuration will append and prepend HTML to hints.
         * This causes all sorts of confusion when inserting into the DRI page, so this
         * method will strip that extra HTML and just leave the cleaned comments.
         *
         *
         * However this method will not remove naughty or sexual innuendoes from the
         * field's hints.
         *
         *
         * @param dirtyHints HTML-ized hints
         * @return Hints without HTML.
         */
        private static final String HINT_HTML_PREFIX = "<tr><td colspan=\"4\" class=\"submitFormHelp\">";
        private static final String HINT_HTML_POSTFIX = "</td></tr>";
        private String cleanHints(String dirtyHints)
        {
                String clean = (dirtyHints!=null ? dirtyHints : "");

                if (clean.startsWith(HINT_HTML_PREFIX))
                {
                        clean = clean.substring(HINT_HTML_PREFIX.length());
                }

                if (clean.endsWith(HINT_HTML_POSTFIX))
                {
                        clean = clean.substring(0,clean.length() - HINT_HTML_POSTFIX.length());
                }

                return clean;
        }
}
