Package logisticspipes.request

Source Code of logisticspipes.request.RequestTreeNode$CraftingSorterNode

package logisticspipes.request;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.SortedSet;
import java.util.TreeSet;

import logisticspipes.interfaces.routing.IAdditionalTargetInformation;
import logisticspipes.interfaces.routing.ICraftItems;
import logisticspipes.interfaces.routing.IFilter;
import logisticspipes.interfaces.routing.IProvideItems;
import logisticspipes.interfaces.routing.IRequestFluid;
import logisticspipes.interfaces.routing.IRequestItems;
import logisticspipes.pipes.basic.CoreRoutedPipe;
import logisticspipes.proxy.SimpleServiceLocator;
import logisticspipes.request.RequestTree.ActiveRequestType;
import logisticspipes.request.RequestTree.workWeightedSorter;
import logisticspipes.routing.ExitRoute;
import logisticspipes.routing.IRouter;
import logisticspipes.routing.LogisticsExtraPromise;
import logisticspipes.routing.LogisticsPromise;
import logisticspipes.routing.LogisticsPromise.PromiseType;
import logisticspipes.routing.PipeRoutingConnectionType;
import logisticspipes.routing.ServerRouter;
import logisticspipes.routing.order.LinkedLogisticsOrderList;
import logisticspipes.routing.order.LogisticsOrder;
import logisticspipes.utils.CraftingRequirement;
import logisticspipes.utils.FluidIdentifier;
import logisticspipes.utils.item.ItemIdentifier;
import logisticspipes.utils.item.ItemIdentifierStack;
import logisticspipes.utils.tuples.Pair;
import logisticspipes.utils.tuples.Triplet;

public class RequestTreeNode {

  protected RequestTreeNode(ItemIdentifierStack item, IRequestItems requester, RequestTreeNode parentNode, EnumSet<ActiveRequestType> requestFlags, IAdditionalTargetInformation info) {
    this(null,item,requester,parentNode,requestFlags, info);
  }
  private RequestTreeNode(CraftingTemplate template, ItemIdentifierStack item, IRequestItems requester, RequestTreeNode parentNode, EnumSet<ActiveRequestType> requestFlags, IAdditionalTargetInformation info) {
    this.request = item;
    this.target = requester;
    this.info = info;
    this.parentNode = parentNode;
//    this.requestFlags=requestFlags;
    if(parentNode!=null) {
      parentNode.subRequests.add(this);
      this.root = parentNode.root;
    } else {
      this.root = (RequestTree)this;
    }
    if(template!=null) {
      this.declareCrafterUsed(template);
    }
   
    if(requestFlags.contains(ActiveRequestType.Provide) && checkProvider()){
      return;
    }

    if(requestFlags.contains(ActiveRequestType.Craft) && checkExtras()) {
      return;// crafting was able to complete
    }
   
    if(requestFlags.contains(ActiveRequestType.Craft) && checkCrafting()) {
      return;// crafting was able to complete
    }
   
    // crafting is not done!
  }

//  private final EnumSet<ActiveRequestType> requestFlags;
  private final IRequestItems target;
  private final IAdditionalTargetInformation info;
  private final ItemIdentifierStack request;
  private final RequestTreeNode parentNode;
  protected final RequestTree root;
  private List<RequestTreeNode> subRequests = new ArrayList<RequestTreeNode>();
  List<FluidRequestTreeNode> liquidSubRequests = new ArrayList<FluidRequestTreeNode>();
  private List<LogisticsPromise> promises = new ArrayList<LogisticsPromise>();
  private List<LogisticsExtraPromise> extrapromises = new ArrayList<LogisticsExtraPromise>();
  private List<LogisticsExtraPromise> byproducts = new ArrayList<LogisticsExtraPromise>();
  private SortedSet<CraftingTemplate> usedCrafters= new TreeSet<CraftingTemplate>();
  private CraftingTemplate lastCrafterTried = null;
 
  private int promiseItemCount = 0;

  private boolean isCrafterUsed(CraftingTemplate test) {
    if(!usedCrafters.isEmpty() && usedCrafters.contains(test))
      return true;
    if(parentNode==null)
      return false;
    return parentNode.isCrafterUsed(test);
  }
 
