package jamel.basicModel.households; import java.util.function.Consumer; import jamel.basicModel.banks.Account; import jamel.basicModel.banks.Amount; import jamel.basicModel.banks.Bank; import jamel.basicModel.banks.Cheque; import jamel.basicModel.firms.Goods; import jamel.basicModel.firms.Supplier; import jamel.basicModel.firms.Supply; import jamel.util.Agent; import jamel.util.AgentDataset; import jamel.util.JamelObject; import jamel.util.NotUsedException; import jamel.util.Sector; /** * Represents a shareholder. */ public class BasicShareholder extends JamelObject implements Agent, Shareholder { /** * Returns the specified action. * * @param phaseName * the name of the action. * @return the specified action. */ static public Consumer<? super Agent> getAction(final String phaseName) { final Consumer<? super Agent> action; switch (phaseName) { case "opening": action = (agent) -> { ((BasicShareholder) agent).open(); }; break; case "consumption": action = (agent) -> { ((BasicShareholder) agent).consumption(); }; break; case "closure": action = (agent) -> { ((BasicShareholder) agent).close(); }; break; default: throw new IllegalArgumentException(phaseName); } return action; } /** * The bank account of this shareholder. */ private final Account account; /** * The dataset. */ final private AgentDataset dataset; /** * The amount of the dividends received for this period. */ private final Amount dividends = new Amount(); /** * The id of this agent. */ final private int id; /** * A flag that indicates whether this shareholder is open or not. */ private boolean open; /** * The parent sector. */ final private Sector sector; /** * The sector of the suppliers. */ private final Sector supplierSector; /** * Creates a new shareholder. * * @param sector * the sector of this shareholder. * @param id * the id of this shareholder. */ public BasicShareholder(final Sector sector, final int id) { super(sector.getSimulation()); this.sector = sector; this.id = id; this.account = ((Bank) this.getSimulation().getSector("Banks").select(1)[0]).openAccount(this); this.supplierSector = this.getSimulation().getSector("Firms"); // TODO "Firms" sould be a parameter if (this.supplierSector == null) { throw new RuntimeException("Supplier sector is missing."); } this.dataset = new AgentDataset(this); } /** * Closes the shareholder at the end of the period. */ private void close() { if (!this.open) { throw new RuntimeException("Already closed."); } this.dataset.put("countAgent", 1); this.dataset.put("money", this.account.getAmount()); this.open = false; } /** * The consumption phase. */ private void consumption() { if (!this.open) { throw new RuntimeException("Closed."); } long consumptionVolume = 0; long consumptionValue = 0; long budget = this.account.getAmount(); this.dataset.put("consumptionBudget", budget); if (budget > 0) { final Agent[] selection = this.supplierSector.select(10); while (budget > 0) { Agent supplier = null; for (int i = 0; i < selection.length; i++) { if (selection[i] != null) { final Supply supply_i = ((Supplier) selection[i]).getSupply(); if (supply_i == null || supply_i.getPrice() > budget || supply_i.getVolume() == 0) { selection[i] = null; } else if (supplier == null) { supplier = selection[i]; selection[i] = null; } else if (((Supplier) supplier).getSupply().getPrice() > supply_i.getPrice()) { final Agent disappointing = supplier; supplier = selection[i]; selection[i] = disappointing; } } } if (supplier == null) { break; } final Supply supply = ((Supplier) supplier).getSupply(); final long spending; final int consumVol; if (supply.getTotalValue() <= budget) { consumVol = supply.getVolume(); spending = (long) (consumVol * supply.getPrice()); if (spending != supply.getTotalValue()) { throw new RuntimeException("Inconsistency."); } } else { consumVol = (int) (budget / supply.getPrice()); spending = (long) (consumVol * supply.getPrice()); } final Goods goods = supply.purchase(consumVol, this.account.issueCheque(supply.getSupplier(), spending)); if (goods.getVolume() != consumVol) { throw new RuntimeException("Bad volume"); } budget -= spending; consumptionValue += spending; consumptionVolume += goods.getVolume(); goods.consume(); } } this.dataset.put("consumptionVolume", consumptionVolume); this.dataset.put("consumptionValue", consumptionValue); // TODO updater les chiffres de la consommation et de l'épargne. } /** * Opens the shareholder at the beginning of the period. */ private void open() { if (this.open) { throw new RuntimeException("Already open."); } this.open = true; this.dividends.cancel(); } @Override public void acceptDividendCheque(Cheque cheque) { this.account.deposit(cheque); // TODO comptabiliser ce dépôt pour calculer le revenu de l'agent. // a des fins statistiques mais aussi comportementales. } @Override public Long getAssetTotalValue() { throw new NotUsedException(); } @Override public int getBorrowerStatus() { throw new NotUsedException(); } @Override public Double getData(String dataKey, String period) { return this.dataset.getData(dataKey); } @Override public String getName() { return "shareholder_" + this.id; } @Override public Sector getSector() { return this.sector; } @Override public void goBankrupt() { throw new NotUsedException(); } @Override public boolean isBankrupted() { throw new NotUsedException(); } @Override public boolean isSolvent() { throw new NotUsedException(); } }