/*
 * 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;
  }

}
