package org.inria.bmajwatcher.client.ui;

import java.util.List;
import java.util.Map;

import org.inria.bmajwatcher.client.i18n.BmajWatcherConstants;
import org.inria.bmajwatcher.client.services.BankDetailServiceAsync;
import org.inria.bmajwatcher.client.services.BankStatServiceAsync;
import org.inria.bmajwatcher.client.ui.styles.Resources;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
import com.google.gwt.visualization.client.visualizations.OrgChart;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.tree.TreeGrid;
import com.smartgwt.client.widgets.tree.TreeGridField;

/**
 * Dialog that displays charts and detailed information about a bank.
 * 
 * @author rsabas
 *
 */
public class BankDetailDialog extends DialogBox implements ClickHandler {
	
	private VerticalPanel graphPanel; 
	private LineChartGenerator cGen;
	
	private boolean graphDrawn = false;
	private boolean detailDrawn = false;
	
	private Anchor sizeAnchor = new Anchor(BmajWatcherConstants.INSTANCE.size());
	private Anchor bandwidthAnchor = new Anchor(BmajWatcherConstants.INSTANCE.bandwidth());
	private Anchor fileAnchor = new Anchor(BmajWatcherConstants.INSTANCE.fileCount());
	private Anchor turnoverAnchor = new Anchor(BmajWatcherConstants.INSTANCE.fileTurnover());
	private Anchor durationAnchor = new Anchor(BmajWatcherConstants.INSTANCE.updateDuration());
	
	private int selectedMax = 10;
	
	private TreeGrid tree = null;
	
	private String userKey = "";
	

	public BankDetailDialog(final Record bank, final boolean adminMode, String userKey) {
		super(false, false);
		
		this.userKey = userKey;
		
		final String bankName = bank.getAttribute(BankGridDataSource.NAME);
		
		final VerticalPanel overviewPanel = new VerticalPanel();
		
		BankDetailServiceAsync.INSTANCE.getDetailedBankInfo(bankName, new AsyncCallback<Map<String,String>>() {
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Bank detail loading failure");
			}

			@Override
			public void onSuccess(final Map<String, String> info) {
				
				
				BankDetailServiceAsync.INSTANCE.getFields(adminMode, new AsyncCallback<Map<String,List<String>>>() {
					
					@Override
					public void onFailure(Throwable caught) {
						SC.warn("Release fields loading failure");
					}
					
					@Override
					public void onSuccess(Map<String, List<String>> result) {
						for (String gridName : result.keySet()) {
							List<String> attributes = result.get(gridName);
							Grid grid = new Grid(attributes.size(), 2);
							
							if (gridName.equals("Pre-processes") || gridName.equals("Post-processes")) {
								grid = new Grid(attributes.size(), 1);
								grid.setWidget(0, 0, getProcessTree(info.get(attributes.get(0).split("_:_")[0])));
								grid.getCellFormatter().setStyleName(0, 0, Resources.INSTANCE.mainCss().singleCellValue());
							} else if (gridName.equals("Dependencies")) {
								grid = new Grid(attributes.size(), 1);
								grid.getCellFormatter().setStyleName(0, 0, Resources.INSTANCE.mainCss().singleCellValue());
								setDependenciesTree(grid, bankName);
							} else {
								for (int i = 0; i < attributes.size(); i++) {
									String[] split = attributes.get(i).split("_:_");
									grid.setText(i, 0, split[1]);
									
									if (split[1].equals("Last log") && info.get(split[0]) != null
											&& !info.get(split[0]).trim().isEmpty()) {
										String url = info.get(split[0]);
										// Get the 3 last /
										if (url != null) {
											url = url.substring(0, url.lastIndexOf('/'));
											url = url.substring(0, url.lastIndexOf('/'));
											int index = url.lastIndexOf('/') + 1;
											
											String relativePath = info.get(split[0]).substring(index);
											
											
											grid.setHTML(i, 1, "<a href=\"logs/" + relativePath + "\">" + info.get(split[0]) + "</a>");
										}
									} else if (split[1].equals("Url")) {
										grid.setHTML(i, 1, "<a href=\"" + info.get(split[0]) + "\">" + info.get(split[0]) + "</a>");
									} else if (split[1].equals("Production directories")) {
										grid.setHTML(i, 1, info.get(split[0]));
									} else {
										grid.setText(i, 1, info.get(split[0]));
									}
									
									grid.getCellFormatter().setStyleName(i, 0, Resources.INSTANCE.mainCss().cellLabel());
									grid.getCellFormatter().setStyleName(i, 1, Resources.INSTANCE.mainCss().cellValue());
								}
							}
							
							grid.setStyleName(Resources.INSTANCE.mainCss().detailGrid());
							DisclosurePanel closeablePanel = new DisclosurePanel(gridName);
							closeablePanel.setOpen(true);
							closeablePanel.setContent(grid);
							if (!gridName.equals("Post-processes") && !gridName.equals("Pre-processes"))
								overviewPanel.insert(closeablePanel, 0);
							else
								overviewPanel.add(closeablePanel);
						}
					}
					
				});
			}
		});