  // returns false if the crafter was already on the list.
  private boolean declareCrafterUsed(CraftingTemplate test) {
    if(isCrafterUsed(test))
      return false;
    usedCrafters.add(test);
    return true;
  }
 
  public int getPromiseItemCount() {
    return promiseItemCount;
  }
 
  public int getMissingItemCount() {
    return request.getStackSize() - promiseItemCount;
  }
 
  public void addPromise(LogisticsPromise promise) {
    if(!promise.item.equals(request.getItem())) throw new IllegalArgumentException("wrong item");
    if(getMissingItemCount() == 0) throw new IllegalArgumentException("zero count needed, promises not needed.");
    if(promise.numberOfItems > getMissingItemCount()) {
      int more = promise.numberOfItems - getMissingItemCount();
      promise.numberOfItems = getMissingItemCount();
      //Add Extra
      LogisticsExtraPromise extra = new LogisticsExtraPromise(promise.item, more, promise.sender, false);
      extrapromises.add(extra);
    }
    if(promise.numberOfItems <= 0) throw new IllegalArgumentException("zero count ... again");
    promises.add(promise);
    promiseItemCount += promise.numberOfItems;
    root.promiseAdded(promise);
  }

  public boolean isDone() {
    return getMissingItemCount() <= 0;
  }

  public boolean isAllDone() {
    boolean result = getMissingItemCount() <= 0;
    for(RequestTreeNode node:subRequests) {
      result &= node.isAllDone();
    }
    return result;
  }

  public ItemIdentifier getStackItem() {
    return request.getItem();
  }

  protected void remove(RequestTreeNode subNode) {
    subRequests.remove(subNode);
    subNode.removeSubPromisses();
  }
 
  protected void remove(FluidRequestTreeNode subNode) {
    liquidSubRequests.remove(subNode);
    subNode.removeSubPromisses();
  }

  /* RequestTree helpers */
  protected void removeSubPromisses() {
    for(LogisticsPromise promise:promises) {
      root.promiseRemoved(promise);
    }
    for(RequestTreeNode subNode:subRequests) {
      subNode.removeSubPromisses();
    }
  }

  protected void checkForExtras(ItemIdentifier item, HashMap<IProvideItems,List<LogisticsExtraPromise>> extraMap) {
    for(LogisticsExtraPromise extra:extrapromises) {
      if(extra.item.equals(item)) {
        List<LogisticsExtraPromise> extras = extraMap.get(extra.sender);
        if(extras == null) {
          extras = new LinkedList<LogisticsExtraPromise>();
          extraMap.put(extra.sender, extras);
        }
        extras.add(extra.copy());
      }
    }
    for(RequestTreeNode subNode:subRequests) {
      subNode.checkForExtras(item, extraMap);
    }
  }

  protected void removeUsedExtras(ItemIdentifier item, HashMap<IProvideItems,List<LogisticsExtraPromise>> extraMap) {
    for(LogisticsPromise promise:promises) {
      if(!promise.item.equals(item)) continue;
      if(!(promise instanceof LogisticsExtraPromise)) continue;
      LogisticsExtraPromise epromise = (LogisticsExtraPromise)promise;
      if(epromise.provided) continue;
      int usedcount = epromise.numberOfItems;
      List<LogisticsExtraPromise> extras = extraMap.get(epromise.sender);
      if(extras == null) continue;
      for(Iterator<LogisticsExtraPromise> it = extras.iterator(); it.hasNext();) {
        LogisticsExtraPromise extra = it.next();
        if(extra.numberOfItems >= usedcount) {
          extra.numberOfItems -= usedcount;
          usedcount = 0;
          break;
        } else {
          usedcount -= extra.numberOfItems;
          it.remove();
        }
      }
    }
    for(RequestTreeNode subNode:subRequests) {
      subNode.removeUsedExtras(item,extraMap);
    }
  }

