package org.inria.bmajwatcher.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.visualization.client.AbstractDataTable;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.LegendPosition;
import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
import com.google.gwt.visualization.client.visualizations.PieChart;

/**
 * Class that generates pie charts for banks global statistics. 
 * 
 * @author rsabas
 *
 */
public class PieChartGenerator {

	private List<Map<String, String>> stats;
	private final int maxForLegend = 30; // If more than this, do not display legend 
	
	public PieChartGenerator(List<Map<String, String>> list) {
		stats = list;
	}
	
	/**
	 * Builds the chart that contains the size of each bank.
	 * 
	 * @return
	 */
	public PieChart getAllBanks() {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		for (Map<String, String> map : stats) {
			List<String> nameAndSize = new ArrayList<String>();
			if (!map.get("name").equals("Free space")) {
				nameAndSize.add(map.get("name"));
				nameAndSize.add(MBToGB(map.get("size")));
				namesAndSizes.add(nameAndSize);
			}
		}
		
		return new PieChart(createData(namesAndSizes), getChartOption("All banks (GB)", namesAndSizes.size() < maxForLegend));
	}
	
	public PieChart getSizeByUser() {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		Map<String, Double> users = new HashMap<String, Double>();

		for (Map<String, String> map : stats) {
			String name = map.get("owner");
			if (name != null && !name.isEmpty()) {
				if (users.containsKey(name))
					users.put(name , users.get(name) + Double.valueOf(map.get("size")));
				else
					users.put(name, Double.valueOf(map.get("size")));
			}
		}
		Set<String> keys = users.keySet();
		for (String key : keys) {
			List<String> nameAndSize = new ArrayList<String>();
			nameAndSize.add(key);
			nameAndSize.add(MBToGB(String.valueOf(users.get(key))));
			namesAndSizes.add(nameAndSize);
		}
		
		return new PieChart(createData(namesAndSizes), getChartOption("Size by user (GB)", namesAndSizes.size() < maxForLegend));
	}
	
	public PieChart getBankCountByUser() {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		Map<String, Integer> users = new HashMap<String, Integer>();

		for (Map<String, String> map : stats) {
			String name = map.get("owner");
			if (name != null && !name.isEmpty()) {
				if (users.containsKey(name))
					users.put(name , users.get(name) + 1);
				else
					users.put(name, 1);
			}
		}
		Set<String> keys = users.keySet();
		for (String key : keys) {
			List<String> nameAndSize = new ArrayList<String>();
			nameAndSize.add(key);
			nameAndSize.add(String.valueOf(users.get(key)));
			namesAndSizes.add(nameAndSize);
		}
		
		return new PieChart(createData(namesAndSizes), getChartOption("Bank count by user", namesAndSizes.size() < maxForLegend));
	}
	
	/**
	 * Builds the chart that shows the total size of all the banks
	 * vs available space on disk.
	 * 
	 * @return
	 */
	public PieChart getFreeSpace() {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		float total = 0;
		for (Map<String, String> map : stats) {
			if (!map.get("name").equals("Free space"))
				total += Float.valueOf(map.get("size"));
			else {
				List<String> list = new ArrayList<String>();
				list.add(map.get("name"));
				list.add(String.valueOf(((float) ((int) (Float.valueOf(map.get("size")) * 100)) / 100)));
				namesAndSizes.add(list);
			}
		}
		List<String> list = new ArrayList<String>();
		list.add("Banks");
		list.add(MBToGB(String.valueOf(total)));
		namesAndSizes.add(list);
		
		return new PieChart(createData(namesAndSizes), getChartOption("Free space (GB)", namesAndSizes.size() < maxForLegend));
	}
	
	/**
	 * Builds the chart that contains the size of the banks by origin (server from where the
	 * bank was dl)
	 * 
	 * @return
	 */
	public PieChart getDistribution() {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		Map<String, Double> servers = new HashMap<String, Double>();
		// Size by origin
		for (Map<String, String> map : stats) {
			String name = map.get("server");
			if (!name.isEmpty()) {
				if (servers.containsKey(name))
					servers.put(name , servers.get(name) + Double.valueOf(map.get("size")));
				else
					servers.put(name, Double.valueOf(map.get("size")));
			}
		}
		Set<String> keys = servers.keySet();
		for (String key : keys) {
			List<String> nameAndSize = new ArrayList<String>();
			nameAndSize.add(key);
			nameAndSize.add(MBToGB(String.valueOf(servers.get(key))));
			namesAndSizes.add(nameAndSize);
		}
		
		return new PieChart(createData(namesAndSizes), getChartOption("Size by server (GB)", namesAndSizes.size() < maxForLegend));
	}
	
	/**
	 * Builds the chart that contains the amount of data by origin (server).
	 * 
	 * @return
	 */
	public PieChart getAllTypes() {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		Map<String, Double> servers = new HashMap<String, Double>();
		// Size by type
		for (Map<String, String> map : stats) {
			String name = map.get("type");
			if (!name.isEmpty()) {
				if (servers.containsKey(name))
					servers.put(name , servers.get(name) + Double.valueOf(map.get("size")));
				else
					servers.put(name, Double.valueOf(map.get("size")));
			}
		}
		Set<String> keys = servers.keySet();
		for (String key : keys) {
			List<String> nameAndSize = new ArrayList<String>();
			nameAndSize.add(key);
			nameAndSize.add(MBToGB(String.valueOf(servers.get(key))));
			namesAndSizes.add(nameAndSize);
		}
		
		return new PieChart(createData(namesAndSizes), getChartOption("Size by type (GB)", namesAndSizes.size() < maxForLegend));
	}
	
	/**
	 * Builds the chart that contains the size of the banks for a given type.
	 * 
	 * @param type
	 * @return
	 */
	public PieChart getType(String type) {
		List<List<String>> namesAndSizes = new ArrayList<List<String>>();
		// Size by bank by type
		for (Map<String, String> map : stats) {
			if (type.equals(map.get("type"))) {
				List<String> nameAndSize = new ArrayList<String>();
				nameAndSize.add(map.get("name"));
				nameAndSize.add(MBToGB(map.get("size")));
				namesAndSizes.add(nameAndSize);
			}
		}

		return new PieChart(createData(namesAndSizes), getChartOption(type + "(GB)", namesAndSizes.size() < maxForLegend));
	}
	
	/**
	 * Creates the datatable that contains the data to be displayed in the charts.
	 * 
	 * @param info
	 * @return
	 */
	private AbstractDataTable createData(List<List<String>> info) {
		DataTable data = DataTable.create();
		data.addColumn(ColumnType.STRING, "???");
		data.addColumn(ColumnType.NUMBER, "Size");
		data.addRows(info.size());
		for (int i = 0; i < info.size(); i++) {
			data.setValue(i, 0, info.get(i).get(0));
			data.setValue(i, 1, Double.valueOf(info.get(i).get(1)));
		}
		
		return data;
	}
	
	/**
	 * Creates a generic set of options for a pie chart.
	 * @param title
	 * @param displayLegend
	 * @return
	 */
	private PieChart.Options getChartOption(String title, boolean displayLegend) {
		PieChart.Options options = PieChart.Options.create();
		options.setWidth(500);
		options.setHeight(420);
		options.set3D(false);
		options.setTitle(title);
		if (displayLegend) {
			options.setLegend(LegendPosition.BOTTOM);
		} else {
			options.setLegend(LegendPosition.NONE);
		}

		return options;
	}
	
	private String MBToGB(String size) {
		return String.valueOf(((double) ((int) (Double.valueOf(size) * 100)) / 100) / 1000);
	}
}
