/*
* ShoppingList.java
*
* Copyright (c) 2009 Jay Lawson <jaylawson39 at yahoo.com>. All rights reserved.
*
* This file is part of MekHQ.
*
* MekHQ is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MekHQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MekHQ. If not, see <http://www.gnu.org/licenses/>.
*/
package mekhq.campaign.market;
import java.io.PrintWriter;
import java.util.ArrayList;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import megamek.common.Entity;
import mekhq.MekHQ;
import mekhq.MekHqXmlSerializable;
import mekhq.MekHqXmlUtil;
import mekhq.Version;
import mekhq.campaign.Campaign;
import mekhq.campaign.event.ProcurementEvent;
import mekhq.campaign.parts.Part;
import mekhq.campaign.parts.Refit;
import mekhq.campaign.parts.equipment.AmmoBin;
import mekhq.campaign.parts.equipment.MissingEquipmentPart;
import mekhq.campaign.unit.UnitOrder;
import mekhq.campaign.work.IAcquisitionWork;
/**
* An arraylist of IAcquisitionWork
*
* When a new acquisition is requested (via the parts store or the acquisition tab), we
* iterate through this list and look for the MissingPart.getNewPart that matches
* the desired part. Here are the possible outcomes:
*
* - We find it, but we cannot check today, so we add the quantity requested
* - We don't find it, we immediately check and add to the list if we fail
*
* On Campaign.newDay, we also cycle through the list and check any items that have no
* more days to wait for the next check.
*
* Checking procedure
* Using a while loop, we keep checking using an acquisition roll until we fail or we hit
* zero quantity. If we hit zero quantity, then we can remove the item. If we fail, then
* we reset the dayCounter to the max.
*
* We also now only use one person to make all checks. We allow the user to set the skill
* and other options for who makes the check in the campaign options.,
*
* Do we use a separate shopping list for new units?
*/
public class ShoppingList implements MekHqXmlSerializable {
private ArrayList<IAcquisitionWork> shoppingList;
public ShoppingList() {
shoppingList = new ArrayList<IAcquisitionWork>();
}
public IAcquisitionWork getShoppingItem(Object newEquipment) {
for(IAcquisitionWork shoppingItem : shoppingList) {
if(isSameEquipment(shoppingItem.getNewEquipment(), newEquipment)) {
return shoppingItem;
}
}
return null;
}
public void removeItem(Object equipment) {
int idx = -1;
int row = 0;
for(IAcquisitionWork shoppingItem : shoppingList) {
if(isSameEquipment(shoppingItem.getNewEquipment(), equipment)) {
idx = row;
break;
}
row++;
}
if(idx > -1) {
shoppingList.remove(idx);
}
}
public void addShoppingItemWithoutChecking(IAcquisitionWork newWork) {
shoppingList.add(newWork);
}
public void addShoppingItem(IAcquisitionWork newWork, int quantity, Campaign campaign) {
//ammo bins need a little extra work here
if(newWork instanceof AmmoBin) {
newWork = ((AmmoBin) newWork).getAcquisitionWork();
}
for(IAcquisitionWork shoppingItem : shoppingList) {
if(isSameEquipment(shoppingItem.getNewEquipment(), newWork.getNewEquipment())) {
campaign.addReport(newWork.getShoppingListReport(quantity));
while(quantity > 0) {
shoppingItem.incrementQuantity();
quantity--;
}
return;
}
}
boolean canAfford = true;
if(campaign.getFunds() < getTrueBuyCost(newWork, campaign)) {
campaign.addReport("<font color='red'><b>You cannot afford to purchase " + newWork.getAcquisitionName() + "</b></font>");
canAfford = false;
}
while(canAfford && quantity > 0 && campaign.acquireEquipment(newWork)) {
quantity--;
if(quantity > 0 && campaign.getFunds() < getTrueBuyCost(newWork, campaign)) {
canAfford = false;
campaign.addReport("<font color='red'><b>You cannot afford to purchase " + newWork.getAcquisitionName() + "</b></font>");
}
}
if(quantity > 0) {
campaign.addReport(newWork.getShoppingListReport(quantity));
while(quantity > 1) {
newWork.incrementQuantity();
quantity--;
}
shoppingList.add(newWork);
MekHQ.triggerEvent(new ProcurementEvent(newWork));
}
}
public void newDay(Campaign campaign) {
ArrayList<IAcquisitionWork> newShoppingList = new ArrayList<IAcquisitionWork>();
boolean noStaff = false;
for(IAcquisitionWork shoppingItem : shoppingList) {
shoppingItem.decrementDaysToWait();
if(shoppingItem.getDaysToWait() <= 0 && !noStaff) {
boolean canAfford = true;
if(campaign.getFunds() < getTrueBuyCost(shoppingItem, campaign)) {
campaign.addReport("<font color='red'><b>You cannot afford to purchase " + shoppingItem.getAcquisitionName() + "</b></font>");
canAfford = false;
}
while(canAfford && shoppingItem.getQuantity() > 0 && campaign.acquireEquipment(shoppingItem)) {
shoppingItem.decrementQuantity();
if(shoppingItem.getQuantity() > 0 && campaign.getFunds() < getTrueBuyCost(shoppingItem, campaign)) {
canAfford = false;
campaign.addReport("<font color='red'><b>You cannot afford to purchase " + shoppingItem.getAcquisitionName() + "</b></font>");
}
}
}
if(shoppingItem.getQuantity() > 0 || shoppingItem.getDaysToWait() > 0) {
newShoppingList.add(shoppingItem);
}
}
shoppingList = newShoppingList;
}
@Override
public void writeToXml(PrintWriter pw1, int indent) {
pw1.println(MekHqXmlUtil.indentStr(indent) + "<shoppingList>");
for(IAcquisitionWork shoppingItem : shoppingList) {
//don't write refits to the shopping list - we will add them manually
//when we parse units and find refit kits that have not been found
if(shoppingItem instanceof Part && !(shoppingItem instanceof Refit)) {
((Part)shoppingItem).writeToXml(pw1, indent+1);
}
else if(shoppingItem instanceof UnitOrder) {
((UnitOrder)shoppingItem).writeToXml(pw1, indent+1);
}
}
pw1.println(MekHqXmlUtil.indentStr(indent) + "</shoppingList>");
}
public static ShoppingList generateInstanceFromXML(Node wn, Campaign c, Version version) {
ShoppingList retVal = new ShoppingList();
NodeList nl = wn.getChildNodes();
try {
for (int x=0; x<nl.getLength(); x++) {
Node wn2 = nl.item(x);
if (wn2.getNodeName().equalsIgnoreCase("part")) {
Part p = Part.generateInstanceFromXML(wn2, version);
p.setCampaign(c);
if(p instanceof IAcquisitionWork) {
retVal.shoppingList.add((IAcquisitionWork)p);
}
}
else if (wn2.getNodeName().equalsIgnoreCase("unitOrder")) {
UnitOrder u = UnitOrder.generateInstanceFromXML(wn2, c, version);
u.campaign = c;
if(null != u.getEntity()) {
retVal.shoppingList.add(u);
}
}
}
} catch (Exception ex) {
// Doh!
MekHQ.logError(ex);
}
return retVal;
}
public void restore() {
ArrayList<IAcquisitionWork> newShoppingList = new ArrayList<IAcquisitionWork>();;
for(IAcquisitionWork shoppingItem : shoppingList) {
if(shoppingItem instanceof MissingEquipmentPart) {
((MissingEquipmentPart)shoppingItem).restore();
if(null != ((MissingEquipmentPart)shoppingItem).getType()) {
newShoppingList.add(shoppingItem);
}
} else {
newShoppingList.add(shoppingItem);
}
}
shoppingList = newShoppingList;
}
public void removeZeroQuantityFromList() {
ArrayList<IAcquisitionWork> newShoppingList = new ArrayList<IAcquisitionWork>();
for(IAcquisitionWork shoppingItem : shoppingList) {
if(shoppingItem.getQuantity() > 0) {
newShoppingList.add(shoppingItem);
}
}
shoppingList = newShoppingList;
}
public ArrayList<IAcquisitionWork> getPartList() {
ArrayList<IAcquisitionWork> partList = new ArrayList<IAcquisitionWork>();
for(IAcquisitionWork shoppingItem : shoppingList) {
if(shoppingItem instanceof Part) {
partList.add(shoppingItem);
}
}
return partList;
}
public ArrayList<IAcquisitionWork> getUnitList() {
ArrayList<IAcquisitionWork> unitList = new ArrayList<IAcquisitionWork>();
for(IAcquisitionWork shoppingItem : shoppingList) {
if(shoppingItem instanceof UnitOrder) {
unitList.add(shoppingItem);
}
}
return unitList;
}
private boolean isSameEquipment(Object equipment, Object newEquipment) {
if(newEquipment instanceof Part && equipment instanceof Part) {
if(((Part)equipment).isSamePartType((Part)newEquipment)) {
return true;
}
}
if(newEquipment instanceof Entity && equipment instanceof Entity) {
Entity entityA = (Entity)newEquipment;
Entity entityB = (Entity)equipment;
if(entityA.getChassis().equals(entityB.getChassis())
&& entityA.getModel().equals(entityB.getModel())) {
return true;
}
}
return false;
}
private long getTrueBuyCost(IAcquisitionWork item, Campaign campaign) {
long cost = Long.MIN_VALUE;
if((item instanceof UnitOrder && campaign.getCampaignOptions().payForUnits()) ||
(item instanceof Part && campaign.getCampaignOptions().payForParts())
) {
cost = item.getBuyCost();
}
return cost;
}
}