  protected LinkedLogisticsOrderList fullFill() {
    LinkedLogisticsOrderList list = new LinkedLogisticsOrderList();
    for(RequestTreeNode subNode:subRequests) {
      list.getSubOrders().add(subNode.fullFill());
    }
    for(LogisticsPromise promise:promises) {
      LogisticsOrder result = promise.sender.fullFill(promise, target, info);
      if(result != null) {
        list.add(result);
      }
    }
    for(LogisticsPromise promise:extrapromises) {
      if(promise.sender instanceof ICraftItems) {
        ((ICraftItems)promise.sender).registerExtras(promise);
      }
    }
    for(LogisticsPromise promise:byproducts) {
      if(promise.sender instanceof ICraftItems) {
        ((ICraftItems)promise.sender).registerExtras(promise);
      }
    }
    for(FluidRequestTreeNode subNode:liquidSubRequests) {
      subNode.fullFill();
    }
    return list;
  }

  protected void buildMissingMap(Map<ItemIdentifier,Integer> missing) {
    if(getMissingItemCount() != 0) {
      ItemIdentifier item = request.getItem();
      Integer count = missing.get(item);
      if(count == null)
        count = 0;
      count += getMissingItemCount();
      missing.put(item, count);
    }
    for(RequestTreeNode subNode:subRequests) {
      subNode.buildMissingMap(missing);
    }
    for(FluidRequestTreeNode subNode:liquidSubRequests) {
      subNode.buildMissingMap(missing);
    }
  }

  protected void buildUsedMap(Map<ItemIdentifier,Integer> used, Map<ItemIdentifier,Integer> missing) {
    int usedcount = 0;
    for(LogisticsPromise promise:promises) {
      if(promise.type == PromiseType.PROVIDER) {
        usedcount += promise.numberOfItems;
      }
    }
    if(usedcount != 0) {
      ItemIdentifier item = request.getItem();
      Integer count = used.get(item);
      if(count == null)
        count = 0;
      count += usedcount;
      used.put(item, count);
    }
    if(getMissingItemCount() != 0) {
      ItemIdentifier item = request.getItem();
      Integer count = missing.get(item);
      if(count == null)
        count = 0;
      count += getMissingItemCount();
      missing.put(item, count);
    }
    for(RequestTreeNode subNode:subRequests) {
      subNode.buildUsedMap(used, missing);
    }
    for(FluidRequestTreeNode subNode:liquidSubRequests) {
      subNode.buildUsedMap(used, missing);
    }
  }
 
  private boolean checkProvider() {
   
    CoreRoutedPipe thisPipe = this.target.getRouter().getCachedPipe();
    if(thisPipe == null) return false;
    for(Pair<IProvideItems, List<IFilter>> provider : getProviders(this.target.getRouter(), this.getStackItem())) {
      if(this.isDone()) {
        break;
      }
      if(provider.getValue1() == null || provider.getValue1().getRouter() == null || provider.getValue1().getRouter().getPipe() == null) continue;
      if(!thisPipe.sharesInventoryWith(provider.getValue1().getRouter().getPipe())) {
        provider.getValue1().canProvide(this, root.getAllPromissesFor(provider.getValue1(), this.getStackItem()), provider.getValue2());
      }
    }
    return this.isDone();
  }

  private static List<Pair<IProvideItems,List<IFilter>>> getProviders(IRouter destination, ItemIdentifier item) {

    // get all the routers
    BitSet routersIndex = ServerRouter.getRoutersInterestedIn(item);
    List<ExitRoute> validSources = new ArrayList<ExitRoute>(); // get the routing table
    for (int i = routersIndex.nextSetBit(0); i >= 0; i = routersIndex.nextSetBit(i+1)) {
      IRouter r = SimpleServiceLocator.routerManager.getRouterUnsafe(i,false);

      if(!r.isValidCache()) continue; //Skip Routers without a valid pipe

      List<ExitRoute> e = destination.getDistanceTo(r);
      if (e!=null) validSources.addAll(e);
    }
    // closer providers are good
    Collections.sort(validSources, new workWeightedSorter(1.0));
   
    List<Pair<IProvideItems,List<IFilter>>> providers = new LinkedList<Pair<IProvideItems,List<IFilter>>>();
    for(ExitRoute r : validSources) {
      if(r.containsFlag(PipeRoutingConnectionType.canRequestFrom)) {
        CoreRoutedPipe pipe = r.destination.getPipe();
        if (pipe instanceof IProvideItems) {
          List<IFilter> list = new LinkedList<IFilter>();
          list.addAll(r.filters);
          providers.add(new Pair<IProvideItems,List<IFilter>>((IProvideItems)pipe, list));
        }
      }
    }
    return providers;
  }
 