		HorizontalPanel mainGraphPanel = new HorizontalPanel();
		VerticalPanel graphMenu = new VerticalPanel();
		VerticalPanel topMenu = new VerticalPanel();
		graphMenu.add(topMenu);
		graphMenu.setStylePrimaryName(Resources.INSTANCE.mainCss().graphMenu());
		mainGraphPanel.add(graphMenu);
		graphPanel = new VerticalPanel();
		
		Label lblShow = new Label("Show");
		final ListBox lbShowHowMany = new ListBox();
		lbShowHowMany.addItem("Last 10");
		lbShowHowMany.addItem("All");
		lbShowHowMany.addChangeHandler(new ChangeHandler() {
			
			@Override
			public void onChange(ChangeEvent event) {
				if (lbShowHowMany.getItemText(lbShowHowMany.getSelectedIndex()).equals("All")) {
					selectedMax = 0;
				} else {
					selectedMax = 10;
				}
				graphPanel.clear();
				graphPanel.add(new Label("Loading..."));
				showStats(bankName);
			}
		});
		
		HorizontalPanel hpShow = new HorizontalPanel();
		hpShow.add(lblShow);
		hpShow.add(lbShowHowMany);
		hpShow.setSpacing(2);
		hpShow.setCellVerticalAlignment(lblShow, HasVerticalAlignment.ALIGN_MIDDLE);
		
		VerticalPanel rightPanel = new VerticalPanel();
		rightPanel.add(hpShow);
		rightPanel.add(graphPanel);
		rightPanel.setCellHorizontalAlignment(hpShow, HasHorizontalAlignment.ALIGN_RIGHT);
		
		graphPanel.add(new Label("Loading..."));
		mainGraphPanel.add(rightPanel);
		
		final HorizontalPanel detailPanel = new HorizontalPanel();
		
		/*
		 * TabPanel
		 */
		DecoratedTabPanel tabPanel = new DecoratedTabPanel();
		tabPanel.add(overviewPanel, BmajWatcherConstants.INSTANCE.bankOverview());
		if (adminMode)
			tabPanel.add(detailPanel, BmajWatcherConstants.INSTANCE.bankSessions());
		tabPanel.add(mainGraphPanel, BmajWatcherConstants.INSTANCE.bankStats());
		tabPanel.selectTab(0);
		
		tabPanel.addSelectionHandler(new SelectionHandler<Integer>() {
			
			@Override
			public void onSelection(SelectionEvent<Integer> event) {
				if (event.getSelectedItem() == (2 - (adminMode ? 0 : 1)) 
						&& !graphDrawn) {
					showStats(bankName);
				} else if (adminMode && event.getSelectedItem() == 1 && !detailDrawn) {
//					Peut etre que c'est pas reinit ?? plantage ?
					getBankDetail(detailPanel, bankName);
					detailDrawn = true;
				}
				
			}
		});
		
		sizeAnchor.addClickHandler(this);
		bandwidthAnchor.addClickHandler(this);
		fileAnchor.addClickHandler(this);
		turnoverAnchor.addClickHandler(this);
		durationAnchor.addClickHandler(this);
		topMenu.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
		topMenu.add(sizeAnchor);
		topMenu.add(bandwidthAnchor);
		topMenu.add(fileAnchor);
		topMenu.add(turnoverAnchor);
		topMenu.add(durationAnchor);
		
		Button close = new Button("Close");
		close.addClickHandler(new com.google.gwt.event.dom.client.ClickHandler() {
			
			@Override
			public void onClick(com.google.gwt.event.dom.client.ClickEvent event) {
				if (tree != null)
					tree.hide();
				hide();
			}
		});
		
