@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
	private String name;
	private String protocol;
	private String url;
	private List<String> admins;

	private final Db db = new Db();
	private final Rest rest = new Rest();
	private final Dropbox dropbox = new Dropbox();
	private final Email email = new Email();
	private final Report report = new Report();

	@Data
	public static class Db {
		private String ip;
		private String driver;
		private String name;
		private String user;
		private String password;
	}

	@Data
	public static class Rest {
		private final Client client = new Client();

		@Data
		public static class Client {
			private int maxTotalConnections;
			private int maxConnectionsPerRoute;
			private int readTimeout;
		}
	}

	@Data
	public static class Dropbox {
		private String clientId;
		private String accessToken;
	}

	@Data
	public static class Email {
		private final Smtp smtp = new Smtp();
		private final From from = new From();

		@Data
		public static class Smtp {
			private String host;
			private String port;
		}

		@Data
		public static class From {
			private String username;
			private String password;
			private String from;
		}
	}

	@Data
	public static class Report {
		private String ireport;
	}
} @Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Index {

	private int id;
	private String yahooId;
	private Double value;
	private Date date;
	private Double shareValue;

	public final Date getDate() {
		return date != null ? (Date) date.clone() : null;
	}

	public final void setDate(final Date date) {
		this.date = (Date) date.clone();
	}
} public class Portfolio {
	/**
	 * Precision of calculation
	 **/
	private final MathContext mathContext = MathContext.DECIMAL32;
	private static final int PERCENT = 100;
	@Getter
	@Setter
	private int id;
	@Getter
	@Setter
	private int userId;
	@Getter
	@Setter
	private Currency currency;
	@Getter
	@Setter
	private Double liquidity;
	@Getter
	@Setter
	private List<Equity> equities;
	@Getter
	@Setter
	private List<ShareValue> shareValues;
	@Getter
	@Setter
	private List<Account> accounts;
	@Getter
	@Setter
	private Map<String, List<Index>> indexes;

	// Calculated
	@Getter
	private Double totalQuantity;
	@Getter
	private Double averageUnitCostPrice;
	@Getter
	private Double averageQuotePrice;
	private Double totalValue;
	private Double totalPlusMinusValue;
	private Double yieldYear;
	private Double yieldYearPerc;
	private Double totalGain;
	private Date lastCompanyUpdate;
	/**
	 * Best performance of the share value
	 **/
	private Double maxShareValue;
	/**
	 * Date of the best performance
	 **/
	private Date maxShareValueDate;
	/**
	 * Current share value gain
	 **/
	private Double currentShareValuesGain;
	/**
	 * Current share value gain in percentage
	 **/
	private Double currentShareValuesGainPorcentage;
	/**
	 * Current share value yield
	 **/
	private Double currentShareValuesYield;
	/**
	 * Current share value taxes
	 **/
	private Double currentShareValuesTaxes;
	/**
	 * Current share value taxes
	 **/
	private Double currentShareValuesVolume;
	@Getter
	private Double totalVariation;
	@Getter
	private Double totalGainToday;
	/**
	 * Map that represents data of sector chart
	 **/
	private Map<String, Double> chartSectorData;
	/**
	 * Map that represents data of capitalization chart
	 **/
	private Map<String, Double> chartCapData;
	/**
	 * Maps that represents data of share value (user + cac40 + s&p500
	 **/
	private Map<Date, Double> chartShareValueData, chartShareValueData2, chartShareValueData3;
	/**
	 * Chart objects
	 **/
	private IChart timechart, timeValueChart, piechartsector, piechartcap;

	/**
	 * Constructor
	 */
	public Portfolio() {
		totalQuantity = 0d;
		averageUnitCostPrice = 0d;
		averageQuotePrice = 0d;
		totalValue = 0d;
		totalPlusMinusValue = 0d;
		yieldYear = 0d;
		totalGain = 0d;
		yieldYearPerc = 0d;
		liquidity = 0d;
		indexes = new HashMap<>();
	}

	/**
	 * Get total value
	 *
	 * @return the total value
	 */
	public final Double getTotalValue() {
		return totalValue + getLiquidity();
	}

	/**
	 * Get total plus minus value
	 *
	 * @return the total plus minus value
	 */
	public final Double getTotalPlusMinusValue() {
		return totalPlusMinusValue;
	}

	/**
	 * Get total plus minus value in absolute
	 *
	 * @return the total plus minus value in absolute
	 */
	public final Double getTotalPlusMinusValueAbsolute() {
		return Math.abs(totalPlusMinusValue);
	}

	/**
	 * Get yield year
	 *
	 * @return the yield year
	 */
	public final Double getYieldYear() {
		return yieldYear;
	}

	/**
	 * Get total gain
	 *
	 * @return the total gain
	 */
	public final Double getTotalGain() {
		return totalGain;
	}

	/**
	 * Get Yield per year in percentage
	 *
	 * @return a double
	 */
	public final Double getYieldYearPerc() {
		return yieldYearPerc;
	}

	/**
	 * Get last company update date
	 *
	 * @return a date
	 */
	public final Date getLastCompanyUpdate() {
		if (lastCompanyUpdate != null) {
			return (Date) lastCompanyUpdate.clone();
		} else {
			return null;
		}
	}

	/**
	 * Set last company ypdate date
	 *
	 * @param lastCompanyUpdate the date
	 */
	public final void setLastCompanyUpdate(final Date lastCompanyUpdate) {
		if (lastCompanyUpdate != null) {
			this.lastCompanyUpdate = (Date) lastCompanyUpdate.clone();
		}
	}

	/**
	 * This function generates all calculated field
	 */
	public final void compute() {
		Double totalUnitCostPrice = 0d;
		Double totalAverageQuotePrice = 0d;
		Double totalOriginalValue = 0d;
		totalVariation = 0d;
		double totalValueStart = 0;
		totalGainToday = 0d;
		Date lastUpdate = null;
		if (equities != null) {
			for (final Equity equity : equities) {
				totalQuantity += equity.getQuantity();
				totalUnitCostPrice += equity.getUnitCostPrice();
				totalAverageQuotePrice += equity.getCompany().getQuote() * equity.getParity();
				totalValue += equity.getValue();
				totalOriginalValue += equity.getQuantity() * equity.getUnitCostPrice() * equity.getCurrentParity();
				yieldYear += equity.getYieldYear();
				totalGain += equity.getPlusMinusUnitCostPriceValue();
				if (equity.getCompany().getRealTime()) {
					if (lastUpdate == null) {
						lastUpdate = equity.getCompany().getLastUpdate();
					} else {
						if (equity.getCompany().getLastUpdate() != null && lastUpdate.after(equity.getCompany().getLastUpdate())) {
							lastUpdate = equity.getCompany().getLastUpdate();
						}
					}
				}
				if (equity.getCompany().getChange() != null) {
					double valueStart = equity.getValue() / (equity.getCompany().getChange() / PERCENT + 1);
					totalValueStart += valueStart;
					totalGainToday += valueStart * equity.getCompany().getChange() / PERCENT;
				}

			}
			totalVariation = totalValueStart == 0 ? totalValueStart : ((totalValueStart + totalGainToday) / totalValueStart - 1) * PERCENT;
			averageUnitCostPrice = totalUnitCostPrice / equities.size();
			averageQuotePrice = totalAverageQuotePrice / equities.size();
			totalPlusMinusValue = ((totalValue - totalOriginalValue) / totalOriginalValue) * PERCENT;
			yieldYearPerc = yieldYear / getTotalValue() * PERCENT;
			setLastCompanyUpdate(lastUpdate);
		}
	}

	public final Double getTotalGainTodayAbsolute() {
		return Math.abs(totalGainToday);
	}

	/**
	 * Get chart sector data
	 *
	 * @return a map
	 */
	protected final Map<String, Double> getChartSectorData() {
		if (chartSectorData == null) {
			final Map<String, Double> data = new HashMap<>();
			for (final Equity equity : getEquities()) {
				if (equity.getCompany().getFund()) {
					addEquityValueToMap(data, Constants.FUND, equity);
				} else {
					final String sector = equity.getCurrentSector();
					if (StringUtils.isEmpty(sector)) {
						addEquityValueToMap(data, Constants.UNKNOWN, equity);
					} else {
						addEquityValueToMap(data, sector, equity);
					}
				}
			}
			chartSectorData = new TreeMap<>();
			chartSectorData.putAll(data);
		}
		return chartSectorData;
	}

	private void addEquityValueToMap(final Map<String, Double> data, final String key, final Equity equity) {
		if (data.containsKey(key)) {
			data.put(key, data.get(key) + equity.getValue());
		} else {
			data.put(key, equity.getValue());
		}
	}

	/**
	 * Get chart share value data
	 *
	 * @return a map
	 */
	protected final Map<Date, Double> getChartShareValueData() {
		Map<Date, Double> data = new HashMap<>();
		List<ShareValue> shareValuess = getShareValues();
		int max = shareValuess.size();
		double base = shareValuess.get(max - 1).getShareValue();
		for (int i = max - 1; i != -1; i--) {
			ShareValue temp = shareValuess.get(i);
			Double value = temp.getShareValue() * PERCENT / base;
			data.put(temp.getDate(), value);
		}
		chartShareValueData = new TreeMap<>();
		chartShareValueData.putAll(data);
		return chartShareValueData;
	}

	/**
	 * Generate chart share value data
	 */
	protected final void getChartShareValueData2() {
		if (chartShareValueData2 == null && chartShareValueData3 == null) {
			Map<Date, Double> data = new HashMap<>();
			Map<Date, Double> data2 = new HashMap<>();
			List<ShareValue> shareValuess = getShareValues();
			int max = shareValuess.size();
			for (int i = max - 1; i != -1; i--) {
				ShareValue temp = shareValuess.get(i);
				Double value = temp.getPortfolioValue();
				Double liqui = temp.getLiquidities();
				data.put(temp.getDate(), value);
				if (liqui != null) {
					data2.put(temp.getDate(), liqui);
				}
			}
			chartShareValueData2 = new TreeMap<>();
			chartShareValueData2.putAll(data);
			chartShareValueData3 = new TreeMap<>();
			chartShareValueData3.putAll(data2);
		}
	}

	/**
	 * Get chart capitalization data
	 *
	 * @return a map
	 */
	protected final Map<String, Double> getChartCapData() {
		if (chartCapData == null) {
			Map<String, Double> data = new HashMap<>();
			for (Equity equity : getEquities()) {
				if (!equity.getCompany().getFund()) {
					MarketCapitalization marketCap = equity.getMarketCapitalizationType();
					if (marketCap == null) {
						addEquityValueToMap(data, Constants.UNKNOWN, equity);
					} else {
						addEquityValueToMap(data, marketCap.getValue(), equity);
					}
				} else {
					addEquityValueToMap(data, Constants.UNKNOWN, equity);
				}
			}
			chartCapData = new TreeMap<>();
			chartCapData.putAll(data);
		}
		return chartCapData;
	}

	/**
	 * Get a list of yahoo id representing equities that do not have real time data
	 *
	 * @return a list of yahoo id
	 */
	public final List<String> getCompaniesYahooIdRealTime() {
		return getEquities().stream()
			.filter(equity -> equity.getCompany().getRealTime())
			.map(equity -> equity.getCompany().getYahooId())
			.collect(Collectors.toList());
	}

	/**
	 * Add indexes
	 *
	 * @param indexes a list of indexes
	 */
	public final void addIndexes(final List<Index> indexes) {
		if (indexes.size() > 0) {
			String index = indexes.get(0).getYahooId();
			this.indexes.put(index, indexes);
		}
	}

	/**
	 * Get portfolio review. An horrible function.
	 *
	 * @return a string
	 */
	public final String getPortfolioReview() {
		final StringBuilder sb = new StringBuilder();
		sb.append("<table class=\"shareValueTableDetails\">");
		for (final Equity equity : getEquities()) {
			sb.append("<tr><td width=200px><b>")
				.append(equity.getCurrentName())
				.append("</b></td><td width=180px>")
				.append(equity.getQuantity())
				.append(" * ")
				.append(equity.getCompany().getQuote());
			if (equity.getCompany().getCurrency() != getCurrency()) {
				sb.append(" * ").append(getCurrency().getParity(equity.getCompany().getCurrency()));
			}
			sb.append("</td><td>").append(equity.getValue()).append(" (").append(getCurrency().getCode()).append(")</td></tr>");
		}
		sb.append("<tr><td colspan=3><b>Liquidity:</b> ").append(getLiquidity()).append(" (").append(getCurrency().getCode()).append(")</td></tr>");
		sb.append("<tr><td colspan=3><b>Total:</b> ").append(new BigDecimal(getTotalValue(), mathContext).doubleValue()).append(" (").append(getCurrency().getCode()).append(")</td></tr>");
		sb.append("</table>");
		return sb.toString();
	}

	/**
	 * Get pie chart sector
	 *
	 * @return the chart
	 */
	public final IChart getPieChartSector() {
		if (piechartsector == null) {
			final Map<String, Double> map = getChartSectorData();
			piechartsector = new PieChart(map);
			piechartsector.generate();
		}
		return piechartsector;
	}

	/**
	 * Get pie chart capitalization
	 *
	 * @return the chart
	 */
	public final IChart getPieChartCap() {
		if (piechartcap == null) {
			final Map<String, Double> map = getChartCapData();
			piechartcap = new PieChart(map);
			piechartcap.generate();
		}
		return piechartcap;
	}

	/**
	 * Get time value chart
	 *
	 * @return the chart
	 */
	public final IChart getTimeValueChart() {
		if (timeValueChart == null && shareValues.size() > 0) {
			getChartShareValueData2();
			timeValueChart = new TimeValueChart(chartShareValueData2, chartShareValueData3, shareValues.get(0).getDate());
			timeValueChart.generate();
		}
		return timeValueChart;
	}

	/**
	 * Get time chart
	 *
	 * @return the chart
	 */
	public final IChart getTimeChart() {
		if (timechart == null && shareValues.size() > 0) {
			Map<Date, Double> map = getChartShareValueData();
			// Modifying first element of each to make it pretty in chart
			timechart = new TimeChart(map, indexes, shareValues.get(0).getDate());
			timechart.generate();
		}
		return timechart;
	}

	/**
	 * Get one account with its name
	 *
	 * @param name the account name
	 * @return the account
	 */
	public final Optional<Account> getAccount(final String name) {
		return accounts == null
			? Optional.empty()
			: accounts.stream().filter(account -> account.getName().equals(name)).findFirst();
	}

	/**
	 * Get one account with its id
	 *
	 * @param id the id
	 * @return the account
	 */
	public final Optional<Account> getAccount(final int id) {
		return accounts == null
			? Optional.empty()
			: accounts.stream().filter(account -> account.getId() == id).findFirst();
	}

	/**
	 * Get the first account created
	 *
	 * @return the account
	 */
	public final Optional<Account> getFirstAccount() {
		return accounts == null
			? Optional.empty()
			: accounts.stream().filter(account -> !account.getDel()).findFirst();
	}

	/**
	 * Get a string containing the list of sector
	 *
	 * @return a string
	 */
	public final String getHTMLSectorByCompanies() {
		final Map<String, List<Equity>> map = getSectorByCompanies();
		return extractHTMLfromMap(map);
	}

	protected Map<String, List<Equity>> getSectorByCompanies() {
		final Map<String, List<Equity>> map = new TreeMap<>();
		List<Equity> companies;
		for (final Equity equity : getEquities()) {
			if (equity.getCompany().getFund()) {
				companies = map.getOrDefault(Constants.FUND, new ArrayList<>());
				companies.add(equity);
				map.putIfAbsent(Constants.FUND, companies);
			} else {
				final String sector = equity.getCurrentSector();
				if (StringUtils.isEmpty(sector)) {
					companies = map.getOrDefault(Constants.UNKNOWN, new ArrayList<>());
					companies.add(equity);
					map.putIfAbsent(Constants.UNKNOWN, companies);
				} else {
					companies = map.getOrDefault(sector, new ArrayList<>());
					companies.add(equity);
					map.putIfAbsent(sector, companies);
				}
			}
		}
		return map;
	}

	private String extractHTMLfromMap(final Map<String, List<Equity>> map) {
		final StringBuilder res = new StringBuilder("var companies = [");
		boolean addComma = false;
		for (final Entry<String, List<Equity>> entry : map.entrySet()) {
			if (addComma) {
				res.append(",");
			}
			addComma = true;
			res.append("'");
			entry.getValue().forEach(equity -> res.append(" - ").append(equity.getCurrentName()).append("<br>"));
			res.append("'");
		}
		res.append("];");
		return res.toString();
	}

	/**
	 * Get a string containing the list of capitalization
	 *
	 * @return a string
	 */
	public final String getHTMLCapByCompanies() {
		final Map<String, List<Equity>> map = getGapByCompanies();
		return extractHTMLfromMap(map);
	}

	protected Map<String, List<Equity>> getGapByCompanies() {
		final Map<String, List<Equity>> map = new TreeMap<>();
		List<Equity> companies;
		for (final Equity equity : getEquities()) {
			if (equity.getMarketCapitalizationType().getValue() == null || equity.getCompany().getFund()) {
				equity.setMarketCapitalizationType(MarketCapitalization.UNKNOWN);
			}
			companies = map.getOrDefault(equity.getMarketCapitalizationType().getValue(), new ArrayList<>());
			companies.add(equity);
			map.put(equity.getMarketCapitalizationType().getValue(), companies);
		}
		return map;
	}

	/**
	 * This function generates the share value info (because it muse be calculated each time)
	 */
	private void generateShareValueInfo() {
		List<ShareValue> shareValuess = getShareValues();
		if (shareValuess.size() != 0 && maxShareValue == null && maxShareValueDate == null && currentShareValuesYield == null
			&& currentShareValuesTaxes == null && currentShareValuesVolume == null && currentShareValuesGain == null
			&& currentShareValuesGainPorcentage == null) {
			ShareValue lastShareValue = shareValuess.get(0);
			ShareValue firstShareValue = shareValuess.get(shareValuess.size() - 1);
			double liquidityMov = 0;
			currentShareValuesYield = 0d;
			currentShareValuesTaxes = 0d;
			currentShareValuesVolume = 0d;

			double max = shareValuess.get(0).getShareValue();
			Date date = shareValuess.get(0).getDate();
			for (int i = 0; i <= shareValuess.size() - 1; i++) {
				ShareValue sv = shareValuess.get(i);
				double current = sv.getShareValue();
				final Account account = getAccount(sv.getAccount().getName()).orElseThrow(() -> new NotFoundException(sv.getAccount().getName()));
				if (current > max) {
					max = current;
					date = sv.getDate();
				}
				if (sv.getLiquidityMovement() != null && i != shareValuess.size() - 1) {
					liquidityMov += sv.getLiquidityMovement() * account.getParity();
				}
				if (sv.getYield() != null) {
					currentShareValuesYield += sv.getYield() * account.getParity();
				}
				if (sv.getTaxe() != null) {
					currentShareValuesTaxes += sv.getTaxe() * account.getParity();
				}
				if (sv.getBuy() != null) {
					currentShareValuesVolume += sv.getBuy() * account.getParity();
				}
				if (sv.getSell() != null) {
					currentShareValuesVolume += sv.getBuy() * account.getParity();
				}
			}
			// log.info("Liquidity Movement: " + liquidityMov);
			double depart = liquidityMov + firstShareValue.getPortfolioValue();
			double fin = lastShareValue.getPortfolioValue();
			// log.info("Valeur portefeuille départ: " + depart +
			// " ( Liquidity Movement + valeur de départ soit " + liquidityMov + " + "
			// + firstShareValue.getPortfolioValue() + ")");
			// log.info("Valeur portefeuille arrivée: " + fin);
			// log.info("Dividendes recues: " + currentShareValuesYield +
			// " (déja incluse dans le portefeuille d'arrivée");
			// log.info("Gain (arrivée - départ): " + (fin - depart));
			// log.info("Gain en pourcentage (arrivée - départ) / départ * 100: " + (fin -
			// depart)
			// / depart * 100);

			currentShareValuesGain = fin - depart;

			double last = lastShareValue.getShareValue();
			double first = firstShareValue.getShareValue();
			currentShareValuesGainPorcentage = last * PERCENT / first - PERCENT;
			maxShareValue = max;
			maxShareValueDate = date;
		}
	}

	/**
	 * Get max share value
	 *
	 * @return a double
	 */
	public final Double getMaxShareValue() {
		generateShareValueInfo();
		return maxShareValue;
	}

	/**
	 * Get max share value date
	 *
	 * @return a double
	 */
	public final Date getMaxShareValueDate() {
		generateShareValueInfo();
		return maxShareValueDate != null
			? (Date) maxShareValueDate.clone()
			: null;
	}

	/**
	 * Get current share value yield
	 *
	 * @return a double
	 */
	public final Double getCurrentShareValuesYield() {
		generateShareValueInfo();
		return currentShareValuesYield;
	}

	/**
	 * Get current share value taxes
	 *
	 * @return a double
	 */
	public final Double getCurrentShareValuesTaxes() {
		generateShareValueInfo();
		return currentShareValuesTaxes;
	}

	/**
	 * Get current share values volume
	 *
	 * @return a double
	 */
	public final Double getCurrentShareValuesVolume() {
		generateShareValueInfo();
		return currentShareValuesVolume;
	}

	/**
	 * Get current share values gain
	 *
	 * @return a double
	 */
	public final Double getCurrenShareValuesGain() {
		generateShareValueInfo();
		return currentShareValuesGain;
	}

	/**
	 * Get current share values gain in Percentage
	 *
	 * @return a double
	 */
	public final Double getCurrenShareValuesGainPorcentage() {
		generateShareValueInfo();
		return currentShareValuesGainPorcentage;
	}

	/**
	 * Get a view of the object in json
	 *
	 * @return a JSONObject
	 */
	public final JsonObject getJSONObject() {
		JsonObject json = new JsonObject();
		json.addProperty("id", getId());
		json.addProperty("userId", getUserId());
		json.add(Constants.CURRENCY, getCurrency().getJSONObject());
		json.addProperty(Constants.LIQUIDITY, getLiquidity());
		json.addProperty("lastUpdate", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(getLastCompanyUpdate()));
		json.addProperty("totalValue", getTotalValue());
		json.addProperty("totalGain", getTotalGain());
		json.addProperty("totalPlusMinusValue", getTotalPlusMinusValue());
		json.addProperty("yieldYear", getYieldYear());
		json.addProperty("yieldYearPerc", getYieldYearPerc());
		JsonArray equitiess = new JsonArray();
		for (Equity e : getEquities()) {
			equitiess.add(e.getJSONObject());
		}
		json.add("equities", equitiess);
		JsonArray shareValuess = new JsonArray();
		int i = 0;
		while (i < this.shareValues.size() && i < 11) {
			shareValuess.add(this.shareValues.get(i).getJSONObject());
			i++;
		}
		json.add("shareValues", shareValuess);
		JsonArray accs = new JsonArray();
		for (final Account acc : this.accounts) {
			accs.add(acc.getJSONObject());
		}
		json.add("accounts", accs);
		JsonObject jsonPerf = new JsonObject();
		jsonPerf.addProperty("gain", currentShareValuesGain);
		jsonPerf.addProperty("performance", currentShareValuesGainPorcentage);
		jsonPerf.addProperty("yield", currentShareValuesYield);
		jsonPerf.addProperty("taxes", currentShareValuesTaxes);
		json.add("performance", jsonPerf);

		if (getTimeChart() != null) {
			TimeChart timeChart = (TimeChart) getTimeChart();
			json.addProperty("chartShareValueColors", timeChart.getColors());
			json.addProperty("chartShareValueData", timeChart.getData());
			Date date = timeChart.getDate();
			String formatDate = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date);
			json.addProperty("chartShareValueDate", formatDate);
			json.addProperty("chartShareValueDraw", timeChart.getDraw());
		}

		PieChart pieChart = (PieChart) getPieChartSector();
		json.addProperty("chartSectorData", pieChart.getData());
		json.addProperty("chartSectorTitle", pieChart.getTitle());
		json.addProperty("chartSectorDraw", pieChart.getDraw());
		json.addProperty("chartSectorCompanies", getHTMLSectorByCompanies());

		pieChart = (PieChart) getPieChartCap();
		json.addProperty("chartCapData", pieChart.getData());
		json.addProperty("chartCapTitle", pieChart.getTitle());
		json.addProperty("chartCapDraw", pieChart.getDraw());
		json.addProperty("chartCapCompanies", getHTMLCapByCompanies());

		json.addProperty("totalVariation", totalVariation);

		return json;
	}
} @Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User implements Serializable {

	private static final long serialVersionUID = -7736017495914032958L;

	private int id;
	@NotNull
	private String login;
	private String password;
	private String email;
	private String locale;
	private String timeZone;
	private Integer updateHourTime;
	private Boolean updateSendMail;
	private String datePattern;
	private String datePatternWithoutHourMin;
	private Boolean allow;
	private Date lastUpdate;

	public User(final String login, final String password) {
		this.login = login;
		this.password = password;
	}

	public final void setLocale(final String locale) {
		this.locale = locale;
	}

	/**
	 * Get last updapte
	 *
	 * @return the last update
	 */
	public final Date getLastUpdate() {
		return lastUpdate != null ? (Date) lastUpdate.clone() : null;
	}

	/**
	 * Set last update
	 *
	 * @param lastUpdate the last update
	 */
	public final void setLastUpdate(final Date lastUpdate) {
		this.lastUpdate = (Date) lastUpdate.clone();
	}


	/**
	 * Set date pattern
	 *
	 * @param datePattern the date pattern
	 */
	public final void setDatePattern(final String datePattern) {
		this.datePattern = datePattern;
		setDatePatternWithoutHourMin(datePattern.substring(0, datePattern.indexOf(' ')));
	}

	/**
	 * Getter
	 *
	 * @return a language
	 */
	public final String getLanguage() {
		return this.locale.substring(0, this.locale.indexOf('_'));
	}

	/**
	 * Getter
	 *
	 * @return a country
	 */
	public final String getCountry() {
		return this.locale.substring(this.locale.indexOf('_') + 1, this.locale.length());
	}

	/**
	 * Get JSONObject view of the current user
	 *
	 * @return a JSONObject
	 */
	public final JsonObject getJSONObject() {
		final JsonObject json = new JsonObject();
		json.addProperty("id", getId());
		json.addProperty(Constants.LOGIN, getLogin());
		json.addProperty(Constants.PASSWORD, getPassword());
		json.addProperty("email", getEmail());
		json.addProperty("locale", getLocale());
		json.addProperty("timeZone", getTimeZone());
		json.addProperty("updateHourTime", getUpdateHourTime());
		json.addProperty("updateSendMail", getUpdateSendMail());
		json.addProperty("datePattern", getDatePattern());
		json.addProperty("datePatternWithoutHourMin", getDatePatternWithoutHourMin());
		json.addProperty("allow", getAllow());
		final DateFormat formatter = new SimpleDateFormat(getDatePattern());
		json.addProperty("lastUpdate", getLastUpdate() != null ? formatter.format(getLastUpdate()) : "");
		return json;
	}
} public class PieChart extends AChart {

	/** Equities **/
	private final Map<String, Double> equities;

	/**
	 * Constructor that will construct a chart with the given equities
	 *
	 * @param equities
	 *            the equities
	 */
	public PieChart(final Map<String, Double> equities) {
		this.equities = equities;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see IChart#generate()
	 */
	@Override
	public final void generate() {
		StringBuilder dataTemp = new StringBuilder();
		StringBuilder titleTemp = new StringBuilder();
		StringBuilder drawTemp = new StringBuilder();
		drawTemp.append("[");
		titleTemp.append("var title = [");
		int i = 1;
		for (Entry<String, Double> e : equities.entrySet()) {
			String key = e.getKey();
			Double value = e.getValue();
			if (i != 1) {
				drawTemp.append(",");
				titleTemp.append(",");
			}
			drawTemp.append("{ data : d").append(i);
			drawTemp.append(", label : '").append(key).append("'}");
			titleTemp.append("'").append(key).append("'");
			dataTemp.append("var d").append(i).append(" = [[0,").append(value).append("]];");
			i++;
		}
		setData(dataTemp.toString());
		drawTemp.append("]");
		setDraw(drawTemp.toString());
		titleTemp.append("];");
		setTitle(titleTemp.toString());
	}

	/**
	 * get equities
	 *
	 * @return the equities
	 */
	public final Map<String, Double> getEquities() {
		return equities;
	}

} public class TimeChart extends AChart {

	/**
	 * Starting date of the chart
	 **/
	private final Date date;
	/**
	 * Share value info
	 **/
	private final Map<Date, Double> shareValue;
	/**
	 * Indexes
	 **/
	private final Map<String, List<Index>> indexes;

	/**
	 * Constructor that constructs a time chart
	 *
	 * @param shareValue the share values
	 * @param indexes    the indexes
	 * @param date       the starting date
	 */
	public TimeChart(final Map<Date, Double> shareValue, final Map<String, List<Index>> indexes, final Date date) {
		this.shareValue = shareValue;
		this.indexes = indexes;
		this.date = (Date) date.clone();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see IChart#generate()
	 */
	@Override
	public final void generate() {
		StringBuilder dataTemp = new StringBuilder();
		StringBuilder drawTemp = new StringBuilder();
		dataTemp.append("d1 = [");
		drawTemp.append("[{data: d1, label: 'My share value'}");
		int i = 1;
		for (Entry<Date, Double> e : shareValue.entrySet()) {
			if (i != 1) {
				dataTemp.append(",");
			}
			dataTemp.append("[" + e.getKey().getTime() + "," + e.getValue() + "]");
			i++;
		}
		dataTemp.append("]");
		int j = 2;
		for (Entry<String, List<Index>> e : indexes.entrySet()) {
			dataTemp.append(",");
			drawTemp.append(",");
			List<Index> indexesTemp = e.getValue();
			dataTemp.append("\nd").append(j).append(" = [");
			String indexName = null;
			if (indexesTemp.get(0).getYahooId().equals(Constants.CAC_40)) {
				indexName = "CAC 40";
			} else if (indexesTemp.get(0).getYahooId().equals(Constants.SP_500)) {
				indexName = "S&P 500";
			}
			drawTemp.append("{data: d").append(j).append(", label: '").append(indexName).append("'}");
			int k = 0;
			for (Index indexTemp : indexesTemp) {
				if (k != 0) {
					dataTemp.append(",");
				}
				dataTemp.append("[").append(indexTemp.getDate().getTime()).append(",").append(indexTemp.getShareValue()).append("]");
				k++;
			}
			dataTemp.append("]");
			j++;
		}
		drawTemp.append("]");
		setData(dataTemp.toString());
		setDraw(drawTemp.toString());
		generateColors();
	}

	/**
	 * Generate colors
	 */
	private void generateColors() {
		final List<String> colorsList = new ArrayList<>();
		colorsList.add("#3e933d");
		colorsList.add("#190525");
		colorsList.add("#6a0efc");
		colorsList.add("#FF0000");
		colorsList.add("#FFCC00");
		final List<String> colorsListRes = Util.getRandomColors(3, colorsList);
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (String s : colorsListRes) {
			sb.append("'").append(s).append("'");
			if (i != colorsList.size()) {
				sb.append(",");
			}
		}
		setColors(sb.toString());
	}

	/**
	 * Getter
	 *
	 * @return the date
	 */
	public final Date getDate() {
		if (date != null) {
			return (Date) date.clone();
		} else {
			return null;
		}
	}

	/**
	 * Getter
	 *
	 * @return the share values
	 */
	public final Map<Date, Double> getShareValue() {
		return shareValue;
	}

	/**
	 * Getter
	 *
	 * @return the map of indexes
	 */
	public final Map<String, List<Index>> getIndexes() {
		return indexes;
	}
} public class NotFoundException extends RuntimeException {

	public NotFoundException(final int id) {
		super("Id not found: " + id);
	}

	public NotFoundException(final String ticker) {
		super("Ticker not found: " + ticker);
	}
} public class YahooException extends RuntimeException {

	/**
	 * Serialization
	 **/
	private static final long serialVersionUID = 1870609472239446880L;
	/**
	 * Error message
	 **/
	public static final String ERROR = "Yahoo result null. Table is probably locked.";
	/**
	 * Error message
	 **/
	private static final String CONNECT_ERROR = "Can't connect to yahoo website";
	/**
	 * Error message
	 **/
	private static final String ENCODE_ERROR = "Url encoding did not work";

	/**
	 * Constructor
	 *
	 * @param message the error message
	 */
	public YahooException(final String message) {
		super(message);
	}

	/**
	 * Constructor
	 *
	 * @param message the error message
	 * @param e       the exception
	 */
	public YahooException(final String message, final Exception e) {
		super(message, e);
	}

	public static YahooException encodeErrorException(final Exception exception) {
		return new YahooException(ENCODE_ERROR, exception);
	}

	public static YahooException cantConnectException(final Exception exception) {
		return new YahooException(CONNECT_ERROR, exception);
	}
} public enum LanguageFactory {

	INSTANCE;

	/**
	 * The result LANGUAGE_MAP
	 **/
	private static final Map<String, Map<String, String>> LANGUAGE_MAP;

	static {
		LANGUAGE_MAP = new HashMap<>();
		createLanguage("English");
		createLanguage("Francais");
	}

	private static void createLanguage(final String languageName) {
		final Language language = new Language("language/" + languageName + ".xml");
		LANGUAGE_MAP.put(languageName, language.getLanguage());
	}

	/**
	 * Get the current language needed
	 *
	 * @param language the language
	 * @return a LANGUAGE_MAP
	 */
	public Map<String, String> getLanguage(final String language) {
		return LANGUAGE_MAP.get(language);
	}
} public class PdfReport {

	private final String jrxml;
	private final Map<String, Object> parameters;

	/**
	 * Constructor
	 *
	 * @param jrxmlPath the jrxml file to load
	 */
	public PdfReport(final String jrxmlPath) {
		this.jrxml = jrxmlPath;
		this.parameters = new HashMap<>();
	}

	/**
	 * Add param
	 *
	 * @param param  the param
	 * @param object the object
	 */
	public final void addParam(final String param, final Object object) {
		this.parameters.put(param, object);
	}

	/**
	 * Get report
	 *
	 * @return a jasper print
	 * @throws JRException the jreException
	 */
	public final JasperPrint getReport() throws JRException {
		final InputStream inputStream = PdfReport.class.getClassLoader().getResourceAsStream(jrxml);
		final JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
		final JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
		return JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
	}

	/**
	 * Create pie chart
	 *
	 * @param pieChart the pie chart
	 * @param title    the title
	 * @return an image
	 */
	public static Image createPieChart(final PieChart pieChart, final String title) {
		final Map<String, Double> map = pieChart.getEquities();
		final DefaultPieDataset data = new DefaultPieDataset();
		map.forEach(data::setValue);
		final JFreeChart chart = ChartFactory.createPieChart(title, data, true, true, true);
		return chart.createBufferedImage(500, 500);
	}

	/**
	 * Create a time chart
	 *
	 * @param pieChart the pie chart
	 * @param title    the title
	 * @return an image
	 */
	public static Image createTimeChart(final TimeChart pieChart, final String title) {
		final Map<Date, Double> mapShareValueChart = pieChart.getShareValue();
		final Map<String, List<Index>> mapIndexChart = pieChart.getIndexes();

		final TimeSeries series = new TimeSeries("Share value");
		final TimeSeries series2 = new TimeSeries("CAC40");
		final TimeSeries series3 = new TimeSeries("S&P 500");

		mapShareValueChart.forEach((key, value) -> series.add(new Millisecond(key), value));
		mapIndexChart.get("^FCHI").forEach(entry -> series2.add(new Millisecond(entry.getDate()), entry.getShareValue()));
		mapIndexChart.get("^GSPC").forEach(entry -> series3.add(new Millisecond(entry.getDate()), entry.getShareValue()));

		final TimeSeriesCollection dataset = new TimeSeriesCollection();
		dataset.addSeries(series);
		dataset.addSeries(series2);
		dataset.addSeries(series3);
		JFreeChart chart = ChartFactory.createTimeSeriesChart(title, // Title
			"Time", // x-axis Label
			"Performance", // y-axis Label
			dataset, // Dataset
			true, // Show Legend
			true, // Use tooltips
			true // Configure chart to generate URLs?
		);
		return chart.createBufferedImage(1110, 600);
	}
} public interface CompanyService {

	void updateCompaniesNotRealTime();

	void deleteCompany(Company company);

	String addOrUpdateCompaniesLimitedRequest(List<String> companiesYahooIdRealTime) throws YahooException;

	Optional<Company> createManualCompany(String name, String industry, String sector, Currency currency, double quote);

	Optional<Company> addOrUpdateCompany(String ticker) throws YahooException;

	void updateCompanyManual(Integer companyId, Double newQuote);

	void cleanDB();

	boolean updateAllCompanies();
} public interface CurrencyService {

	/**
	 * Load a currency with its data
	 *
	 * @param currency the currency
	 * @return a currency
	 * @throws YahooException the yahoo exception
	 */
	Currency loadCurrencyData(Currency currency) throws YahooException;

	/**
	 * Update all current currencies
	 *
	 * @throws YahooException the yahoo exception
	 */
	void updateAllCurrencies() throws YahooException;

	/**
	 * Update one currency
	 *
	 * @param currency the currency
	 * @throws YahooException the yahoo exception
	 */
	void updateOneCurrency(Currency currency) throws YahooException;

	/**
	 * Get all currency data
	 *
	 * @param currency the currency
	 * @return a 2 dim array of object
	 */
	Object[][] getAllCurrencyData(Currency currency);
} public interface IndexService {

	/**
	 * @param yahooId the yahoo id
	 * @param from the from date
	 * @return a list of indexes
	 */
	List<Index> getIndexes(String yahooId, Date from);

	/**
	 * Get index information
	 *
	 * @param yahooId the yahoo id
	 * @param from    the from date
	 * @param to      the to date
	 * @return a list of indexes
	 */
	List<Index> getIndexes(String yahooId, Date from, Date to);

	/**
	 * Update an index
	 *
	 * @param yahooId the yahoo id
	 * @throws YahooException the yahoo exception
	 */
	void updateIndex(String yahooId) throws YahooException;

	/**
	 * Don't remember what it does
	 *
	 * @param yahooId  the yahoo id
	 * @param timeZone the timezone
	 * @throws YahooException the yahoo exception
	 */
	void checkUpdateIndex(String yahooId, TimeZone timeZone) throws YahooException;
} public interface UserService {

	void createUser(String login, String md5Password, String email) throws NoSuchAlgorithmException, UnsupportedEncodingException, LoginException;

	Optional<User> getUser(String login);

	Optional<User> getUserWithEmail(String email);

	Optional<User> checkUser(String login, String md5Password) throws LoginException;

	Optional<Portfolio> getUserPortfolio(int userId) throws YahooException;

	Optional<Portfolio> getUserPortfolio(int userId, Date from) throws YahooException;

	Optional<Portfolio> getUserPortfolio(int userId, Date from, Date to) throws YahooException;

	void validateUser(String login);

	void updateUser(User user);

	void updateOneUserPassword(User user);

	void updatePortfolio(Portfolio portfolio);

	void updateLiquidity(Account account, double liquidity);
} public enum Constants {
	;

	public static final String SP_500 = "^GSPC";
	public static final String CAC_40 = "^FCHI";


	public static final String APP_TITLE = "appTitle";
	public static final String AUTO_UPDATE = "autoUpdate";
	public static final String AUTO_UPDATE_EMAIL = "autoUpdateEmail";
	public static final String ACCOUNT = "account";
	public static final String ACCOUNT_ID = "accountId";
	public static final String ADD = "add";
	public static final String ADDED = "Added!";

	public static final String BUY = "buy";
	public static final String BEGIN = "begin";

	public static final String CHECK = "check";
	public static final String CHECKED = "checked";
	public static final String CURRENCIES = "currencies";
	public static final String CURRENCY = "currency";
	public static final String CURRENCY_2 = "currency2";
	public static final String CURRENCY_UPDATE = "currencyUpdate";
	public static final String CONSTANT_ADDED = "CONSTANT_ADDED";
	public static final String CONSTANT_MODIFIED = "CONSTANT_MODIFIED";
	public static final String CONSTANT_DELETED = "CONSTANT_DELETED";
	public static final String COMPANY_ID = "companyId";
	public static final String CAP_PIE = "capPie";
	public static final String COMMENTARY = "commentary";
	public static final String COMMENTARY_UPDATED = "commentaryUpdated";

	public static final String DATE_PATTERN = "datePattern";
	public static final String DAYS = "days";
	public static final String DELETE = "delete";
	public static final String DELETE_2 = "delete2";
	public static final String DELETE_FOLLOW_ID = "deleteFollowId";

	public static final String ERROR = "error";
	public static final String EMAIL = "email";
	public static final String ENGLISH = "English";
	public static final String END = "end";
	public static final String EQUITIES = "equities";

	public static final String FORMAT = "format";
	public static final String FRANCAIS = "Francais";
	public static final String FILE = "file";
	public static final String FROM = "from";
	public static final String FROM_UNDERSCORE = "_from";
	public static final String FOLLOWS = "follows";
	public static final String FUND = "Fund";

	public static final String HOME = "home";
	public static final String HOMEMOBILE = "homemobile";
	public static final String HIGHER = "higher";

	public static final String ID = "id";
	public static final String INDUSTRY_PERSONAL = "industryPersonal";

	public static final String LANGUAGE = "language";
	public static final String LOWER = "lower";
	public static final String LIQUIDITY = "liquidity";
	public static final String LIQUIDITY_MOVEMENT = "liquidityMovement";
	public static final String LOGIN = "login";
	public static final String LOCATION = "Location";

	public static final String MANUAL = "manual";
	public static final String MANUAL_NAME = "manualName";
	public static final String MANUAL_UNIT_COST_PRICE = "manualUnitCostPrice";
	public static final String MANUAL_QUANTITY = "manualQuantity";
	public static final String MANUAL_PARITY_PERSONAL = "manualParityPersonal";
	public static final String MANUAL_CURRENCY = "manualCurrency";
	public static final String MANUAL_INDUSTRY = "manualIndustry";
	public static final String MANUAL_SECTOR = "manualSector";
	public static final String MANUAL_QUOTE = "manualQuote";
	public static final String MESSAGE = "message";
	public static final String MAP_SECTOR = "mapSector";
	public static final String MAP_CAP = "mapCap";
	public static final String MODIFIED = "modified";
	public static final String MARKET_CAP_PERSONAL = "marketCapPersonal";
	public static final String MODIFY_PARITY_PERSONAL = "modifyParityPersonal";
	public static final String MODIFY_ERROR = "modifyError";
	public static final String MOD = "mod";
	public static final String MODIFIED_MESSAGE = "Modified!";
	public static final String MOVEMENT = "movement";

	public static final String NB_PAGE = "nbPage";
	public static final String NAME_PERSONAL = "namePersonal";

	public static final String OBJECTIVE = "objective";

	public static final String PASSWORD = "password";
	public static final String PARITY = "parity";
	public static final String PAGE = "page";
	public static final String PARITY_PERSONAL = "parityPersonal";
	public static final String PORTFOLIO = "portfolio";
	public static final String PDF = "pdf";

	public static final String STOP_LOSS = "stopLoss";
	public static final String SHARE_ID = "shareId";
	public static final String SELL = "sell";
	public static final String SECTOR_PERSONAL = "sectorPersonal";
	public static final String SECTOR_PIE = "sectorPie";
	public static final String SHARE_VALUE_PIE = "shareValuePie";
	public static final String SESSION = "session";

	public static final String TAXE = "taxe";
	public static final String TIME_ZONE = "timeZone";
	public static final String TAB = "tab";

	public static final String QUOTE = "quote";
	public static final String QUANTITY = "quantity";

	public static final String TICKER = "ticker";
	public static final String TO = "to";
	public static final String TO_UNDERSCORE = "_to";

	public static final String USER = "user";
	public static final String UPDATE = "update";
	public static final String UPDATED = "updated";
	public static final String UPDATE_TIME = "updateTime";
	public static final String UPDATE_STATUS = "updateStatus";
	public static final String UNIT_COST_PRICE = "unitCostPrice";
	public static final String UNKNOWN = "Unknown";

	public static final String WARN = "warn";

	public static final String YIELD = "yield";
	public static final String YIELD_1 = "yield1";
	public static final String YIELD_2 = "yield2";
	public static final String YIELD_PERSONAL = "yieldPersonal";
} @Log4j2
public enum Util {
	;

	private static final Random RANDOM = new Random();

	/**
	 * Get the timezone difference with Paris
	 *
	 * @param timezone the timezone
	 * @return an hour
	 */
	public static int timeZoneDiff(final TimeZone timezone) {
		final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
		final int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
		final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);

		final Calendar calendar2 = Calendar.getInstance(timezone);
		final int hourOfDay2 = calendar2.get(Calendar.HOUR_OF_DAY);
		final int dayOfMonth2 = calendar2.get(Calendar.DAY_OF_MONTH);

		int hourDifference = hourOfDay - hourOfDay2;
		final int dayDifference = dayOfMonth - dayOfMonth2;
		final int hourInDay = 24;
		if (dayDifference != 0) {
			hourDifference = hourDifference + hourInDay;
		}
		return hourDifference;
	}

	/**
	 * Get date in timezone
	 *
	 * @param date     the date
	 * @param timeZone the timezone
	 * @return a Calendar
	 */
	public static Calendar getDateInTimeZone(final Date date, final TimeZone timeZone) {
		final Calendar calendar = Calendar.getInstance(timeZone);
		calendar.setTime(date);
		return calendar;
	}

	/**
	 * Get current calendar in time zone
	 *
	 * @param timeZone the timezone
	 * @return the calendar
	 */
	public static Calendar getCurrentCalendarInTimeZone(final TimeZone timeZone) {
		return Calendar.getInstance(timeZone);
	}

	/**
	 * Test if 2 calendar are in the same day
	 *
	 * @param calendar1 the 1st calendar
	 * @param calendar2 the 2nd calendar
	 * @return a boolean
	 */
	public static boolean isSameDay(final Calendar calendar1, final Calendar calendar2) {
		boolean res = false;
		if (calendar1.getTimeZone() == calendar2.getTimeZone()
			&& calendar1.get(Calendar.DAY_OF_YEAR) == calendar2.get(Calendar.DAY_OF_YEAR)
			&& calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR)) {
			res = true;
		}
		return res;
	}

	/**
	 * Get current date in specified format
	 *
	 * @param format the format to use
	 * @return the formatted date
	 */
	public static String getCurrentDateInFormat(final String format) {
		final Calendar cal = Util.getCurrentCalendarInTimeZone(TimeZone.getTimeZone("America/Chicago"));
		final DateFormat formatter = new SimpleDateFormat(format);
		return formatter.format(cal.getTime());
	}

	/**
	 * Not sure what it is doing
	 *
	 * @param hour1 the 1st hour
	 * @param hour2 the second hour
	 * @return an int
	 */
	public static int getRealHour(final int hour1, final int hour2) {
		final int hourInDay = 24;
		int res;
		int add = hour1 + hour2;
		if (add >= hourInDay) {
			res = add - hourInDay;
		} else {
			if (add < 0) {
				res = add + hourInDay;
			} else {
				res = add;
			}
		}
		return res;
	}

	/**
	 * Get RANDOM colors
	 *
	 * @param size   the size
	 * @param colors the list of color
	 * @return a list of string
	 */
	public static List<String> getRandomColors(final int size, final List<String> colors) {
		final Set<String> res = new HashSet<>();
		while (res.size() != size) {
			final String color = colors.get(RANDOM.nextInt(colors.size()));
			res.add(color);
		}
		return new ArrayList<>(res);
	}

	/**
	 * Create tar.gz file
	 *
	 * @param input  the input path
	 * @param output the output path
	 * @throws IOException the exception
	 */
	public static void createTarGz(final String input, final String output) throws IOException {
		try (final FileOutputStream fOut = new FileOutputStream(new File(output));
			 final BufferedOutputStream bOut = new BufferedOutputStream(fOut);
			 final GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(bOut);
			 final TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {
			final File f = new File(input);
			final TarArchiveEntry tarEntry = new TarArchiveEntry(f, input);
			tOut.putArchiveEntry(tarEntry);
			IOUtils.copy(new FileInputStream(f), tOut);
			tOut.closeArchiveEntry();
		} catch (final IOException e) {
			throw new IOException(e);
		}
	}

	/**
	 * Make a pause
	 *
	 * @param timeInMs the time to wait in ms
	 */
	public static void makeAPause(final int timeInMs) {
		try {
			Thread.currentThread();
			Thread.sleep(timeInMs);
		} catch (final InterruptedException e) {
			log.warn(e.getMessage(), e);
		}
	}

	/**
	 * Reset hours, minutes, seconds and milliseconds
	 *
	 * @param date the date
	 * @return the date with reseted field
	 */
	public static Date resetHourMinSecMill(final Date date) {
		final int hourOfDay = 17;
		final Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}
} public enum Constants {
	;

	public static final String SP_500 = "^GSPC";
	public static final String CAC_40 = "^FCHI";


	public static final String APP_TITLE = "appTitle";
	public static final String AUTO_UPDATE = "autoUpdate";
	public static final String AUTO_UPDATE_EMAIL = "autoUpdateEmail";
	public static final String ACCOUNT = "account";
	public static final String ACCOUNT_ID = "accountId";
	public static final String ADD = "add";
	public static final String ADDED = "Added!";

	public static final String BUY = "buy";
	public static final String BEGIN = "begin";

	public static final String CHECK = "check";
	public static final String CHECKED = "checked";
	public static final String CURRENCIES = "currencies";
	public static final String CURRENCY = "currency";
	public static final String CURRENCY_2 = "currency2";
	public static final String CURRENCY_UPDATE = "currencyUpdate";
	public static final String CONSTANT_ADDED = "CONSTANT_ADDED";
	public static final String CONSTANT_MODIFIED = "CONSTANT_MODIFIED";
	public static final String CONSTANT_DELETED = "CONSTANT_DELETED";
	public static final String COMPANY_ID = "companyId";
	public static final String CAP_PIE = "capPie";
	public static final String COMMENTARY = "commentary";
	public static final String COMMENTARY_UPDATED = "commentaryUpdated";

	public static final String DATE_PATTERN = "datePattern";
	public static final String DAYS = "days";
	public static final String DELETE = "delete";
	public static final String DELETE_2 = "delete2";
	public static final String DELETE_FOLLOW_ID = "deleteFollowId";

	public static final String ERROR = "error";
	public static final String EMAIL = "email";
	public static final String ENGLISH = "English";
	public static final String END = "end";
	public static final String EQUITIES = "equities";

	public static final String FORMAT = "format";
	public static final String FRANCAIS = "Francais";
	public static final String FILE = "file";
	public static final String FROM = "from";
	public static final String FROM_UNDERSCORE = "_from";
	public static final String FOLLOWS = "follows";
	public static final String FUND = "Fund";

	public static final String HOME = "home";
	public static final String HOMEMOBILE = "homemobile";
	public static final String HIGHER = "higher";

	public static final String ID = "id";
	public static final String INDUSTRY_PERSONAL = "industryPersonal";

	public static final String LANGUAGE = "language";
	public static final String LOWER = "lower";
	public static final String LIQUIDITY = "liquidity";
	public static final String LIQUIDITY_MOVEMENT = "liquidityMovement";
	public static final String LOGIN = "login";
	public static final String LOCATION = "Location";

	public static final String MANUAL = "manual";
	public static final String MANUAL_NAME = "manualName";
	public static final String MANUAL_UNIT_COST_PRICE = "manualUnitCostPrice";
	public static final String MANUAL_QUANTITY = "manualQuantity";
	public static final String MANUAL_PARITY_PERSONAL = "manualParityPersonal";
	public static final String MANUAL_CURRENCY = "manualCurrency";
	public static final String MANUAL_INDUSTRY = "manualIndustry";
	public static final String MANUAL_SECTOR = "manualSector";
	public static final String MANUAL_QUOTE = "manualQuote";
	public static final String MESSAGE = "message";
	public static final String MAP_SECTOR = "mapSector";
	public static final String MAP_CAP = "mapCap";
	public static final String MODIFIED = "modified";
	public static final String MARKET_CAP_PERSONAL = "marketCapPersonal";
	public static final String MODIFY_PARITY_PERSONAL = "modifyParityPersonal";
	public static final String MODIFY_ERROR = "modifyError";
	public static final String MOD = "mod";
	public static final String MODIFIED_MESSAGE = "Modified!";
	public static final String MOVEMENT = "movement";

	public static final String NB_PAGE = "nbPage";
	public static final String NAME_PERSONAL = "namePersonal";

	public static final String OBJECTIVE = "objective";

	public static final String PASSWORD = "password";
	public static final String PARITY = "parity";
	public static final String PAGE = "page";
	public static final String PARITY_PERSONAL = "parityPersonal";
	public static final String PORTFOLIO = "portfolio";
	public static final String PDF = "pdf";

	public static final String STOP_LOSS = "stopLoss";
	public static final String SHARE_ID = "shareId";
	public static final String SELL = "sell";
	public static final String SECTOR_PERSONAL = "sectorPersonal";
	public static final String SECTOR_PIE = "sectorPie";
	public static final String SHARE_VALUE_PIE = "shareValuePie";
	public static final String SESSION = "session";

	public static final String TAXE = "taxe";
	public static final String TIME_ZONE = "timeZone";
	public static final String TAB = "tab";

	public static final String QUOTE = "quote";
	public static final String QUANTITY = "quantity";

	public static final String TICKER = "ticker";
	public static final String TO = "to";
	public static final String TO_UNDERSCORE = "_to";

	public static final String USER = "user";
	public static final String UPDATE = "update";
	public static final String UPDATED = "updated";
	public static final String UPDATE_TIME = "updateTime";
	public static final String UPDATE_STATUS = "updateStatus";
	public static final String UNIT_COST_PRICE = "unitCostPrice";
	public static final String UNKNOWN = "Unknown";

	public static final String WARN = "warn";

	public static final String YIELD = "yield";
	public static final String YIELD_1 = "yield1";
	public static final String YIELD_2 = "yield2";
	public static final String YIELD_PERSONAL = "yieldPersonal";
}
import fr.cph.stock.config.AppProperties;
import fr.cph.stock.entities.Index;
import fr.cph.stock.entities.Portfolio;
import fr.cph.stock.entities.User;
import fr.cph.stock.entities.chart.PieChart;
import fr.cph.stock.entities.chart.TimeChart;
import fr.cph.stock.exception.NotFoundException;
import fr.cph.stock.exception.YahooException;
import fr.cph.stock.language.LanguageFactory;
import fr.cph.stock.report.PdfReport;
import fr.cph.stock.service.CompanyService;
import fr.cph.stock.service.CurrencyService;
import fr.cph.stock.service.IndexService;
import fr.cph.stock.service.UserService;
import fr.cph.stock.util.Constants;
import fr.cph.stock.util.Util;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperExportManager;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.awt.*;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import static fr.cph.stock.util.Constants.*;
/**
 * Copyright 2017 Carl-Philipp Harmant
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package fr.cph.stock.controller.portfolio;




/**
 * This servlet is called when the user want to update the portfolio
 *
 * @author Carl-Philipp Harmant
 */
@SessionAttributes(USER)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Log4j2
@Controller
public class PortfolioController {

	private static final String DATE_FORMAT = "dd/MM/yyyy";

	@NonNull
	private AppProperties appProperties;
	@NonNull
	private final UserService userService;
	@NonNull
	private final CompanyService companyService;
	@NonNull
	private final CurrencyService currencyService;
	@NonNull
	private final IndexService indexService;

	@RequestMapping(value = "/portfolio", method = RequestMethod.POST)
	public ModelAndView updatePortfolio(@RequestParam(value = CURRENCY_UPDATE, required = false) final String updateCurrencies,
										@Valid @ModelAttribute final User user,
										@CookieValue(LANGUAGE) final String lang) throws IOException, ServletException {
		final ModelAndView model = new ModelAndView("forward:/" + HOME);
		String yahooError = null;
		String yahooUpdateCompanyError = null;
		try {
			final Portfolio portfolio = userService.getUserPortfolio(user.getId()).orElseThrow(() -> new NotFoundException(user.getId()));