  private boolean checkExtras() {
    LinkedList<LogisticsExtraPromise> map = root.getExtrasFor(this.getStackItem());
    for (LogisticsExtraPromise extraPromise : map){
      if(this.isDone()) {
        break;
      }
      if(extraPromise.numberOfItems == 0)
        continue;
      boolean valid = false;
      List<ExitRoute> sources = extraPromise.sender.getRouter().getRouteTable().get(this.target.getRouter().getSimpleID());
outer:
      for(ExitRoute source:sources) {
        if(source != null && source.containsFlag(PipeRoutingConnectionType.canRouteTo)) {
          for(ExitRoute node:this.target.getRouter().getIRoutersByCost()) {
            if(node.destination == extraPromise.sender.getRouter()) {
              if(node.containsFlag(PipeRoutingConnectionType.canRequestFrom)) {
                valid = true;
                break outer;
              }
            }
          }
        }
      }
      if(valid) {
        extraPromise.numberOfItems = Math.min(extraPromise.numberOfItems, this.getMissingItemCount());
        this.addPromise(extraPromise);
      }
    }
    return isDone();
  }
 
  private boolean checkCrafting() {
   
    // get all the routers
    BitSet routersIndex = ServerRouter.getRoutersInterestedIn(this.getStackItem());
    List<ExitRoute> validSources = new ArrayList<ExitRoute>(); // get the routing table
    for (int i = routersIndex.nextSetBit(0); i >= 0; i = routersIndex.nextSetBit(i+1)) {
      IRouter r = SimpleServiceLocator.routerManager.getRouterUnsafe(i,false);

      if(!r.isValidCache()) continue; //Skip Routers without a valid pipe

      List<ExitRoute> e = this.target.getRouter().getDistanceTo(r);
      if (e!=null) validSources.addAll(e);
    }
    workWeightedSorter wSorter = new workWeightedSorter(0); // distance doesn't matter, because ingredients have to be delivered to the crafter, and we can't tell how long that will take.
    Collections.sort(validSources, wSorter);
   
    List<Pair<CraftingTemplate, List<IFilter>>> allCraftersForItem = getCrafters(this.getStackItem(), validSources);
   
    // if you have a crafter which can make the top treeNode.getStack().getItem()
    Iterator<Pair<CraftingTemplate, List<IFilter>>> iterAllCrafters = allCraftersForItem.iterator();
   
    //a queue to store the crafters, sorted by todo; we will fill up from least-most in a balanced way.
    PriorityQueue<CraftingSorterNode> craftersSamePriority = new PriorityQueue<CraftingSorterNode>(5);
    ArrayList<CraftingSorterNode> craftersToBalance = new ArrayList<CraftingSorterNode>();
    //TODO ^ Make this a generic list
    boolean done=false;
    Pair<CraftingTemplate, List<IFilter>> lastCrafter =null;
    int currentPriority=0;
outer:
    while(!done) {
     
      /// First: Create a list of all crafters with the same priority (craftersSamePriority). 
      if(iterAllCrafters.hasNext()) {
        if(lastCrafter == null){
          lastCrafter = iterAllCrafters.next();
        }
      } else if(lastCrafter == null) {
        done=true;
      }
     
      int itemsNeeded = this.getMissingItemCount();
     
      if(lastCrafter!=null && (craftersSamePriority.isEmpty() || (currentPriority == lastCrafter.getValue1().getPriority()))) {
        currentPriority=lastCrafter.getValue1().getPriority();
        Pair<CraftingTemplate, List<IFilter>> crafter = lastCrafter;
        lastCrafter = null;
        CraftingTemplate template = crafter.getValue1();
        if(this.isCrafterUsed(template)) // then somewhere in the tree we have already used this
          continue;
        if(!template.canCraft(this.getStackItem()))
          continue; // we this is crafting something else   
        for(IFilter filter:crafter.getValue2()) { // is this filtered for some reason.
          if(filter.isBlocked() == filter.isFilteredItem(template.getResultItem().getUndamaged()) || filter.blockCrafting()) continue outer;
        }
        CraftingSorterNode cn =  new CraftingSorterNode(crafter,itemsNeeded,root,this);
//        if(cn.getWorkSetsAvailableForCrafting()>0)
          craftersSamePriority.add(cn);
        continue;
      }
      if(craftersToBalance.isEmpty() && (craftersSamePriority == null || craftersSamePriority.isEmpty())) {
        continue; //nothing at this priority was available for crafting
      }
      /// end of crafter prioriy selection.

      if(craftersSamePriority.size() == 1){ // then no need to balance.
        craftersToBalance.add(craftersSamePriority.poll());
        // automatically capped at the real amount of extra work.
        craftersToBalance.get(0).addToWorkRequest(itemsNeeded);
      } else {
//        for(CraftingSorterNode c:craftersSamePriority)
//          c.clearWorkRequest(); // so the max request isn't in there; nothing is reserved, balancing can work correctly.
       
        // go through this list, pull the crafter(s) with least work, add work until either they can not do more work,
        //   or the amount of work they have is equal to the next-least busy crafter. then pull the next crafter and repeat.
        if(!craftersSamePriority.isEmpty())
          craftersToBalance.add(craftersSamePriority.poll());
        // while we crafters that can work and we have work to do.
        while(!craftersToBalance.isEmpty() && itemsNeeded>0) {
          //while there is more, and the next crafter has the same toDo as the current one, add it to craftersToBalance.
          //  typically pulls 1 at a time, but may pull multiple, if they have the exact same todo.
          while(!craftersSamePriority.isEmpty() && 
              craftersSamePriority.peek().currentToDo() <= craftersToBalance.get(0).currentToDo()) {
            craftersToBalance.add(craftersSamePriority.poll());
          }
         
          // find the most we can add this iteration
          int cap;
          if(!craftersSamePriority.isEmpty())
            cap = craftersSamePriority.peek().currentToDo();
          else
            cap = Integer.MAX_VALUE;
         
          //split the work between N crafters, up to "cap" (at which point we would be dividing the work between N+1 crafters.
          int floor = craftersToBalance.get(0).currentToDo();
          cap = Math.min(cap,floor + (itemsNeeded + craftersToBalance.size()-1)/craftersToBalance.size());
         
          Iterator<CraftingSorterNode> iter = craftersToBalance.iterator();
          while(iter.hasNext()){
            CraftingSorterNode crafter = iter.next();
            int request = Math.min(itemsNeeded,cap-floor);
            if(request > 0) {
              int craftingDone = crafter.addToWorkRequest(request);
              itemsNeeded -= craftingDone; // ignored under-crafting
            }
          }
 
        } // all craftersToBalance exhausted, or work completed.
       
      }// end of else more than 1 crafter at this priority
      // commit this work set.
      Iterator<CraftingSorterNode> iter = craftersToBalance.iterator();
      while(iter.hasNext()){
        CraftingSorterNode c = iter.next();
        if(c.stacksOfWorkRequested>0 && !c.addWorkPromisesToTree()) { // then it ran out of resources
          iter.remove();               
        }

      }
      itemsNeeded = this.getMissingItemCount();
     
      if(itemsNeeded <= 0)
        break outer; // we have everything we need for this crafting request

      // don't clear, because we might have under-requested, and need to consider these again
      if(!craftersToBalance.isEmpty())
        done=false;
      //craftersSamePriority.clear(); // we've extracted all we can from these priority crafters, and we still have more to do, back to the top to get the next priority level.
    }
    //LogisticsPipes.log.info("done");
    return isDone();
  }

