/**
 * Copyright (c) 2002-2006 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 */
package org.eclipse.emf.mapping.command;


import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.mapping.Mapping;
import org.eclipse.emf.mapping.MappingPlugin;
import org.eclipse.emf.mapping.MappingRoot;
import org.eclipse.emf.mapping.domain.MappingDomain;


public abstract class MatchMappingCommand extends CompoundCommand
{
  /**
   * This keeps track of the mapping domain in which the command operates.
   */
  protected MappingDomain domain;

  /**
   * This is the mapping that is being recursively matched.
   */
  protected Mapping mapping;

  /**
   * This is the collection of inputs that have been matched by this command
   */
  protected Collection<?> mappedInputs;

  /**
   * This caches the label.
   */
  protected static final String LABEL = MappingPlugin.getPlugin().getString("_UI_MatchMappingCommand_label");

  /**
   * This cachaes the description.
   */
  protected static final String DESCRIPTION = MappingPlugin.getPlugin().getString("_UI_MatchMappingCommand_description");

  public MatchMappingCommand(MappingDomain domain, Mapping mapping)
  {
    super(LABEL, DESCRIPTION);

    this.domain = domain;
    this.mapping = mapping;
  }

  @Override
  protected boolean prepare() 
  {
    if (domain != null && mapping != null)
    {
      Collection<Object> inputChildren = new ArrayList<Object>(); 
      for (Object input : mapping.getSenders())
      {
        inputChildren.addAll(domain.getChildren(input));
      }

      Collection<Object> outputChildren = new ArrayList<Object>(); 
      for (Object output : mapping.getReceivers())
      {
        outputChildren.addAll(domain.getChildren(output));
      }

      matchChildren(inputChildren, outputChildren);
    }

    // We have done our preparation. Now ask the super to validate.
    //
    boolean result = super.prepare();
    return result;
  }

  protected void matchChildren(Collection<?> inputChildren, Collection<?> outputChildren)
  {
    ArrayList<Object> newMappedInputs = new ArrayList<Object>();
    mappedInputs = newMappedInputs;
    MappingRoot mappingRoot = domain.getMappingRoot();
    boolean multipleMatchesAllowed = (domain.getMappingEnablementFlags() & MappingDomain.ENABLE_MULTIPLE_INPUT_MAPPINGS) != 0;

    for (Object childOutput : outputChildren)
    {
      if (mappingRoot.getMappings(childOutput).isEmpty())
      {
        Collection<Object> mappedObjects = new ArrayList<Object>();

        for (Object childInput : inputChildren)
        {
          boolean canCreateMapping = 
            multipleMatchesAllowed || (!mappedInputs.contains(childInput) && mappingRoot.getMappings(childInput).isEmpty());
          if (canCreateMapping && match(childInput, childOutput, mappedObjects))
          {
            break;
          }
        }

        if (!mappedObjects.isEmpty())
        {
          newMappedInputs.addAll(mappedObjects);
          mappedObjects.add(childOutput);

          Command mapCommand = CreateMappingCommand.create(domain, mappedObjects);
          appendIfCanExecute(mapCommand);
        }
      }
    }
  }

  protected abstract boolean match(Object inputObject, Object outputObject, Collection<Object> mappedObjects);

  /**
   * This gives an abbreviated name using this object's own class' name, without package qualification,
   * followed by a space separated list of <tt>field:value</tt> pairs.
   */
  @Override
  public String toString()
  {
    StringBuffer result = new StringBuffer(super.toString());
    result.append(" (domain: " + domain + ")");
    result.append(" (mapping: " + mapping + ")");

    return result.toString();
  }
}
