public static ManuQuote calculateReverseEngineeringQuote(
      Blueprint t3Bpo,
      int runs,
      ManuQuote oldQuote,
      PriceDB pdb,
      BlueprintDB bdb,
      ItemDB idb,
      Preferences prefs,
      RevPriority rPrio,
      MatAcquirePriority mPrio) {
    // Combination list.
    ArrayList<ManuQuote> qL = new ArrayList<>();
    ManuQuote tmp = null;
    Blueprint cleanT3Bpo = bdb.getByBlueprintId(t3Bpo.getBlueprintItem().getTypeId());

    for (Relic rel : cleanT3Bpo.getRevRelics()) {

      // Only add quote if relic is available.
      if (MarketCalculator.isItemAvailable(rel.getRelic(), pdb, prefs)) {
        tmp =
            calculateQuote(
                calculateReverseEngineeringBPC(cleanT3Bpo, rel, idb, prefs),
                runs,
                oldQuote,
                pdb,
                bdb,
                prefs,
                mPrio);
        tmp.setRev(
            new ReverseEngineering(
                calculateReverseEngineeringChance(cleanT3Bpo, rel, idb, prefs),
                cleanT3Bpo.getRevDecryptor(),
                rel.getRevMaxRunModifier(),
                new Time(DEFAULT_REV_TIME),
                rPrio));
        qL.add(tmp);
      }
    }

    // Pick the best Quote according to priority.
    for (ManuQuote q : qL) {
      if (rPrio == RevPriority.PROFIT_MARGIN && q.getProfit() > tmp.getProfit()) {
        tmp = q;
      } else if (rPrio == RevPriority.PROFIT_PER_REV_H
          && q.getProfit()
                  / (q.getRev().getRevTime().toHours()
                      * q.getRev().getBpcRuns()
                      * q.getRev().getSuccessRate())
              > tmp.getProfit()
                  / (tmp.getRev().getRevTime().toHours()
                      * tmp.getRev().getBpcRuns()
                      * tmp.getRev().getSuccessRate())) {
        tmp = q;
      }
    }
    return tmp;
  }
  public static ManuQuote calculateQuote(
      Blueprint b,
      int runs,
      ManuQuote oldQuote,
      PriceDB pdb,
      BlueprintDB bdb,
      Preferences prefs,
      MatAcquirePriority mPrio) {

    // ---Skills and modifiers---
    // Apply skills, implant and slotModifier.
    double time =
        b.getManuTime()
            * (1 - (0.04 * SKILL_LEVEL_VALUE[prefs.getSkillLvlIndex(Skill.INDUSTRY)]))
            * MOD_IMPLANT_VALUE[prefs.getImplantModIndex(ImplantMod.MOD_PE)]
            * MOD_SLOT_VALUE[prefs.getInstallationModIndex(InstallationMod.SLOT_MOD_PE)];

    // Finalize time calculation.
    if ((b.getProductivityMod() / ((double) b.getManuTime())) <= 1) {
      if (b.getPe() >= 0) {

        time *=
            (1
                - ((b.getProductivityMod() / ((double) b.getManuTime()))
                    * (b.getPe() / ((double) (1 + b.getPe())))));
      } else {

        time *= (1 - ((b.getProductivityMod() / ((double) b.getManuTime())) * (b.getPe() - 1)));
      }
    }

    double cost = 0, income = 0;
    // Base material list.
    ArrayList<Material> bML = new ArrayList<>();
    // Extra material list.
    ArrayList<Material> eML = new ArrayList<>();
    // Extra material recycle list.
    ArrayList<Material> eMRL = new ArrayList<>();
    double amount;
    double afterRecycleAmount;

    // Divide the extra materials.
    for (Material m : b.getExtraMaterials()) {

      // Check if base materials already has a entry for the material.
      if (m.isRecycled()) {
        // Add to subtraction-list if item is recycled.
        for (Material m1 : m.getItem().getBaseMaterials()) {
          eMRL.add(new Material(m1.getItem(), m1.getAmount()));
        }
      }
      // Add skill waste.
      eML.add(
          new Material(
              m.getItem(),
              (m.getAmount()
                  + (int)
                      ((((25
                                      - (5
                                          * SKILL_LEVEL_VALUE[
                                              prefs.getSkillLvlIndex(Skill.PRODUCTION_EFFICIENCY)]))
                                  * m.getAmount())
                              / 100)
                          + 0.5))));
    }

    // Calculate the correct amount of base materials including, skill and Me-based waste.
    for (Material m : b.getProduct().getBaseMaterials()) {

      afterRecycleAmount = m.getAmount();

      // Remove recycled amount.
      if (eMRL.contains(m)) {
        afterRecycleAmount -= eMRL.get(eMRL.indexOf(m)).getAmount();
        // Material amount can't be lower than 0.
        if (afterRecycleAmount < 0) {
          // Don't add the material, recycle has covered the amount.
          continue;
        }
      }

      // Set base.
      amount = afterRecycleAmount;

      // Add skill waste.
      amount +=
          (int)
              ((((25 - (5 * SKILL_LEVEL_VALUE[prefs.getSkillLvlIndex(Skill.PRODUCTION_EFFICIENCY)]))
                          * afterRecycleAmount)
                      / 100)
                  + 0.5);

      // Add ME waste.
      if (b.getMe() >= 0) {
        amount +=
            (int)
                ((afterRecycleAmount
                        * ((b.getWasteFactor()) / 100.0)
                        * (1 / (double) (b.getMe() + 1)))
                    + 0.5);

      } else {
        amount +=
            (int) ((afterRecycleAmount * ((b.getWasteFactor()) / 100.0) * (1 - b.getMe())) + 0.5);
      }

      // Apply slot modifier.
      amount *= MOD_SLOT_VALUE[prefs.getInstallationModIndex(InstallationMod.SLOT_MOD_ME)];
      bML.add(new Material(m.getItem(), (amount * runs)));
    }

    // Add extra materials to base-list.
    for (Material m : eML) {
      if (bML.contains(m)) {
        bML.get(bML.indexOf(m))
            .setAmount(bML.get(bML.indexOf(m)).getAmount() + (m.getAmount() * runs));
      } else {
        bML.add(new Material(m.getItem(), m.getAmount() * runs));
      }
    }

    // ---PriceChecks---
    // Calculate the cost for the Materials.
    for (Material m : bML) {
      m.setPrice(MarketCalculator.calculatePrice(Action.BUY, m.getItem().getTypeId(), pdb, prefs));
    }

    // Do pre calculations of cost to decide how to acquire materials.
    for (Material m : bML) {
      cost += m.getPrice() * m.getAmount();
    }
    // Installation cost.
    cost +=
        prefs.getManufacturingCost(ManufacturingCost.INSTALLATION_COST)
            + time * prefs.getManufacturingCost(ManufacturingCost.INSTALLATION_COST_H) / 3600;

    // Income.
    income =
        b.getItemsPerRun()
            * runs
            * MarketCalculator.calculatePrice(Action.SELL, b.getProduct().getTypeId(), pdb, prefs);

    Blueprint tempB;
    // Add special manufacturing prices.
    for (Material m : bML) {
      // Add price to manufacture if applicable, otherwise add null.
      tempB = bdb.getByProductId(m.getItem().getTypeId());
      if (tempB != null) {
        // Set default me/pe levels.
        tempB.setMe(prefs.getBlueprintStat(BlueprintStat.MOD_ME));
        tempB.setPe(prefs.getBlueprintStat(BlueprintStat.MOD_PE));
        m.setManufactureQuote(calculateQuote(tempB, 1, null, pdb, bdb, prefs, mPrio));
        m.setCanBeManufactured(true);
      } else {
        m.setCanBeManufactured(false);
        m.setProduced(false);
        // Leave ManufactureQuote to null.
      }
    }

    // Set which prices to use, checks the values from the "oldQuote".
    if (oldQuote != null) {
      for (Material m1 : bML) {
        for (Material m : oldQuote.getMatList()) {
          if (m1.getItem().equals(m.getItem())) {
            m1.setProduced(m.isProduced());
            break;
          }
        }
      }
    }

    // Set the values that were not set.
    for (Material m : bML) {
      if (m.isProduced() == null) {
        // Overrides priorities, if material can't be bought (price = 0), always produce.
        if (m.getPrice() == 0) {
          m.setProduced(true);
        } else if (mPrio == MatAcquirePriority.PROFIT_MARGIN
            && m.getManufactureQuote().getManuCost() < m.getPrice()) {
          // Set to true if the profit increases by manufacturing the material.
          m.setProduced(true);
        } else if (mPrio == MatAcquirePriority.PROFIT_PER_H_CHAIN
            && ((income - cost) * 3600) / (time * runs)
                < ((m.getPrice() - m.getManufactureQuote().getManuCost())
                    / m.getManufactureQuote().getManuTime().toHours())) {
          // Set to true if the profit/h increases by manufacturing the material in regard to the
          // time it takes.
          m.setProduced(true);
        } else {
          m.setProduced(false);
        }
      }
    }

    // Reset and re-do calculations.
    cost = 0;
    for (Material m : bML) {
      if (m.isProduced()) {
        cost += m.getManufactureQuote().getManuCost() * m.getAmount();
      } else {
        cost += m.getPrice() * m.getAmount();
      }
    }
    // Installation cost.
    cost +=
        prefs.getManufacturingCost(ManufacturingCost.INSTALLATION_COST)
            + time * prefs.getManufacturingCost(ManufacturingCost.INSTALLATION_COST_H) / 3600;

    // Set the T1 BPO market availability.
    boolean seededOnMarket = b.getBlueprintItem().isOnMarket();

    // Create quote.
    ManuQuote ans =
        new ManuQuote(
            b,
            new Time((int) (time + 0.5)),
            cost,
            income,
            income - cost,
            ((income - cost) * 3600) / (time * runs),
            calculateSalesVolumeRatio(b, pdb, time),
            runs,
            bML,
            mPrio,
            MarketCalculator.calculateMarketTrend(Action.SELL, b.getProduct(), pdb, prefs),
            seededOnMarket);

    // Set sustainableProfitValue.
    ans.setSustainableProfitValue(calculateSustainableProfitValue(ans));

    return ans;
  }
  public static ManuQuote calculateInventionQuote(
      Blueprint t2Bpo,
      int runs,
      ManuQuote oldQuote,
      PriceDB pdb,
      TechDB tdb,
      BlueprintDB bdb,
      Preferences prefs,
      InvPriority iPrio,
      MatAcquirePriority mPrio) {

    // Corresponding T1 blueprint.
    Blueprint t1Bpo = bdb.getByProductId(tdb.getParentId(t2Bpo.getProduct()));
    Blueprint cleanT2Bpo = bdb.getByBlueprintId(t2Bpo.getBlueprintItem().getTypeId());

    // Variations.
    ArrayList<Decryptor> dL = t1Bpo.getInvDecryptors();
    dL.add(null); // No decryptor.
    ArrayList<Item> iL = tdb.getT1Items(t1Bpo.getProduct().getTypeId());
    iL.add(null); // No item.
    int[] rL = {1, t1Bpo.getMaxRuns()};

    // Combination list.
    ArrayList<ManuQuote> qL = new ArrayList<>();
    ManuQuote tmp = null;

    for (int t1BpcRuns : rL) {
      for (Item metaItem : iL) {
        for (Decryptor dec : dL) {

          // Only add quote if decryptor is available.
          if (dec == null || MarketCalculator.isItemAvailable(dec.getDecryptor(), pdb, prefs)) {

            // Get quote.
            tmp =
                calculateQuote(
                    calculateInventionBPC(t1Bpo, cleanT2Bpo, metaItem, t1BpcRuns, dec, prefs),
                    runs,
                    oldQuote,
                    pdb,
                    bdb,
                    prefs,
                    mPrio);
            tmp.setInv(
                new Invention(
                    calculateInventionChance(t1Bpo, metaItem, dec, prefs),
                    dec,
                    metaItem,
                    t1BpcRuns,
                    new Time(
                        (int)
                            (((t1Bpo.getCopyTime() * t1BpcRuns / (double) t1Bpo.getMaxRuns()) + 0.5)
                                * MOD_COPY_VALUE[
                                    prefs.getInstallationModIndex(
                                        InstallationMod
                                            .SLOT_MOD_COPY)])), // Divide copyTime with number of
                                                                // max-runs.
                    calculateInventionBPCRuns(t1Bpo, cleanT2Bpo, t1BpcRuns, dec),
                    new Time(0, 0, 0, calculateInventionTime(t1Bpo, prefs)),
                    iPrio));

            qL.add(tmp);
          }
        }
      }
    }

    // Pick the best Quote according to priority.
    for (ManuQuote q : qL) {
      if (iPrio == InvPriority.PROFIT_MARGIN && q.getProfit() > tmp.getProfit()) {
        tmp = q;
      } else if (iPrio == InvPriority.PROFIT_PER_H
          && q.getProfitPerHour() > tmp.getProfitPerHour()) {
        tmp = q;
      } else if (iPrio == InvPriority.PROFIT_PER_COPY_H
          && q.getProfit()
                  / (q.getInv().getCopyTime().toHours()
                      * q.getInv().getT2BpcRuns()
                      * q.getInv().getSuccessRate())
              > tmp.getProfit()
                  / (tmp.getInv().getCopyTime().toHours()
                      * tmp.getInv().getT2BpcRuns()
                      * tmp.getInv().getSuccessRate())) {
        tmp = q;
      } else if (iPrio == InvPriority.PROFIT_SUSTAINED
          && q.getSustainableProfitValue() > tmp.getSustainableProfitValue()) {
        tmp = q;
      }
    }
    // Set the T1 BPO market availability.
    tmp.setBaseBPOSeededOnMarket(
        MarketCalculator.isItemAvailable(t1Bpo.getBlueprintItem(), pdb, prefs));

    return tmp;
  }