  private class CraftingSorterNode implements Comparable<CraftingSorterNode> {
    private int stacksOfWorkRequested;
    private final int setSize;
    private final int maxWorkSetsAvailable;
    private final RequestTreeNode treeNode; // current node we are calculating

    public final Pair<CraftingTemplate, List<IFilter>> crafter;
    public final int originalToDo;

    CraftingSorterNode(Pair<CraftingTemplate, List<IFilter>> crafter, int maxCount, RequestTree tree, RequestTreeNode treeNode) {
      this.crafter = crafter;
      this.treeNode = treeNode;
      this.originalToDo = crafter.getValue1().getCrafter().getTodo();
      this.stacksOfWorkRequested = 0;
      this.setSize = crafter.getValue1().getResultStackSize();
      this.maxWorkSetsAvailable = ((treeNode.getMissingItemCount()) + setSize - 1) / setSize;
    }
   
    int calculateMaxWork(int maxSetsToCraft){
     
      int nCraftingSetsNeeded;
      if(maxSetsToCraft == 0)
        nCraftingSetsNeeded = ((treeNode.getMissingItemCount()) + setSize - 1) / setSize;
      else
        nCraftingSetsNeeded = maxSetsToCraft;
     
      if(nCraftingSetsNeeded==0) // not sure how we get here, but i've seen a stack trace later where we try to create a 0 size promise.
        return 0;
     
      CraftingTemplate template = crafter.getValue1();
      int stacks= getSubRequests(nCraftingSetsNeeded, template);
     
     
     
      return stacks;
    }