	  	setHTML("<b>" + bankName + "</b>");
		addStyleName(Resources.INSTANCE.mainCss().bankDetail());
		VerticalPanel vp = new VerticalPanel();
		vp.add(tabPanel);
		vp.add(close);
		vp.setCellHorizontalAlignment(close, HasHorizontalAlignment.ALIGN_RIGHT);
		add(vp);
	}
	
	private void showStats(String bankName) {
		BankStatServiceAsync.INSTANCE.getBankStats(bankName, selectedMax, new AsyncCallback<List<Map<String,String>>>() {
			
			@Override
			public void onSuccess(List<Map<String, String>> result) {
				graphPanel.clear();
				cGen = new LineChartGenerator(result);
				graphPanel.add(cGen.getSizeEvolution());
				graphDrawn = true;
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Failure");
			}
		});
	}
	
	
	private void getBankDetail(Panel pnl, String bankName) {
		
		tree = new TreeGrid();
		tree.setLoadDataOnDemand(true);
		tree.setWidth(600);
		tree.setHeight(400);
		tree.setAnimateFolders(false);
		tree.setDataSource(BankTreeDataSource.getInstance(bankName, userKey));
		tree.setAutoFetchData(true);
		TreeGridField field = new TreeGridField("value");
		tree.setFields(field);
		
		pnl.add(tree);
		tree.redraw();
	}
	
	private void setDependenciesTree(final Grid grid, final String bankName) {
		
		final DataTable currentData = DataTable.create();
		currentData.addColumn(ColumnType.STRING, "Bank");
		currentData.addColumn(ColumnType.STRING, "Parent");
		int currentIndex = 0;
		
		currentData.addRow();
		currentData.setValue(currentIndex++, 0, bankName);
		
		final OrgChart.Options opts = OrgChart.Options.create();
		opts.setSize(OrgChart.Size.MEDIUM);
		
		BankDetailServiceAsync.INSTANCE.getDependencies(bankName, new AsyncCallback<Map<String,String>>() {
			
			@Override
			public void onSuccess(Map<String, String> result) {
				
				if (result.size() > 0) {
					buildDependencies(result, currentData, bankName);
					grid.setWidget(0, 0, new OrgChart(currentData, opts));
				}
				
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Could not retrieve bank dependencies");
			}
		});
	}
	
	private void buildDependencies(Map<String, String> values, DataTable data, String key) {
		String value = "";
		if ((value = values.get(key)) != null) {
			String[] split = value.split(",");
			for (String dep : split) {
				data.addRow();
				int pos = data.getNumberOfRows() - 1;
				data.setValue(pos, 0, dep);
				data.setValue(pos, 1, key);
				buildDependencies(values, data, dep);
			}
		}
	}
	
	private OrgChart getProcessTree(String processes) {
		
		if (processes != null && !processes.trim().isEmpty()) {
			String[] split = processes.split(";");
			int currentIndex = 0;
			DataTable currentData = DataTable.create();
			currentData.addColumn(ColumnType.STRING, "Process");
			currentData.addColumn(ColumnType.STRING, "Parent");
			currentData.addColumn(ColumnType.STRING, "Tooltip");
			String parentBlock = "";
			String parentMeta = "";
			
			currentData.addRow();
			currentData.setValue(currentIndex++, 0, "Root");
			
			for (String elt : split) {
				String[] pair = elt.split("=");
				if (pair[0].equals("BLOCK")) {
					currentData.addRow();
					if (pair.length == 2) {
						currentData.setValue(currentIndex++, 0, pair[1]);
						parentBlock = pair[1];
					} else {
						currentData.setValue(currentIndex++, 0, "BLOCK");
						parentBlock = "BLOCK";
					}
					currentData.setValue(currentIndex - 1, 1, "Root");
					
				} else if (pair[0].equals("META")) {
					currentData.addRow();
					currentData.setValue(currentIndex++, 0, pair[1]); // position 0 is element name
					currentData.setValue(currentIndex - 1, 1, parentBlock); // position 1 is element parent name
					parentMeta = pair[1];
				} else { // PS
					currentData.addRow();
					String keyName = pair[1].split("_:_")[0];
					String exe = pair[1].split("_:_")[1];
					currentData.setValue(currentIndex++, 0, "<span style=\"color:#A44B98\">" + keyName + "</span>");
					currentData.setValue(currentIndex - 1, 1, parentMeta);
					currentData.setValue(currentIndex - 1, 2, exe); // tooltip
				}
			}
			
			OrgChart.Options opts = OrgChart.Options.create();
			opts.setSize(OrgChart.Size.MEDIUM);
			opts.setAllowHtml(true);
				
			return new OrgChart(currentData, opts);
		}
		
		return null;
	}
	
	/*
	 * 
	 * EVENT HANDLING WHEN ANCHOR IS CLICKED
	 * 
	 */
	@Override
	public void onClick(ClickEvent event) {
		graphPanel.clear();

		if (event.getSource() == sizeAnchor) {
			graphPanel.add(cGen.getSizeEvolution());
		} else if (event.getSource() == fileAnchor) {
			graphPanel.add(cGen.getFileCount());
		} else if (event.getSource() == durationAnchor) {
			graphPanel.add(cGen.getDurationEvolution());
		} else if (event.getSource() == turnoverAnchor) {
			graphPanel.add(cGen.getFileTurnover());
		} else if (event.getSource() == bandwidthAnchor) {
			graphPanel.add(cGen.getBandwidthEvolution());
		}
	}
}
