package lab01;

import java.util.ArrayList;
import java.util.List;


/**
 * Portfolio containing a list of holdings and cash balance
 * 
 * @author CS-140 Teaching Staff
 *
 */
public class Portfolio {

	/* Fields... */
	private List<Holding> holdings; // List of current stock holdings
	private double cash; // Current cash balance
	private double startingCash; // Initial value of this portfolio

	/**
	 * Create a new portfolio with no holdings, and parameter specified 
	 * initial cash balance
	 * 
	 * @param cash - Initial cash value for this portfolio
	 */
	public Portfolio(double cash) {
		this.holdings = new ArrayList<Holding>();
		this.cash = cash;
		this.startingCash = cash;
	}

	/**
	 * Purchase a new holding of specified number of shares for the 
	 * specified company. Use the purchase price of the current value of 
	 * the stock, as specified by the Quotes parameter
	 * 
	 * @param company - the ticker-tape symbol for this purchase
	 * @param shares - the maximum number of shares to buy
	 * @param price - the purchase price/share 
	 * @return number of shares actually purchased
	 */
	public int buy(String company,int shares,double price) {
		if (cash < (shares * price)) {
			shares = (int) (cash / price); // Cast to int rounds down;
			if (shares == 0) return 0;
			System.out.println("Low on cash... you can only buy " + shares 
					+ " shares of " + company + ".");
		}
		Holding bought = new Holding(company,shares,price);
		this.holdings.add(bought);
		cash -=  shares * price;
		System.out.println("You bought " + shares +" shares of " + company 
				+ " at $" + price);
		return shares;
	}

	/**
	 * Sell shares from the portfolio (if you own them) for the specified 
	 * price. Increase the cash balance by the sale amount.
	 * 
	 * @param company - this ticker tape symbol for this sale
	 * @param shares - the maximum number of shares to sell
	 * @param price - The price of each share when sold
	 * @return true  
	 */
	public boolean sell(String company,int shares,double price) {
		// In the real world, there may be different capital gain taxes, 
		// depending on whether older or newer purchased shares are sold.
		// For this, simplified case, sell first shares found
		int sellReq = shares;
		int netGain = 0;
		for (Holding holding : this.holdings ) {
			if (holding.getCompany().equals(company)) {
				netGain +=  holding.salesNet(shares, price);
				shares -=  holding.sell(shares);
			}
		}
		int sold = sellReq-shares;
		removeEmptyHoldings();
		if (sold>0) { 
			System.out.println("You sold " + sold +" shares of " + company 
					+ " at $" + price + " for a net gain of $" + netGain );
			this.cash +=  sold * price;
		}
		return true;
	}

	/**
	 * Buy shares if the price is below a trigger price
	 * @param company - ticker tape symbol of shares to buy
	 * @param maxPrice - buy shares if the quote prices is lower than this 
	 * maximum price
	 * @param shares - Maximum number of shares to buy
	 * @param quote - A quote object that defines the current price 
	 * offered for a stock
	 * @return - true if a purchase was attempted, false otherwise
	 */
	public boolean buyTrigger(String company,double maxPrice, int shares,
			Quote quote) {
		if (quote.getCompany().equals(company)) {
			double price = quote.getPrice();
			if (price<maxPrice) {
				this.buy(company,shares,price);
				return true;
			}	
		}
		return false;
	}

	/**
	 * Sell specified shares if the sale prices is above a minimum trigger 
	 * price
	 * @param company - Ticker tape symbol for the stock to be sold
	 * @param minPrice - Sell shares if the quote is above this minimum 
	 * price
	 * @param shares - Maximum number of shares to sell
	 * @param quote - A quote object that defines the current price 
	 * offered for a stock
	 * @return - True if attempted a sale, false if not
	 */
	public boolean sellTrigger(String company,double minPrice, int shares,
			Quote quote) {
		if (quote.getCompany().equals(company)) {
			double price = quote.getPrice();
			if (price >= minPrice) {
				this.sell(company, shares, price);
				return true;
			}
		}
		return false;
	}

	/**
	 * Find the current value of this portfolio, based on the quotes in 
	 * the board parameter
	 * @param board - Latest list of quotes for all companies
	 * @return Current value of the portfolio
	 */
	public double currentValue(Quotes board) {
		double value = 0;
		for (Holding holding : holdings) {
			value +=  holding.getShares() * board.getPrice(holding.getCompany());
		}
		return value+this.cash;
	}

	/**
	 * Determine the net value of the portfolio, based on the current 
	 * quotes in the board parameter
	 * 
	 * @param board - Latest list of quotes for all companies
	 * @return net increase or decrease in value of all stock and cash 
	 * holdings, compared to the initial portfolio value
	 */
	public double netValue(Quotes board) {
		return this.currentValue(board) - this.startingCash;
	}

	/**
	 * Generate a report (to "out") for this portfolio, based on the quotes 
	 * in the board parameter
	 * @param board - Latest list of quotes for all companies
	 */
	public void report(Quotes board) {
		System.out.println("Portfolio Report...");
		System.out.format("%10s | %5s | %6s | %6s | %8s | %8s |\n",
				"Company","Shrs","Purch","Curr","Value","Net");
		System.out.println("-----------|-------|--------|--------|"
				+ "----------|----------|");
		for (Holding holding : holdings) {
			double cval = board.getPrice(holding.getCompany());
			System.out.format("%10s | %5d | %6.2f | %6.2f | %8.2f | %8.2f |\n",
					holding.getCompany(), holding.getShares(),
					holding.getPurchasePrice(), cval,cval*holding.getShares(),
					holding.getShares()*(cval-holding.getPurchasePrice()));
		}
		System.out.println("-----------|-------|--------|--------|"
				+ "----------|----------|");
		System.out.printf("    Cash on Hand: $%8.2f\n", this.cash);
		System.out.format("Net Value Change: $%8.2f\n",this.netValue(board));
	}

	/**
	 * Private function to remove any empty holdings
	 */
	private void removeEmptyHoldings() {
		int i = 0;
		while(i < holdings.size() ) {
			if (0 == holdings.get(i).getShares()) {
				holdings.remove(i);
			} else {
				i++;
			}
		}
	}

}