    int addToWorkRequest(int extraWork) {
      int stacksRequested = (extraWork+setSize-1)/setSize;
      stacksOfWorkRequested += stacksRequested;
      return stacksRequested*setSize;
    }

    /**
     * Add promises for the requested work to the tree.
     */
    boolean addWorkPromisesToTree() {
      CraftingTemplate template = crafter.getValue1();
      int setsToCraft = Math.min(this.stacksOfWorkRequested,this.maxWorkSetsAvailable);
      int setsAbleToCraft = calculateMaxWork(setsToCraft); // Deliberately outside the 0 check, because calling generatePromies(0) here clears the old ones.
     
      if(setsAbleToCraft>0) { // sanity check, as creating 0 sized promises is an exception. This should never be hit.
        //LogisticsPipes.log.info("crafting : " + setsToCraft + "sets of " + treeNode.getStack().getItem().getFriendlyName());
        //if we got here, we can at least some of the remaining amount
        LogisticsPromise job = template.generatePromise(setsAbleToCraft);
        if(job.numberOfItems!=setsAbleToCraft*this.setSize)
          throw new IllegalStateException("generatePromises not creating the promisesPromised; this is goign to end badly.");
        treeNode.addPromise(job);
      } else {
        //stacksOfWorkRequested=0; // just incase we call it twice.
        //return true; // don't remove from the list if we have no w
       
        //LogisticsPipes.log.info("minor bug detected, 0 sized promise attempted. Crafting:" + treeNode.request.makeNormalStack().getUnlocalizedName());
        //LogisticsPipes.log.info("failed crafting : " + setsToCraft + "sets of " + treeNode.getStack().getItem().getFriendlyName());
      }
      boolean isDone = setsToCraft == setsAbleToCraft;
      stacksOfWorkRequested=0; // just incase we call it twice.
//      if(setsToCraft == 0) // so that we remove this node as failed when there is no work to do.
//        return false;
      return isDone;
    }

    @Override
    public int compareTo(CraftingSorterNode o) {
      return  this.currentToDo() - o.currentToDo();
    }

    public int currentToDo() {
      return this.originalToDo+this.stacksOfWorkRequested*setSize;
    }
  }

  private static List<Pair<CraftingTemplate,List<IFilter>>> getCrafters(ItemIdentifier itemToCraft, List<ExitRoute> validDestinations) {
    List<Pair<CraftingTemplate,List<IFilter>>> crafters = new ArrayList<Pair<CraftingTemplate,List<IFilter>>>(validDestinations.size());
    for(ExitRoute r : validDestinations) {
      CoreRoutedPipe pipe = r.destination.getPipe();
      if(r.containsFlag(PipeRoutingConnectionType.canRequestFrom)) {
        if (pipe instanceof ICraftItems){
          CraftingTemplate craftable = ((ICraftItems)pipe).addCrafting(itemToCraft);
          if(craftable!=null) {
            for(IFilter filter: r.filters) {
              if(filter.isBlocked() == filter.isFilteredItem(craftable.getResultItem().getUndamaged()) || filter.blockCrafting()) continue;
            }
            List<IFilter> list = new LinkedList<IFilter>();
            list.addAll(r.filters);
            crafters.add(new Pair<CraftingTemplate, List<IFilter>>(craftable, list));
          }
        }
      }
    }
    // don't need to sort, as a sorted list is passed in and List guarantees order preservation
//    Collections.sort(crafters,new CraftingTemplate.PairPrioritizer());
    return crafters;
  }

