package data;

import java.util.*;

/**
  * A StockFromStockCreator that performs backtracking.
  *
  * @author Steffen Zschaler
  * @version 2.0 18/08/1999
  * @since v0.5
  */
public class StockFromStockCreatorBT extends StockFromStockCreator {

  /**
    * A sorted list of the CatalogItems in the destination Stock's Catalog.
    */
  protected List m_lSortedCI;

  /**
    * A Map of the items that were added. Needed for undo operations during backtracking.
    */
  protected Map m_mplsiItemsAdded = new HashMap();

  /**
    * Create a new StockFromStockCreatorBT.
    *
    * @param stSource the source Stock.
    * @param civ the CatalogItemValue used to determine the CatalogItems' values.
    */
  public StockFromStockCreatorBT (Stock stSource,
                                  CatalogItemValue civ) {
    super (stSource, civ);
  }

  /**
    * Fill the destination Stock using the same algorithm as in {@link StockFromStockCreator#fillStock}, but
    * with backtracking.
    *
    * @override Never
    */
  public Value fillStock (Stock st, Value v, DataBasket db) {
    Catalog c = st.getCatalog (db);

    if (c != m_stSource.getCatalog (db)) {
      throw new CatalogConflictException();
    }

    m_lSortedCI = new LinkedList();
    for (Iterator i = c.iterator (db, false); i.hasNext();) {
      m_lSortedCI.add (i.next());
    }

    if (m_lSortedCI.size() == 0) {
      return v;
    }

    Collections.sort (m_lSortedCI,                   // Sort the items, greates first
                      DefaultCountingStockFromValueCreator.invertedCIValueOrder (m_civEvaluator));

    // fill Stock
    return doFill (0, v, st, db);
  }

  /**
    * Backtracking step method.
    *
    * @override Never
    */
  protected Value doFill (int nIdx, Value v, Stock st, DataBasket db) {
    if (v.isAddZero()) {
      return v;
    }

    if (nIdx >= m_lSortedCI.size()) {
      return v;
    }


    // Attempt to add as many of idx as possible
    CatalogItem ci = (CatalogItem) m_lSortedCI.get (nIdx);
    Value vItemValue = m_civEvaluator.getValue (ci);

    LinkedList lAddedItems = new LinkedList();
    m_mplsiItemsAdded.put (ci.getName(), lAddedItems);

    for (Iterator i = m_stSource.get (ci.getName(), db, false); i.hasNext();) {
      if (vItemValue.compareTo (v) <= 0) {
        StockItem si = (StockItem) i.next();

        try {
          i.remove();

          st.add (si, db);

          v.subtractAccumulating (vItemValue);
          lAddedItems.add (si);
        }
        catch (UnsupportedOperationException uoe) {}
      }
      else {
        break;
      }
    }

    // if we are not ready yet, go on.
    if (!v.isAddZero()) {

      Value vTemp = doFill (nIdx + 1, v, st, db);

      while ((!vTemp.isAddZero()) &&
             (lAddedItems.size() > 0)) {
        // undo unsuccessful filling operation
        undoFill (nIdx + 1, v, st, db);

        // return one item onto theOrgStock
        try {
          StockItem si = (StockItem) lAddedItems.removeLast();
          st.remove (si, db);
          m_stSource.add (si, db);

          v.addAccumulating (m_civEvaluator.getValue (ci));
        }
        catch (data.events.VetoException ve) {}

        vTemp = doFill (nIdx + 1, v, st, db);
      }

      return vTemp;
    }
    else {
      return v;
    }
  }

  /**
    * Backtracking back-step  method.
    *
    * @override Never
    */
  protected void undoFill (int nIdx, Value v, Stock st, DataBasket db) {
    for (; nIdx < m_lSortedCI.size(); nIdx++) {
      CatalogItem ci = (CatalogItem) m_lSortedCI.get (nIdx);

      List lItems = (List) m_mplsiItemsAdded.get (ci.getName());

      if (lItems != null) {
        for (Iterator i = lItems.iterator(); i.hasNext();) {
          try {
            StockItem si = (StockItem) i.next();
            i.remove();

            st.remove (si, db);
            m_stSource.add (si, db);

            v.addAccumulating (m_civEvaluator.getValue (ci));
          }
          catch (data.events.VetoException ex) {}
        }
      }
    }
  }
}