/*
 * Copyright 2006 Werner Guttmann, Ralf Joachim
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.exolab.castor.builder.conflictresolution;

import java.util.Enumeration;

import org.exolab.castor.builder.SGStateInfo;
import org.exolab.castor.builder.binding.XPathHelper;
import org.exolab.castor.builder.info.ClassInfo;
import org.exolab.castor.builder.info.nature.XMLInfoNature;
import org.exolab.castor.util.dialog.ConsoleDialog;
import org.exolab.castor.xml.schema.Annotated;
import org.exolab.castor.xml.schema.SchemaNames;
import org.exolab.javasource.JClass;

/**
 * An implementation of {@link ClassNameCRStrategy} that reports any conflict
 * notifications to a console dialog, asking the user whether to stop code
 * generation (as the conflict is not acceptable), or whether to proceed by
 * overwriting an already existing class.
 *
 * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
 * @since 1.1
 */
public final class WarningViaDialogClassNameCRStrategy
extends BaseClassNameCRStrategy implements ClassNameCRStrategy {

    /**
     * Name of this strategy.
     */
    public static final String NAME = "warnViaConsoleDialog";

    /**
     * The {@link ConsoleDialog} instance to use for output.
     */
    private ConsoleDialog _dialog;

    /**
     * Creates an instance of this name conflict resolution strategy, that will
     * use the specified {@link ConsoleDialog} instance to emit warnings to the
     * user and ask about an approach to deal with them.
     */
    public WarningViaDialogClassNameCRStrategy() {
        // Nothing to do.
    }

    /**
     * Handle a class name conflict between newClassInfo and conflict.
     *
     * @param state SourceGeneration state
     * @param newClassInfo ClassInfo for the new class
     * @param conflict JClass for the existing class
     * @return the provided source generator state, as modified by the strategy
     * @see org.exolab.castor.builder.conflictresolution.ClassNameCRStrategy
     *      #dealWithClassNameConflict(org.exolab.castor.builder.SGStateInfo,
     *      org.exolab.castor.builder.info.ClassInfo,
     *      org.exolab.javasource.JClass)
     */
    public SGStateInfo dealWithClassNameConflict(final SGStateInfo state,
            final ClassInfo newClassInfo, final JClass conflict) {
        if (!state.getSuppressNonFatalWarnings()) {
            // -- if the ClassInfo are equal, we can just return
            ClassInfo oldClassInfo = state.resolve(conflict);
            if (oldClassInfo == newClassInfo) {
                return state;
            }

            // -- Find the Schema structures that are conflicting
            Annotated a1 = null;
            Annotated a2 = null;

            // Loop until we exhaust the Enumeration or until we have found both
            Enumeration enumeration = state.keys();
            while (enumeration.hasMoreElements() && (a1 == null || a2 == null)) {
                Object key = enumeration.nextElement();
                if (!(key instanceof Annotated)) {
                    continue;
                }

                ClassInfo cInfo = state.resolve(key);
                if (newClassInfo == cInfo) {
                    a1 = (Annotated) key;
                } else if (oldClassInfo == cInfo) {
                    a2 = (Annotated) key;
                }
            }

            StringBuilder error = new StringBuilder(64);
            error.append("Warning: A class name generation conflict has occured between ");
            if (a1 != null) {
                error.append(SchemaNames.getStructureName(a1));
                error.append(" '");
                error.append(XPathHelper.getSchemaLocation(a1));
            } else {
                if (newClassInfo.hasNature(XMLInfoNature.class.getName())) {
                    XMLInfoNature xmlNature = new XMLInfoNature(newClassInfo);
                    error.append(xmlNature.getNodeTypeName());
                    error.append(" '");
                    error.append(xmlNature.getNodeName());
                }
            }
            error.append("' and ");
            if (a2 != null) {
                error.append(SchemaNames.getStructureName(a2));
                error.append(" '");
                error.append(XPathHelper.getSchemaLocation(a2));
            } else {
                if (oldClassInfo.hasNature(XMLInfoNature.class.getName())) {
                    XMLInfoNature xmlNature = new XMLInfoNature(oldClassInfo);
                    error.append(xmlNature.getNodeTypeName());
                    error.append(" '");
                    error.append(xmlNature.getNodeName());
                }
            }
            error.append("'. Please use a Binding file to solve this problem.");
            error.append("Continue anyway [not recommended] ");

            char ch = _dialog
                    .confirm(error.toString(), "yn", "y = yes, n = no");
            if (ch == 'n') {
                state.setStatusCode(SGStateInfo.STOP_STATUS);
            }
        }
        return state;
    }

    /**
     * Returns the name of the strategy.
     * @return the name of the strategy.
     * @see org.exolab.castor.builder.conflictresolution.ClassNameCRStrategy#getName()
     */
    public String getName() {
        return NAME;
    }

    /**
     * Sets the console dialog to use with this strategy.
     *
     * @param dialog the console dialog to use with this strategy.
     * @see org.exolab.castor.builder.conflictresolution.ClassNameCRStrategy#
     *      setConsoleDialog(org.exolab.castor.util.dialog.ConsoleDialog)
     */
    public void setConsoleDialog(final ConsoleDialog dialog) {
        this._dialog = dialog;
    }

    /**
     * Presents the user with a console dialog, asking for confirmation whether
     * an existing file should be overwritten (or not).
     *
     * @param filename the filename to potentially overwrite.
     * @return whether or not the file should be overwritten.
     *
     * @see org.exolab.castor.builder.conflictresolution.ClassNameCRStrategy
     *      #dealWithFileOverwrite(java.lang.String, boolean)
     */
    public boolean dealWithFileOverwrite(final String filename) {
        boolean allowPrinting = true;

        String message = filename + " already exists. overwrite";
        char ch = _dialog.confirm(message, "yna", "y = yes, n = no, a = all");
        switch (ch) {
            case 'a':
                getSingleClassGenerator().setPromptForOverwrite(false);
                allowPrinting = true;
                break;
            case 'y':
                allowPrinting = true;
                break;
            default:
                allowPrinting = false;
                break;
        }
        return allowPrinting;
    }

    /**
     * Returns the {@link ConsoleDialog} instance in use.
     * @return the {@link ConsoleDialog} used for output/feedback gathering.
     */
    protected ConsoleDialog getConsoleDialog() {
        return this._dialog;
    }

}