  private int getSubRequests(int nCraftingSets, CraftingTemplate template){
    boolean failed = false;
    List<Triplet<CraftingRequirement, IRequestItems, IAdditionalTargetInformation>> stacks = template.getComponentItems(nCraftingSets);
    int workSetsAvailable = nCraftingSets;
    ArrayList<SubRequestGroup>lastNodes = new ArrayList<SubRequestGroup>(stacks.size());
    for(Triplet<CraftingRequirement, IRequestItems, IAdditionalTargetInformation> stack:stacks) {
      if(stack.getValue1().isUnique())
      {
        RequestTreeNode node = new RequestTreeNode(template,stack.getValue1().stack, stack.getValue2(), this, RequestTree.defaultRequestFlags, stack.getValue3());
        SubRequestGroup grp = new SubRequestGroup();
        grp.addNode(node);
        lastNodes.add(grp);
        if(!node.isDone()) {
          failed = true;
        }
      }
      else
      {
        ArrayList<ItemIdentifier> subtitutes = stack.getValue1().GetSubtitutes(this.target);
        int req = stack.getValue1().stack.getStackSize();
        SubRequestGroup grp = new SubRequestGroup();
        for(ItemIdentifier i : subtitutes)
        {
          if(req <= 0)
            break;
          RequestTreeNode node = new RequestTreeNode(template, new ItemIdentifierStack(i, req), stack.getValue2(), this, RequestTree.defaultRequestFlags, stack.getValue3());
          req -= node.getPromiseItemCount();
          grp.addNode(node);
        }
        if(req > 0)
          failed = true;
        lastNodes.add(grp);
      }
    }
    List<Triplet<FluidIdentifier, Integer, IRequestFluid>> liquids = template.getComponentFluid(nCraftingSets);
    ArrayList<FluidRequestTreeNode>lastFluidNode = new ArrayList<FluidRequestTreeNode>(liquids.size());
    for(Triplet<FluidIdentifier, Integer, IRequestFluid> liquid:liquids) {
      FluidRequestTreeNode node = new FluidRequestTreeNode(liquid.getValue1(), liquid.getValue2(), liquid.getValue3(), this);
      lastFluidNode.add(node);
      if(!node.isDone()) {
        failed = true;
      }
    }
    if(failed) {
      for (SubRequestGroup g:lastNodes) {
        for(RequestTreeNode n : g.getNodes())
          n.destroy(); // drop the failed requests.
      }
      for (FluidRequestTreeNode n:lastFluidNode) {
        n.destroy(); // drop the failed requests.
      }
      //save last tried template for filling out the tree
      this.lastCrafterTried = template;
      //figure out how many we can actually get
      for(int i = 0; i < stacks.size(); i++) {
        workSetsAvailable = Math.min(workSetsAvailable, lastNodes.get(i).getTotalPromiseItemCount() / (stacks.get(i).getValue1().stack.getStackSize() / nCraftingSets));
      }
     
      for(int i = 0; i < liquids.size(); i++) {
        workSetsAvailable = Math.min(workSetsAvailable, lastFluidNode.get(i).getPromiseFluidAmount() / (liquids.get(i).getValue2() / nCraftingSets));
      }
     
      return generateRequestTreeFor(workSetsAvailable, template);
    }
    for(ItemIdentifierStack stack:template.getByproduct()) {
      LogisticsExtraPromise extra = new LogisticsExtraPromise(stack.getItem(), stack.getStackSize() * workSetsAvailable, template.getCrafter(), false);
      byproducts.add(extra);
    }
    return workSetsAvailable;
  }

  private int generateRequestTreeFor(int workSets, CraftingTemplate template) {
   
    //and try it
    ArrayList<RequestTreeNode> newChildren = new ArrayList<RequestTreeNode>();
    ArrayList<FluidRequestTreeNode> newFluidChildren = new ArrayList<FluidRequestTreeNode>();
    if(workSets>0) {
      //now set the amounts

      List<Triplet<CraftingRequirement, IRequestItems, IAdditionalTargetInformation>> stacks = template.getComponentItems(workSets);

      boolean failed = false;
      for(Triplet<CraftingRequirement, IRequestItems, IAdditionalTargetInformation> stack:stacks) {
        if(stack.getValue1().isUnique())
        {
          RequestTreeNode node = new RequestTreeNode(template,stack.getValue1().stack, stack.getValue2(), this, RequestTree.defaultRequestFlags, stack.getValue3());
          newChildren.add(node);
          if(!node.isDone()) {
            failed = true;
          }
        }
        else
        {
          ArrayList<ItemIdentifier> subtitutes = stack.getValue1().GetSubtitutes(this.target);
          int req = stack.getValue1().stack.getStackSize();
          for(ItemIdentifier i : subtitutes)
          {
            if(req <= 0)
              break;
            RequestTreeNode node = new RequestTreeNode(template, new ItemIdentifierStack(i, req), stack.getValue2(), this, RequestTree.defaultRequestFlags, stack.getValue3());
            req -= node.getPromiseItemCount();
            newChildren.add(node);
          }
          if(req > 0)
            failed = true;
        }
      }
      List<Triplet<FluidIdentifier, Integer, IRequestFluid>> liquids = template.getComponentFluid(workSets);
      for(Triplet<FluidIdentifier, Integer, IRequestFluid> liquid:liquids) {
        FluidRequestTreeNode node = new FluidRequestTreeNode(liquid.getValue1(), liquid.getValue2(), liquid.getValue3(), this);
        newFluidChildren.add(node);
        if(!node.isDone()) {
          failed = true;
        }
      }
      if(failed) {
        for(RequestTreeNode c:newChildren) {
          c.destroy();
        }
        for (FluidRequestTreeNode n:newFluidChildren) {
          n.destroy();
        }
        return 0;
      }
    }
    for(ItemIdentifierStack stack:template.getByproduct()) {
      LogisticsExtraPromise extra = new LogisticsExtraPromise(stack.getItem(), stack.getStackSize() * workSets, template.getCrafter(), false);
      byproducts.add(extra);
    }
    return workSets;
  }

  void recurseFailedRequestTree() {
    if(this.isDone())
      return;
    if(this.lastCrafterTried == null)
      return;

    CraftingTemplate template = this.lastCrafterTried;

    int nCraftingSetsNeeded = (this.getMissingItemCount() + template.getResultStackSize() - 1) / template.getResultStackSize();

    List<Triplet<CraftingRequirement, IRequestItems, IAdditionalTargetInformation>> stacks = template.getComponentItems(nCraftingSetsNeeded);

    for(Triplet<CraftingRequirement, IRequestItems, IAdditionalTargetInformation> stack:stacks) {
      new RequestTreeNode(template, stack.getValue1().stack, stack.getValue2(), this, RequestTree.defaultRequestFlags, stack.getValue3());
    }

    List<Triplet<FluidIdentifier, Integer, IRequestFluid>> liquids = template.getComponentFluid(nCraftingSetsNeeded);

    for(Triplet<FluidIdentifier, Integer, IRequestFluid> liquid:liquids) {
      new FluidRequestTreeNode(liquid.getValue1(), liquid.getValue2(), liquid.getValue3(), this);
    }

    this.addPromise(template.generatePromise(nCraftingSetsNeeded));

    for(RequestTreeNode subNode : this.subRequests) {
      subNode.recurseFailedRequestTree();
    }
  }


  protected void logFailedRequestTree(RequestLog log) {
    Map<ItemIdentifier,Integer> missing = new HashMap<ItemIdentifier,Integer>();
    for(RequestTreeNode node:this.subRequests) {
      if(node instanceof RequestTree) {
        if(!node.isDone()) {
          node.recurseFailedRequestTree();
          node.buildMissingMap(missing);
        }
      }
    }
    log.handleMissingItems(missing);
  }
 
  private void destroy() {
    parentNode.remove(this);   
  }
}
TOP

Related Classes of logisticspipes.request.RequestTreeNode$CraftingSorterNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.