package org.inria.bmajwatcher.client.ui.bank_editor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.inria.bmajwatcher.client.i18n.BmajWatcherConstants;
import org.inria.bmajwatcher.client.services.BankEditingServiceAsync;
import org.inria.bmajwatcher.client.services.UserManagerServiceAsync;
import org.inria.bmajwatcher.client.ui.confirmation.MessagePanel;
import org.inria.bmajwatcher.client.ui.styles.MainCss;
import org.inria.bmajwatcher.client.ui.styles.PropertyCellStyle;
import org.inria.bmajwatcher.client.ui.styles.Resources;

import com.google.gwt.core.client.GWT;
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.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Command;
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.CheckBox;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
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.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.smartgwt.client.util.SC;

/**
 * Class for bank properties edition.
 * 
 * @author rsabas
 */
public class BankEditorDialog extends DialogBox {

	private static BankEditorDialog instance = null;
	
	
	private VerticalPanel miscContent = new VerticalPanel();
	private VerticalPanel initContent = new VerticalPanel();
	private VerticalPanel releaseContent = new VerticalPanel();
	private VerticalPanel downloadContent = new VerticalPanel();
	private VerticalPanel extractContent = new VerticalPanel();
	private VerticalPanel makeReleaseContent = new VerticalPanel();
	private VerticalPanel deploymentContent = new VerticalPanel();
	private VerticalPanel ftpContent = new VerticalPanel();
	private VerticalPanel httpContent = new VerticalPanel();
	private VerticalPanel directHttpContent = new VerticalPanel();
	
	private VerticalPanel vpDependency = new VerticalPanel();
	private BankDependencyBuilder dependencyBuilder;
	
	private DisclosurePanel initPanel = new DisclosurePanel("Initialization");
	private DisclosurePanel removePsPanel = new DisclosurePanel("Remove-processes");
	private DisclosurePanel generalPanel = new DisclosurePanel("Miscellaneous");
	private DisclosurePanel releasePanel = new DisclosurePanel("Release");
	private DisclosurePanel downloadPanel = new DisclosurePanel("Download");
	private DisclosurePanel ftpCfgPanel = new DisclosurePanel("Connection configuration");
	private DisclosurePanel httpCfgPanel = new DisclosurePanel("HTTP configuration");
	private DisclosurePanel directHttpCfgPanel = new DisclosurePanel("Direct HTTP configuration");
	private DisclosurePanel extractPanel = new DisclosurePanel("Extract");
	private DisclosurePanel deploymentPanel = new DisclosurePanel("Deployment");
	private DisclosurePanel makeReleasePanel = new DisclosurePanel("Make release");
	
	private VerticalPanel bankPanel = new VerticalPanel();
	private HorizontalPanel hpSelectedBank = new HorizontalPanel();
	
	private ListBox localBankList = new ListBox(false);
	private ListBox remoteBankList = new ListBox(false);
	private ListBox templateList = new ListBox(false);
	private ListBox parentList = new ListBox(false);
	private ListBox includeList = new ListBox(false);
	
	private HTML lblTemplateDesc = new HTML();
	private Label lblSelectedBank = new Label();
	private MenuBar menuSave = new MenuBar();
	private HorizontalPanel pnlIncludeWarning = new HorizontalPanel();
	private Label lblIncludeWarning = new Label();
	private HorizontalPanel hpIncludeList = new HorizontalPanel();
	
	private Button addInclude = new Button("Add");
	private PushButton removeInclude = new PushButton(new Image("images/edit-delete.png"));
	
	private Map<String, Map<String, String>> currentIncludedFiles = null;
	private Map<String, String> propertyHelp = null;
	private String currentFile = "";
	private String selectedBank = "";
	private BankValidator validator = BankValidator.getInstance();
	
	private String type = "";
	private final static String CREATE_NEW_INCLUDE = "Create new include file...";

	private RadioButton radioLoadRemote = new RadioButton("default", "Load from remote :");
	private RadioButton radioEmptyBank = new RadioButton("default", "Create bank from template :");
	private RadioButton radioLoadLocal = new RadioButton("default", "Load from local :");
	
	private ProcessEditor preProcessEditor = new ProcessEditor();
	private ProcessEditor postProcessEditor = new ProcessEditor();
	private ProcessEditor removeProcessEditor = new ProcessEditor();
	
	private CheckBox seeInherited = new CheckBox("See inherited properties");
	private CheckBox override = new CheckBox("Override inherited properties");
	
	private MainCss css = Resources.INSTANCE.mainCss();
	
	private PopupPanel propertyListPopup = new PopupPanel(true);
	private CellList<String> propsList;
	private Set<String> currentProperties = new TreeSet<String>();
	private Set<String> currentInheritedProperties = new HashSet<String>();
	private Label lblDescValue;
	private List<CheckBox> cbList;
	private String propSortType = PropertyCell.SORT_ON_NAME;
	private boolean propertyHelpShowInherited = true;
	private boolean propertyHelpShowNonInherited = true;
	private boolean propertyHelpShowMandatory = true;
	private boolean propertyHelpShowNonMandatory = true;
	
	
	private BankEditingServiceAsync server = BankEditingServiceAsync.INSTANCE;
	
	private static PropertyCellStyle propertyStyle = GWT.create(PropertyCellStyle.class);
	
	// Bank index for saving
	private int pos;
	
	private String login;
	private boolean admin = false;
	
	private BankEditorDialog(String _login) {
		super(false, false);
		
		login = _login;
		UserManagerServiceAsync.INSTANCE.getAdminLogin(new AsyncCallback<String>() {
			
			@Override
			public void onSuccess(String result) {
				admin = result.equals(login);
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("RPC failure");
			}
		});
		
		setHTML("<b>Bank editor</b>");
		
		addStyleName(css.bankEditor());
		
		pnlIncludeWarning.addStyleName(css.warningPanel());
		pnlIncludeWarning.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		Image img = new Image("images/vcs-conflicting.png");
		pnlIncludeWarning.add(img);
		pnlIncludeWarning.setCellWidth(img, "35px");
		pnlIncludeWarning.add(lblIncludeWarning);
		pnlIncludeWarning.setVisible(false);
		
		/*
		 * Gets the mandatory properties list
		 */
		server.getMandatoryProperties(new AsyncCallback<Collection<String>>() {
			
			@Override
			public void onSuccess(Collection<String> result) {
				validator.setMandatoryProps(result);
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Could not retrieve mandatory properties !");
			}
		});
		
		/*
		 * Gets the text bubbles for each property
		 */
		server.getPropertyHelp(new AsyncCallback<Map<String,String>>() {
			
			@Override
			public void onSuccess(Map<String, String> result) {
				propertyHelp = new HashMap<String, String>();
				/*
				 * Adds property type to validator
				 */
				for (String prop : result.keySet()) {
					String[] split = result.get(prop).split("__:__");
					propertyHelp.put(prop, split[0]);
					validator.addType(prop, split[1]);
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Properties description could not be retrieved !");
			}
		});
		
		/*
		 * Main panels
		 */
		VerticalPanel mainPanel = new VerticalPanel();
		mainPanel.addStyleName(css.bankEditorMainPanel());
//		topPanel.addStyleName(css.bankEditorPanel());
		VerticalPanel topPanel = new VerticalPanel();
		topPanel.setSpacing(3);
		topPanel.addStyleName(css.bankEditorTopPanel());
		final VerticalPanel centerPanel = new VerticalPanel();
		centerPanel.addStyleName(css.bankEditorPanel());
		HorizontalPanel bottomPanel = new HorizontalPanel();

		/*
		 * Options panel
		 */
		final VerticalPanel optionsPanel = new VerticalPanel();
		optionsPanel.addStyleName(css.bankEditorPanel());
		optionsPanel.add(seeInherited);
		optionsPanel.add(override);
		
		hpIncludeList.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		hpIncludeList.setSpacing(3);
		Label includeLabel = new Label("Include files :");
		hpIncludeList.add(includeLabel);
		hpIncludeList.add(includeList);
		addInclude.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				int index = includeList.getSelectedIndex();
				Collection<String> names = new ArrayList<String>();
				String name = "include/" + includeList.getValue(index);
				names.add(name);
				for (int i = 0; i < parentList.getItemCount(); i++) {
					String value = parentList.getItemText(i);
					if (!name.equals(value) && !value.equals("global.properties") && !value.equals(selectedBank))
						names.add(value);
				}
				loadBank(selectedBank.substring(0, selectedBank.lastIndexOf('.')), names);
				if (!name.equals(CREATE_NEW_INCLUDE))
					includeList.removeItem(index);
			}
		});
		hpIncludeList.add(addInclude);
		
		hpIncludeList.addStyleName(css.includeFilePanel());
		optionsPanel.add(hpIncludeList);
		
		
		/*
		 * Inherited properties options
		 */
		seeInherited.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				if (parentList.getItemCount() > 0) {
					
					Map<String, String> pre = preProcessEditor.getProcessProperties();
					Map<String, String> post = postProcessEditor.getProcessProperties();
					Map<String, String> remove = removeProcessEditor.getProcessProperties();
					
					Map<String, String> ps = new HashMap<String, String>();
					ps.putAll(pre);
					ps.putAll(post);
					ps.putAll(remove);
					validator.setBankProcesses(currentFile, ps);
					
					BankValidator.clearMiscProperties(currentIncludedFiles.get(currentFile));
					currentIncludedFiles.get(currentFile).putAll(ps);
					
					String value = parentList.getItemText(parentList.getSelectedIndex());
					populateProperties(currentIncludedFiles.get(value));
				}
			}
		});
		override.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				if (parentList.getItemCount() > 0) {
					String value = parentList.getItemText(parentList.getSelectedIndex());
					populateProperties(currentIncludedFiles.get(value));
				}
			}
		});
		
		
		/*
		 * Properties files for a bank
		 */
		VerticalPanel vpOptions = new VerticalPanel();
		vpOptions.addStyleName(css.bankEditorSubPanel());
		HorizontalPanel hpOptions = new HorizontalPanel();
		hpOptions.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		Label filz = new Label("Related files :");
		filz.addStyleName(css.propsParameter());
		hpOptions.add(filz);
		parentList.addStyleName(css.propsParameter());
		hpOptions.add(parentList);
		hpOptions.add(removeInclude);
		/*
		 * Delete selected included file
		 */
		removeInclude.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				
//				for (String key : currentIncludedFiles.get(currentFile).keySet()) {
//					validator.removeValue(selectedBank, "inherited:" + key, currentIncludedFiles);
//					currentIncludedFiles.get(selectedBank).remove("inherited:" + key);
//				}
				
				final String name = parentList.getItemText(parentList.getSelectedIndex());
				validator.removeBank(name, currentIncludedFiles);
				currentIncludedFiles.remove(name);
				parentList.removeItem(parentList.getSelectedIndex());
				server.propertiesFileExists(login, name, new AsyncCallback<Boolean>() {
					
					@Override
					public void onSuccess(Boolean result) {
						if (result)
							includeList.addItem(name.substring(name.lastIndexOf('/') + 1));
					}
					
					@Override
					public void onFailure(Throwable caught) {
						SC.warn("Could not test file existence");
					}
				});
				parentList.setSelectedIndex(0);
				loadChangedFile();
			}
		});
		
		/*
		 * Properties list
		 */
		
		buildPropertyPanel();
		
		final Anchor addPropertiesAnchor = new Anchor("Add a property...");
		addPropertiesAnchor.addStyleName(css.propsParameter());
		hpOptions.add(addPropertiesAnchor);
		addPropertiesAnchor.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propertyListPopup.showRelativeTo(addPropertiesAnchor);
			}
		});
		
		parentList.addChangeHandler(new ChangeHandler() {
			
			@Override
			public void onChange(ChangeEvent event) {
				loadChangedFile();
			}
		});
		vpOptions.add(hpOptions);
		
		
		
		/*
		 * Properties panel
		 */
		centerPanel.add(vpOptions);
		
		vpDependency.addStyleName(css.bankEditorSubPanel());
		centerPanel.add(vpDependency);
		
		DisclosurePanel syncPanel = new DisclosurePanel("Synchronization");
		DisclosurePanel postPsPanel = new DisclosurePanel("Post-processes");
		DisclosurePanel prePsPanel = new DisclosurePanel("Pre-processes");
		
		HorizontalPanel preHeader = new HorizontalPanel();
		preHeader.add(prePsPanel.getHeader());
		Image helpPre = new Image("images/help-hint.png");
		helpPre.addStyleName(css.helpIcon());
		helpPre.setTitle("From top to bottom is sequential, From left to right is parallel");
		preHeader.add(helpPre);
		prePsPanel.setHeader(preHeader);
		
		HorizontalPanel postHeader = new HorizontalPanel();
		postHeader.add(postPsPanel.getHeader());
		Image helpPost = new Image("images/help-hint.png");
		helpPost.addStyleName(css.helpIcon());
		helpPost.setTitle("From top to bottom is sequential, From left to right is parallel");
		postHeader.add(helpPost);
		postPsPanel.setHeader(postHeader);
		
		
		HorizontalPanel removeHeader = new HorizontalPanel();
		removeHeader.add(removePsPanel.getHeader());
		Image helpRemove = new Image("images/help-hint.png");
		helpRemove.addStyleName(css.helpIcon());
		helpRemove.setTitle("From top to bottom is sequential, From left to right is parallel");
		removeHeader.add(helpRemove);
		removePsPanel.setHeader(removeHeader);
		
		
		
		generalPanel.add(miscContent);
		initPanel.add(initContent);
		

		VerticalPanel syncContent = new VerticalPanel();
		
		releasePanel.add(releaseContent);
		syncContent.add(releasePanel);
		
		
		ftpCfgPanel.add(ftpContent);
		httpCfgPanel.add(httpContent);
		directHttpCfgPanel.add(directHttpContent);
		VerticalPanel downloadChildren = new VerticalPanel();
		downloadChildren.add(downloadContent);
		downloadChildren.add(ftpCfgPanel);
		downloadChildren.add(httpCfgPanel);
		downloadChildren.add(directHttpCfgPanel);
		downloadPanel.add(downloadChildren);
		syncContent.add(downloadPanel);
		
		extractPanel.add(extractContent);
		syncContent.add(extractPanel);
		
		makeReleasePanel.add(makeReleaseContent);
		syncContent.add(makeReleasePanel);
		syncPanel.add(syncContent);
		
		prePsPanel.add(preProcessEditor);
		postPsPanel.add(postProcessEditor);
		removePsPanel.add(removeProcessEditor);
		
		deploymentPanel.add(deploymentContent);
		
		centerPanel.add(pnlIncludeWarning);
		centerPanel.add(initPanel);
		centerPanel.add(syncPanel);
		centerPanel.add(prePsPanel);
		centerPanel.add(postPsPanel);
		centerPanel.add(removePsPanel);
		centerPanel.add(deploymentPanel);
		centerPanel.add(generalPanel);
		
		/*
		 * Buttons
		 */
		Command cmdOverride = new Command() {
			
			@Override
			public void execute() {
				
				if (validate()) {
					if (selectedBank.equals("global.properties")) {
						save(null, false);
					} else {
						String dbName = ((TextBox) validator.getWidget(selectedBank, "init__db.name")).getText();
						if (validator.getMode().equals(BankValidator.NEW) || type.equals("included") ||
								!(dbName + ".properties").equals(selectedBank)) {
							showSaveDialog(dbName, false);
						} else {
							save(null, false);
						}
					}
				}
			}
		};
		Command cmdSaveAs = new Command() {
			
			@Override
			public void execute() {
				if (currentIncludedFiles != null && validate()) {
					if (selectedBank.equals("global.properties")) {
						SC.warn("Operation not allowed for 'global.properties'");
					} else {
						String dbName = "";
						if (!type.equals("included"))
							dbName = ((TextBox) validator.getWidget(selectedBank, "init__db.name")).getText();
						showSaveDialog(dbName, false);
					}
				}
			}
		};
		Command cmdSaveAndRun = new Command() {
			
			@Override
			public void execute() {
				if (validate()) {
					
					if (selectedBank.equals("global.properties") || type.equals("included")) {
						SC.warn("Operation not allowed for 'global.properties'");
					} else {
						String dbName = ((TextBox) validator.getWidget(selectedBank, "init__db.name")).getText();
						if (validator.getMode().equals(BankValidator.NEW) ||
								!(dbName + ".properties").equals(selectedBank)) {
							showSaveDialog(dbName, true);
						} else {
							save(null, true);
						}
					}
				}
			}
		};
		
		menuSave.setVisible(false);
		menuSave.addStyleName(css.buttonMenu());
		MenuBar saveMenu = new MenuBar(true);
		menuSave.addItem("Save", saveMenu);
		saveMenu.addItem("Override", cmdOverride);
		saveMenu.addItem("Save as", cmdSaveAs);
		saveMenu.addItem("Save and run update", cmdSaveAndRun);
		
		MenuBar menuClose = new MenuBar();
		menuClose.addStyleName(css.buttonMenu());
		menuClose.addItem("Close", new Command() {
			
			@Override
			public void execute() {
				hide();
			}
		});
		
		bottomPanel.setSpacing(3);
		bottomPanel.add(menuSave);
		bottomPanel.add(menuClose);
		
		
		loadLocalBankList();
		
		Label loadBank = new Label("BANK SELECTION");
		loadBank.addStyleName(css.bankEditorTitle());
		Label properties = new Label("Properties");
		properties.addStyleName(css.bankEditorSmallTitle());
		Label options = new Label("Options");
		options.addStyleName(css.bankEditorSmallTitle());
		
		HorizontalPanel hpLoad = new HorizontalPanel();
		hpLoad.add(loadBank);
		hpLoad.setCellVerticalAlignment(loadBank, HasVerticalAlignment.ALIGN_BOTTOM);
		
		final HorizontalPanel hpOpt = new HorizontalPanel();
		hpOpt.add(options);
		hpOpt.setCellVerticalAlignment(options, HasVerticalAlignment.ALIGN_BOTTOM);
		
		final HorizontalPanel hpProps = new HorizontalPanel();
		hpProps.add(properties);
		hpProps.setCellVerticalAlignment(properties, HasVerticalAlignment.ALIGN_BOTTOM);
		
		mainPanel.add(hpLoad);
		mainPanel.add(topPanel);
		
		bankPanel.addStyleName(css.bankPanel());
		bankPanel.add(hpOpt);
		bankPanel.add(optionsPanel);
		bankPanel.add(hpProps);
		bankPanel.add(centerPanel);
		
		final Label titleSelect = new Label("SELECTED BANK : ");
		titleSelect.addStyleName(css.bankEditorTitle());
		lblSelectedBank.addStyleName(css.bankEditorTitleValue());
		
		hpSelectedBank.add(titleSelect);
		hpSelectedBank.add(lblSelectedBank);
		mainPanel.add(hpSelectedBank);
		mainPanel.add(bankPanel);
		
		mainPanel.add(bottomPanel);
		mainPanel.setCellHorizontalAlignment(bottomPanel, HasAlignment.ALIGN_CENTER);
		
		bankPanel.setVisible(false);
		hpSelectedBank.setVisible(false);
		
		/*
		 * Building top panel
		 */
		remoteBankList.setVisible(false);
		
		Image refreshList = new Image("images/view-refresh-4.png");
		refreshList.addStyleName(css.imgPointer());
		refreshList.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				loadLocalBankList();
			}
		});
		radioLoadLocal.setValue(true);
		
		
		final HorizontalPanel hpLocal = new HorizontalPanel();
		hpLocal.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		hpLocal.add(radioLoadLocal);
		hpLocal.add(localBankList);
		hpLocal.add(refreshList);
		localBankList.setWidth("200px");
		hpLocal.setCellWidth(radioLoadLocal, "150px");
		
		final HorizontalPanel hpRemote = new HorizontalPanel();
		hpRemote.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		final TextBox tbUrl = new TextBox();
		tbUrl.setWidth("200px");
		
		Image openSiteManager = new Image("images/bookmark_add.png");
		openSiteManager.addStyleName(css.imgPointer());
		openSiteManager.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				final SiteManagerDialog diag = new SiteManagerDialog(tbUrl.getText());
				diag.center();
				diag.addCloseHandler(new CloseHandler<PopupPanel>() {
					
					@Override
					public void onClose(CloseEvent<PopupPanel> event) {
						tbUrl.setText(diag.getSelectedUrl());
					}
				});
			}
		});
		
		Image goBtn = new Image("images/edit-find-5.png");
		goBtn.addStyleName(css.imgPointer());
		goBtn.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				if (!tbUrl.getText().trim().isEmpty())
					loadRemoteBankList(tbUrl.getText());
			}
		});
		hpRemote.add(radioLoadRemote);
		hpRemote.add(tbUrl);
		hpRemote.add(openSiteManager);
		hpRemote.add(goBtn);
		hpRemote.add(remoteBankList);
		hpRemote.setCellWidth(radioLoadRemote, "150px");
		
		final HorizontalPanel hpEmpty = new HorizontalPanel();
		hpEmpty.addStyleName(css.subCbPanel());
		hpEmpty.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		Label lblTemplate = new Label("Select template : ");
		lblTemplate.setWidth("150px");
		hpEmpty.add(lblTemplate);
		hpEmpty.add(templateList);
		hpEmpty.add(lblTemplateDesc);
		lblTemplateDesc.setHTML(BmajWatcherConstants.INSTANCE.mandatoryDescription());
		templateList.setWidth("200px");
		templateList.addItem("Mandatory");
		templateList.addItem("Common");
		templateList.addItem("Exhaustive");
		templateList.addChangeHandler(new ChangeHandler() {
			
			@Override
			public void onChange(ChangeEvent event) {
				String value = templateList.getItemText(templateList.getSelectedIndex());
				if (value.equals("Mandatory"))
					lblTemplateDesc.setHTML(BmajWatcherConstants.INSTANCE.mandatoryDescription());
				else if (value.equals("Common"))
					lblTemplateDesc.setHTML(BmajWatcherConstants.INSTANCE.commonDescription());
				else if (value.equals("Exhaustive"))
					lblTemplateDesc.setHTML(BmajWatcherConstants.INSTANCE.exhaustiveDescription());
			}
		});
		hpEmpty.setCellWidth(radioEmptyBank, "250px");
		
		hpLocal.setHeight("25px");
		hpRemote.setHeight("25px");
		hpEmpty.setHeight("25px");
		
		
		RadioButton rbEditBank = new RadioButton("neworedit", "Edit bank");
		rbEditBank.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				hpEmpty.setVisible(false);
				hpLocal.setVisible(true);
				hpRemote.setVisible(true);
			}
		});
		final RadioButton rbNewBank = new RadioButton("neworedit", "Create new bank");
		rbNewBank.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				hpEmpty.setVisible(true);
				hpLocal.setVisible(false);
				hpRemote.setVisible(false);
			}
		});
		rbEditBank.setValue(true);
		
		Button load = new Button("Load", new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				String value = "";
				if (rbNewBank.getValue()) {
					type = "empty";
					value = templateList.getValue(templateList.getSelectedIndex());
				} else if (radioLoadLocal.getValue()) {
					type = "local";
					value = localBankList.getValue(localBankList.getSelectedIndex());
				} else if (radioLoadRemote.getValue()) {
					type = "remote";
					if (remoteBankList.getItemCount() > 0) {
						value = remoteBankList.getValue(remoteBankList.getSelectedIndex());
					} else
						value = "";
				}
				
				hpIncludeList.setVisible(type.equals("local"));
				
				if (!value.isEmpty()) {
					bankPanel.setVisible(true);
					hpSelectedBank.setVisible(true);
					menuSave.setVisible(true);
					
					addInclude.setEnabled(type.equals("local"));
					getProperties(value);
				}
			}
		});
		
		VerticalPanel vpEdit = new VerticalPanel();
		vpEdit.addStyleName(css.subCbPanel());
		vpEdit.add(hpLocal);
		vpEdit.add(hpRemote);
		Label lblSelectBank = new Label("You can choose to either create a new bank or edit an existing bank's properties :");
		lblSelectBank.addStyleName(css.selectBankLabel());
		topPanel.add(lblSelectBank);
		topPanel.add(rbEditBank);
		topPanel.add(vpEdit);
		topPanel.add(rbNewBank);
		topPanel.add(hpEmpty);
		topPanel.add(load);
		
		hpEmpty.setVisible(false);
		
		topPanel.setCellHorizontalAlignment(load, HasHorizontalAlignment.ALIGN_CENTER);
		/*
		 * END topPanel
		 */
		
		add(mainPanel);
	}
	
	private void buildPropertyPanel() {
		propertyListPopup.addStyleName(css.propertyListPopup());
		
		PropertyCell tc = new PropertyCell();
		propsList = new CellList<String>(tc, propertyStyle);
		propsList.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION);
		
		final SingleSelectionModel<String> selectionModel = new SingleSelectionModel<String>();		
		propsList.setSelectionModel(selectionModel);
		selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
			
			@Override
			public void onSelectionChange(SelectionChangeEvent event) {
				showPropertyDescriptionPopup(selectionModel.getSelectedObject());
			}
		});
		
		
		ScrollPanel sp = new ScrollPanel(propsList);
		sp.setSize("250px", "320px");
		
		HorizontalPanel hpPopup = new HorizontalPanel();
		VerticalPanel vpLeft = new VerticalPanel();
		vpLeft.add(sp);
		hpPopup.add(vpLeft);
		
		
		/*
		 * Search
		 */
		VerticalPanel vpSearch = new VerticalPanel();
		VerticalPanel vpSubSearch = new VerticalPanel();
		vpSearch.addStyleName(css.propertyHelpSearchPanel());
		final TextBox tbSearch = new TextBox();
		tbSearch.setWidth("220px");
		final CheckBox cbOnName = new CheckBox("Search in name");
		cbOnName.setValue(true);
		final CheckBox cbOnDesc = new CheckBox("Search in description");
		
		tbSearch.addKeyPressHandler(new KeyPressHandler() {
			
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
					String searchType = null;
					if (cbOnName.getValue() && cbOnDesc.getValue())
						searchType = PropertyCell.SEARCH_IN_BOTH;
					else if (cbOnName.getValue())
						searchType = PropertyCell.SEARCH_IN_NAME;
					else if (cbOnDesc.getValue())
						searchType = PropertyCell.SEARCH_IN_DESC;
					updatePropertyList(tbSearch.getText(), searchType);
				}
			}
		});
		
		HorizontalPanel hpSearch = new HorizontalPanel();
		hpSearch.add(tbSearch);
		Image imgClear = new Image("images/edit-delete_Disabled.png");
		imgClear.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				updatePropertyList("", PropertyCell.SEARCH_IN_NAME);
			}
		});
		hpSearch.add(imgClear);
		hpSearch.setCellVerticalAlignment(imgClear, HasVerticalAlignment.ALIGN_MIDDLE);
		vpSubSearch.add(hpSearch);
		vpSubSearch.add(cbOnName);
		vpSubSearch.add(cbOnDesc);
		vpSearch.add(vpSubSearch);
		
		vpLeft.add(vpSearch);
		
		
		/*
		 * Description
		 */
		VerticalPanel vpDesc = new VerticalPanel();
		lblDescValue = new Label();
		
		vpDesc.add(lblDescValue);
		Button addProp = new Button("Add property", new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				addProperty(selectionModel.getSelectedObject());
				String prop = selectionModel.getSelectedObject();
				currentProperties.remove(prop.substring(0, prop.indexOf(':')));
				updatePropertyList(null, null);
			}
		});
		
		Button close  = new Button("Close", new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propertyListPopup.hide();
			}
		});
		
		/*
		 * Options
		 ********************************/
		
		HorizontalPanel hpMainOptions = new HorizontalPanel();
		VerticalPanel vpCategories = new VerticalPanel();
		Label lblCat = new Label("Categories");
		lblCat.addStyleName(css.propertyHelpSubTitle());
		vpCategories.add(lblCat);
		VerticalPanel vpSubCategories = new VerticalPanel();
		
		/*
		 * Categories filtering
		 */
		cbList = new ArrayList<CheckBox>();
		for (String key : PropertyCell.categories.keySet()) {
			String value = PropertyCell.categories.get(key);
			String title = value.substring(0, value.indexOf(':'));
			CheckBox cb = new CheckBox(title);
			cb.setValue(true);
			cb.setName("cb_" + key);
			cb.addClickHandler(new ClickHandler() {
				
				@Override
				public void onClick(ClickEvent event) {
					updatePropertyList(null, null);
				}
			}); 
			vpSubCategories.add(cb);
			cbList.add(cb);
		}
		SafeHtmlBuilder htmlBuilder = new SafeHtmlBuilder();
		htmlBuilder.appendHtmlConstant("<b>All</b>");
		final CheckBox cbAll = new CheckBox(htmlBuilder.toSafeHtml());
		
		cbAll.setValue(true);
		cbAll.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				boolean val = cbAll.getValue();
				for (CheckBox cb : cbList)
					cb.setValue(val);
				
				updatePropertyList(null, null);
			}
		});
		vpSubCategories.add(cbAll);
		vpCategories.add(vpSubCategories);
		vpSubCategories.addStyleName(css.propertyHelpCategoryPanel());

		hpMainOptions.add(vpCategories);
		VerticalPanel vpSubRight = new VerticalPanel();

		/*
		 * Sorting
		 */
		VerticalPanel vpFilter = new VerticalPanel();
		
		RadioButton rbNameSort = new RadioButton("sort", "Sort by name");
		rbNameSort.setValue(true);
		rbNameSort.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propSortType = PropertyCell.SORT_ON_NAME;
				updatePropertyList(null, null);
			}
		});
		RadioButton rbCatSort = new RadioButton("sort", "Sort by category");
		rbCatSort.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propSortType = PropertyCell.SORT_ON_CAT;
				updatePropertyList(null, null);
			}
		});
		
		Label lblSort = new Label("Sorting");
		lblSort.addStyleName(css.propertyHelpSubTitle());
		vpFilter.add(lblSort);
		VerticalPanel vpSubFilter = new VerticalPanel();
		vpSubFilter.addStyleName(css.propertyHelpSortPanel());
		vpSubFilter.add(rbNameSort);
		vpSubFilter.add(rbCatSort);
		vpFilter.add(vpSubFilter);
		
		
		
		
		
		/*
		 * Inheritance
		 */
		VerticalPanel vpInheritance = new VerticalPanel();
		VerticalPanel vpSubInheritance = new VerticalPanel();
		vpSubInheritance.addStyleName(css.propertyHelpInheritancePanel());
		Label lblInheritance = new Label("Inheritance");
		lblInheritance.addStyleName(css.propertyHelpSearchTitle());
		
		final CheckBox cbNonInherited = new CheckBox("Show non inherited");
		cbNonInherited.setValue(true);
		cbNonInherited.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propertyHelpShowNonInherited = cbNonInherited.getValue();
				updatePropertyList(null, null);
			}
		});
		final CheckBox cbShowInherited = new CheckBox("Show inherited");
		cbShowInherited.setValue(true);
		cbShowInherited.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propertyHelpShowInherited = cbShowInherited.getValue();
				updatePropertyList(null, null);
			}
		});
		vpSubInheritance.add(cbShowInherited);
		vpSubInheritance.add(cbNonInherited);
		
		vpInheritance.add(lblInheritance);
		vpInheritance.add(vpSubInheritance);
		
		/*
		 * Mandatoriness
		 */
		VerticalPanel vpMandatory = new VerticalPanel();
		VerticalPanel vpSubMandatory = new VerticalPanel();
		vpSubMandatory.addStyleName(css.propertyHelpInheritancePanel());
		Label lblMandatory = new Label("Mandatory properties");
		lblMandatory.addStyleName(css.propertyHelpSearchTitle());
		
		final CheckBox cbNonMandatory = new CheckBox("Show non mandatory");
		cbNonMandatory.setValue(true);
		cbNonMandatory.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propertyHelpShowNonMandatory = cbNonMandatory.getValue();
				updatePropertyList(null, null);
			}
		});
		final CheckBox cbMandatory = new CheckBox("Show mandatory");
		cbMandatory.setValue(true);
		cbMandatory.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				propertyHelpShowMandatory = cbMandatory.getValue();
				updatePropertyList(null, null);
			}
		});
		vpSubMandatory.add(cbMandatory);
		vpSubMandatory.add(cbNonMandatory);
		
		vpMandatory.add(lblMandatory);
		vpMandatory.add(vpSubMandatory);
		
		
		vpSubRight.add(vpFilter);
		vpSubRight.add(vpInheritance);
		vpSubRight.add(vpMandatory);
		
		hpMainOptions.add(vpSubRight);
		
		VerticalPanel vpRight = new VerticalPanel();
		
//		vpRight.add(lblDescTitle);
		
		DisclosurePanel discTop = new DisclosurePanel("Description");
		discTop.setOpen(true);
		discTop.add(vpDesc);
		
		vpRight.add(discTop);
		vpDesc.addStyleName(css.propertyHelpDescPanel());
		HorizontalPanel hpBtn = new HorizontalPanel();
		hpBtn.setSpacing(3);
		hpBtn.add(addProp);
		hpBtn.add(close);
		vpRight.add(hpBtn);
		vpRight.setCellHorizontalAlignment(hpBtn, HasHorizontalAlignment.ALIGN_RIGHT);
		
//		vpRight.add(optionTitle);
		
		DisclosurePanel discBtm = new DisclosurePanel("Options");
		discBtm.setOpen(true);
		discBtm.add(hpMainOptions);
		vpRight.add(discBtm);
		hpMainOptions.addStyleName(css.propertyHelpOptionPanel());
		
		hpPopup.add(vpRight);
		propertyListPopup.add(hpPopup);
	}
	
	/**
	 * Update the property list according to given parameters.
	 * 
	 * - properties categories to display
	 * - show inherited properties
	 * - sort type : sort categories of property names
	 * - search terms
	 * - search on property name or/and description
	 * 
	 * 
	 * @param search search terms
	 * @param searchCriteria search on property name or/and description
	 */
	private void updatePropertyList(String search, String searchCriteria) {
	
		List<String> categories = new ArrayList<String>();
		for (CheckBox check : cbList) {
			if (check.getValue()) {
				categories.add(check.getName().substring(3));
			}
		}
		
		Set<String> props = new TreeSet<String>();
		Set<String> sortedCats = new TreeSet<String>();
		for (String p : currentProperties) {
			
			boolean mandatory = validator.getMandatoryProps().contains(p);
			// Filter mandatoriness
			if (propertyHelpShowMandatory == propertyHelpShowNonMandatory && propertyHelpShowMandatory == false) {
				continue; // Show none
			} else if (propertyHelpShowMandatory != propertyHelpShowNonMandatory) {
				if ((propertyHelpShowMandatory && !mandatory) || (propertyHelpShowNonMandatory && mandatory))
					continue;
			}
			
			String value = propertyHelp.get(p);
			String type = value.substring(0, value.indexOf(':'));
			String desc = value.substring(value.indexOf(':') + 1);
			String inherited = String.valueOf(currentInheritedProperties.contains(p));
			
			// Filter inheritance
			if (propertyHelpShowInherited == propertyHelpShowNonInherited && propertyHelpShowInherited == false) {
				continue; // Dont show any
			} else if (propertyHelpShowInherited != propertyHelpShowNonInherited) { // Do not show both
				if ((propertyHelpShowInherited && !Boolean.valueOf(inherited)) // Show only inherited ?
						|| (propertyHelpShowNonInherited && Boolean.valueOf(inherited))) // Show only non inherited ?
					continue;
			}			
			
			// Filter by search terms
			if (searchCriteria != null) {
				
				if (searchCriteria.equals(PropertyCell.SEARCH_IN_NAME) && !p.contains(search))
					continue;
				if (searchCriteria.equals(PropertyCell.SEARCH_IN_DESC) && !desc.contains(search))
					continue;
				if (searchCriteria.equals(PropertyCell.SEARCH_IN_BOTH) && !desc.contains(search) && !p.contains(search))
					continue;
			}
			
			// Filter by categories
			if (categories == null || categories.contains(type)) {
				sortedCats.add(type);
				props.add(p + ":" + type + ":" + inherited + ":" + mandatory);
			}
		}
		
		// Sorting
		if (propSortType.equals(PropertyCell.SORT_ON_CAT)) {
			List<String> res = new ArrayList<String>();
			for (String cat : sortedCats) {
				for (String value : props) {
					if (value.split(":")[1].equals(cat)) {
						res.add(value);
					}
				}
			}
			propsList.setRowData(Arrays.asList(res.toArray(new String[res.size()])));
		} else { // TreeSet is naturally sorted
			propsList.setRowData(Arrays.asList(props.toArray(new String[props.size()])));
		}
	}

	private void loadChangedFile() {
		String value = parentList.getItemText(parentList.getSelectedIndex());
		
		vpDependency.setVisible(value.equals(selectedBank));
		
		// Save misc__ properties
		if (currentIncludedFiles.get(currentFile) != null) { // If include file was not removed
			Map<String, String> pre = preProcessEditor.getProcessProperties();
			Map<String, String> post = postProcessEditor.getProcessProperties();
			Map<String, String> remove = removeProcessEditor.getProcessProperties();
			
			Map<String, String> ps = new HashMap<String, String>();
			ps.putAll(pre);
			ps.putAll(post);
			ps.putAll(remove);
			validator.setBankProcesses(currentFile, ps);
			
			BankValidator.clearMiscProperties(currentIncludedFiles.get(currentFile));
			currentIncludedFiles.get(currentFile).putAll(ps);
			// Saving directhttp parameters
			currentIncludedFiles.get(currentFile).putAll(validator.getDirectHttpParameters(currentFile).getParameters());
		}
		
		pnlIncludeWarning.setVisible(false);
		if (value.equals("global.properties")) {
			pnlIncludeWarning.setVisible(true);
			if (admin)
				lblIncludeWarning.setText(BmajWatcherConstants.INSTANCE.globalWarning());
			else
				lblIncludeWarning.setText(BmajWatcherConstants.INSTANCE.globalForbidden());
		} else if (!value.equals(selectedBank)) { // another include file
			pnlIncludeWarning.setVisible(true);
			lblIncludeWarning.setText(BmajWatcherConstants.INSTANCE.includeWarning());
		}
		
		currentFile = value;
		boolean enable = currentFile.equals(selectedBank);
		seeInherited.setEnabled(enable);
		override.setEnabled(enable);
		removeInclude.setEnabled(!value.equals(selectedBank) && !value.equals("global.properties"));
		
		populateProperties(currentIncludedFiles.get(value));
	}
	
	private boolean validate() {
		if (currentIncludedFiles != null) {
			Map<String, String> ps = new HashMap<String, String>();
			
			ps.putAll(preProcessEditor.getProcessProperties());
			ps.putAll(postProcessEditor.getProcessProperties());
			ps.putAll(removeProcessEditor.getProcessProperties());
			validator.setBankProcesses(currentFile, ps);

			boolean looseValidation = selectedBank.equals("global.properties");
			return validator.validateValues(currentIncludedFiles, looseValidation);
		}
		return false;
	}
	
	private void save(final Map<String, String> newBankNames, boolean runUpdate) {
		
		Map<String, Map<String, String>> res = validator.getNewBankProperties();
		if (res != null) {
			/*
			 * Add include.properties property
			 */
			StringBuilder sb = new StringBuilder();
			for (String inc : currentIncludedFiles.keySet()) {
				if (!inc.equals(selectedBank) && !inc.equals("global.properties")) {
					sb.append(sb.length() == 0 ? inc : "," + inc);
					// If was not added to validator (file added only if selected in the
					// selectbox), add it there because still need to be created.
					if (!res.containsKey(inc))
						res.put(inc, null); // Null means that current content will not be overwritten
				}
			}
			
			if (sb.length() > 0)
				res.get(selectedBank).put("misc__include.properties", sb.toString());
			else {
				res.get(selectedBank).put("misc__include.properties", "__deleted");
			}
			
			if (newBankNames != null) {
				/*
				 * Add renamed include.properties for included files
				 */
				String includedProperties = "";
				for (String oldBank : newBankNames.keySet()) {
					if (!oldBank.equals("global.properties") && !oldBank.equals(selectedBank)) {
						includedProperties += newBankNames.get(oldBank) + ",";
					}
				}
				if (includedProperties.length() > 0) {
					res.get(selectedBank).put("misc__include.properties", includedProperties.substring(0, includedProperties.length() - 1));
				}
			}
			
			
			final int total = res.size();
			pos = 0;
			for (String bankName : res.keySet()) {
				if (admin || !bankName.equals("global.properties")) {
					final String newName = newBankNames != null && newBankNames.get(bankName) != null ?
							newBankNames.get(bankName).substring(0, newBankNames.get(bankName).lastIndexOf('.')) :
								bankName.substring(0, bankName.lastIndexOf('.'));
					
					server.save(login, type, bankName.substring(0, bankName.lastIndexOf('.')),
							newName, res.get(bankName), runUpdate && bankName.equals(selectedBank), new AsyncCallback<Integer>() {
						
						@Override
						public void onSuccess(Integer result) {
							if (result == 0) {
								SC.warn("That bank name already exists for another user. Try another name");
							} else if (result < 0) {
								SC.warn("Some kind of error happened");
							} else if (++pos == total) {
								final String nn = newBankNames != null ?
										newBankNames.get(selectedBank).substring(0, newBankNames.get(selectedBank).lastIndexOf('.')) :
											selectedBank.substring(0, selectedBank.lastIndexOf('.'));
								
								/*
								 * Update bank list and reload the bank
								 */
								server.getBankList(login, new AsyncCallback<Collection<String>>() {
									
									@Override
									public void onSuccess(Collection<String> result) {
										localBankList.clear();
										int index = 0;
										for (String s : result) {
											if (s.equals(nn))
												index = localBankList.getItemCount();
											localBankList.addItem(s);
										}
										
										localBankList.setSelectedIndex(index);
										radioLoadLocal.setValue(true);
										type = "local";
										getProperties(nn);
										
										MessagePanel.showMessage("Operation successful");
									}
									
									@Override
									public void onFailure(Throwable caught) {
										SC.say("Failure !");
									}
								});
							}
						}
						
						@Override
						public void onFailure(Throwable caught) {
							SC.say("Failure");
						}
					});
				}
			}
		}
	}
	
	private void showSaveDialog(String dbName, final boolean runUpdate) {
		final DialogBox dbSave = new DialogBox(false, true);
		dbSave.addStyleName(css.bankEditor());
		dbSave.setWidth("320px");
		dbSave.setHTML("<b>Save as...</b>");
		VerticalPanel vp = new VerticalPanel();
		vp.setSpacing(5);
		
		HorizontalPanel warningPnl = new HorizontalPanel();
		warningPnl.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		warningPnl.setSpacing(3);
		warningPnl.addStyleName(css.warningPanel());
		Image warningImg = new Image("images/vcs-conflicting.png");
		Label warningLbl = new Label("Note that BioMAJ needs the file name to be the same as db.name property. If these are different the bank won't be updated.", true);
		warningPnl.add(warningImg);
		warningPnl.add(warningLbl);
		vp.add(warningPnl);
		
		final Map<TextBox, String> boxes = new HashMap<TextBox, String>();
		for (String fileName : currentIncludedFiles.keySet()) {
			if (!fileName.equals("global.properties")) {
				TextBox tb = new TextBox();
				tb.setWidth("350px");
				if (fileName.equals(selectedBank))
					tb.setText(dbName + ".properties");
				else
					tb.setText(fileName);
				vp.add(tb);
				boxes.put(tb, fileName);
			}
		}
		HorizontalPanel hpBtn = new HorizontalPanel();
		Button ok = new Button("Save", new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				Map<String, String> newBankNames = new HashMap<String, String>();
				boolean valid = true;
				for (TextBox tb : boxes.keySet()) {
					if (tb.getText().trim().isEmpty() || tb.getText().equals(".properties")) {
						tb.addStyleName(css.bankEditorErrorWidget());
						valid = false;
						break;
					} else {
						tb.removeStyleName(css.bankEditorErrorWidget());
						newBankNames.put(boxes.get(tb), tb.getText());
					}
				}
				if (valid) {
					save(newBankNames, runUpdate);
					dbSave.hide();
				}
			}
		});
		Button cancel = new Button("Cancel", new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				dbSave.hide();
			}
		});
		hpBtn.add(ok);
		hpBtn.add(cancel);
		vp.add(hpBtn);
		vp.setCellHorizontalAlignment(hpBtn, HasHorizontalAlignment.ALIGN_CENTER);
		dbSave.setWidget(vp);
		dbSave.center();
	}
	
	private void loadLocalBankList() {
		server.getBankList(login, new AsyncCallback<Collection<String>>() {
			
			@Override
			public void onSuccess(Collection<String> result) {
				localBankList.clear();
				for (String s : result)
					localBankList.addItem(s);
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.say("Failure !");
			}
		});
	}
	
	private void loadRemoteBankList(String url) {
		server.getRemoteBankList(url, new AsyncCallback<Collection<String>>() {
			
			@Override
			public void onSuccess(Collection<String> result) {
				remoteBankList.setVisible(true);
				for (String s : result) {
					remoteBankList.addItem(s);
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.say("Failure !");
			}
		});
	}
	
	private void getProperties(final String bankName) {
		override.setEnabled(true);
		seeInherited.setEnabled(true);
		removeInclude.setEnabled(false);
		
		pnlIncludeWarning.setVisible(false);
		
		vpDependency.clear();
		vpDependency.setVisible(!bankName.equals("global"));
		dependencyBuilder = new BankDependencyBuilder(localBankList);
		vpDependency.add(dependencyBuilder.getDependencyPanel());
		
		if (type.equals("empty")) {
			lblSelectedBank.setText("New bank");
			server.getEmptyBank(bankName, new AsyncCallback<Map<String, Map<String,String>>>() {
				
				@Override
				public void onSuccess(Map<String, Map<String, String>> result) {
					parentList.clear();
					parentList.addItem(bankName + ".properties");
					parentList.addItem("global.properties");
					selectedBank = bankName + ".properties";
					currentFile = bankName + ".properties";
					currentIncludedFiles = result;
					validator.clear(selectedBank, BankValidator.NEW);
					populateProperties(result.get(currentFile));
				}
				
				@Override
				public void onFailure(Throwable caught) {
					SC.say("Failure !");
				}	
			});
		} else {
			lblSelectedBank.setText(bankName);
			loadBank(bankName, new ArrayList<String>());
		}
	}
	
	private void loadBank(final String bankName, final Collection<String> includedProps) {
		
		/*
		 * Load includable files
		 */
		server.getIncludePropertiesList(login, new AsyncCallback<Collection<String>>() {
			
			@Override
			public void onSuccess(Collection<String> result) {
				includeList.clear();
				includeList.addItem(CREATE_NEW_INCLUDE);
				for (String s : result) {
					includeList.addItem(s);
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Could not retrieve included properties");
			}
		});
		
		server.getBankProperties(login, bankName, type, includedProps, new AsyncCallback<Map<String, String>>() {
			
			@Override
			public void onSuccess(final Map<String, String> mainResult) {
				if (mainResult != null) {
					parentList.clear();
					
					server.getIncludedProperties(login, bankName, includedProps, new AsyncCallback<Map<String,Map<String,String>>>() {
						
						@Override
						public void onSuccess(Map<String, Map<String, String>> result) {
							if (result != null) {
								parentList.addItem(bankName + ".properties");
								for (String key : result.keySet()) {
									parentList.addItem(key);
									if (!key.equals("global.properties")) {
										removeItemFromList(key.substring(key.lastIndexOf('/') + 1), includeList);
									}
								}
								currentFile = bankName + ".properties";
								selectedBank = bankName + ".properties";
								currentIncludedFiles = result;
								currentIncludedFiles.put(currentFile, mainResult);
								if (type.equals("local"))
									validator.clear(selectedBank, BankValidator.MODIFY);
								else
									validator.clear(selectedBank, BankValidator.NEW);
								

								validator.setDependenciesParameters(bankName + ".properties", dependencyBuilder);
								
								populateProperties(mainResult);
							} else {
								SC.warn("Load failure ! Please check the bank properties");
							}
						}
						
						@Override
						public void onFailure(Throwable caught) {
							SC.say("Failure !");
						}
					});
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.say("Failure !");
			}
		});
	}
	
	private static void removeItemFromList(String toRemove, ListBox list) {
		for (int i = 0; i < list.getItemCount(); i++) {
			if (list.getItemText(i).equals(toRemove)) {
				list.removeItem(i);
				break;
			}
		}
	}
	
	private void addProperty(String key) {
	
		String newKey = key.substring(0, key.indexOf(':'));
		
		server.getPrefixedKey(newKey, new AsyncCallback<String>() {
			
			@Override
			public void onSuccess(String result) {
				currentIncludedFiles.get(currentFile).put(result, "");
				String inheritedProp = "inherited:" + result;
				// If a parent file is selected, add to descendants
				if (currentFile.equals("global.properties")) {
					for (String bank : currentIncludedFiles.keySet()) {
						if (!bank.equals("global.properties"))
							currentIncludedFiles.get(bank).put(inheritedProp, "");
					}
				} else if (!currentFile.equals(selectedBank)) {
					currentIncludedFiles.get(selectedBank).put(inheritedProp, "");
				}
				buildPropertyLine(result, "", null);
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.warn("Could not retrieve prefixed key !");
			}
		});
			
	}
	
	private void buildPropertyLine(final String key, String propValue, Map<String, String> processContent) {
		
		final String[] split = key.split("__");
		String type = "";
		boolean inherited = false;
		if (split[0].contains(":")) { 
			type = split[0].split(":")[1];
			inherited = true;
		} else
			type = split[0];
		
		boolean isBinary = false;
		boolean isList = false;
		String[] elts = {};
		if (type.contains(">")) {
			if ((elts = getPropertyElement(type)) == null)
				isBinary = true;
			else
				isList = true;
			
			type = type.split(">")[0];
		}
		
		if (type.equals("misc") && processContent != null) {
			processContent.put(split[1], propValue);
			return;
		}
		
		if (inherited) {
			currentInheritedProperties.add(split[1]);
		}
		
		if (inherited && seeInherited.getValue() || !inherited) {
			
			currentProperties.remove(split[1]);
			
			HorizontalPanel hp = new HorizontalPanel();
			hp.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
			Label label = new Label(split[1]);
			label.setWidth("200px");
			
			PushButton btnRemove = new PushButton(new Image("images/edit-delete.png"));
			btnRemove.addStyleName(css.removePropertyButton());
			hp.add(btnRemove);
			if (!selectedBank.equals("global.properties") && validator.getMandatoryProps().contains(split[1]) && !inherited && !BankValidator.propertyExistsInOtherFile(key, currentFile, currentIncludedFiles)) {
				btnRemove.setEnabled(false);
			} else {
				btnRemove.addClickHandler(new ClickHandler() {
					
					@Override
					public void onClick(ClickEvent event) {
						validator.removeValue(currentFile, key, currentIncludedFiles);
						
						currentProperties.add(split[1]);
						updatePropertyList(null, null);

					}
				});
			}
			hp.add(label);
			
			FocusWidget w = validator.getWidget(currentFile, key);
			if (w != null) {
				hp.add(w);
				w.setEnabled(override.getValue() && inherited || !inherited);
			} else {
				if (isBinary) {
					CheckBox cb = new CheckBox();
					cb.setEnabled(override.getValue() && inherited || !inherited);
					hp.add(cb);
					cb.setValue(Boolean.valueOf(propValue));
					validator.addValue(currentFile, key, cb);
				} else if (isList) {
					ListBox lb = new ListBox(false);
					int index = 0;
					for (int i = 0; i < elts.length; i++) {
						if (elts[i].equals(propValue))
							index = i;
						lb.addItem(elts[i]);
						
					}
					lb.setEnabled(override.getValue() && inherited || !inherited);
					lb.setSelectedIndex(index);
					hp.add(lb);
					validator.addValue(currentFile, key, lb);
				} else {
					TextBox value;
					if (split[1].equals("password"))
						value = new PasswordTextBox();
					else
						value = new TextBox();
					
					validator.addValue(currentFile, key, value);
					value.addStyleName(css.bankEditorTextBox());
					value.setEnabled(override.getValue() && inherited || !inherited);
					value.setText(propValue);
					hp.add(value);
				}
			}
			
			if (inherited && !override.getValue())
				validator.updateInheritedProperty(key, currentFile);
			
			Image help = new Image("images/help-about.png");
			if (this.propertyHelp != null) {
				String desc = this.propertyHelp.get(split[1]);
				help.setTitle(desc.substring(desc.indexOf(':') + 1));
			}
			help.addStyleName(css.helpIcon());
			hp.add(help);
			
			if (type.equals("general")) {
				generalPanel.setVisible(true);
				miscContent.add(hp);
			} else if (type.equals("init")) {
				initPanel.setVisible(true);
				initContent.add(hp);
			} else if (type.equals("ftpconfig")) {
				downloadPanel.setVisible(true);
				ftpCfgPanel.setVisible(true);
				ftpContent.add(hp);
			} else if (type.equals("httpconfig")) {
				downloadPanel.setVisible(true);
				httpCfgPanel.setVisible(true);
				httpContent.add(hp);
			} else if (type.equals("directhttpconfig")) {
				downloadPanel.setVisible(true);
				directHttpCfgPanel.setVisible(true);
				directHttpContent.add(hp);
			} else if (type.equals("release")) {
				releasePanel.setVisible(true);
				releaseContent.add(hp);
			} else if (type.equals("download")) {
				downloadPanel.setVisible(true);
				downloadContent.add(hp);
			} else if (type.equals("extract")) {
				extractPanel.setVisible(true);
				extractContent.add(hp);
			} else if (type.equals("makerelease")) {
				makeReleasePanel.setVisible(true);
				makeReleaseContent.add(hp);
			} else if (type.equals("deployment")) {
				deploymentPanel.setVisible(true);
				deploymentContent.add(hp);
			}
		}
	}
	
	private void populateProperties(Map<String, String> map) {
		
		miscContent.clear();
		initContent.clear();
		releaseContent.clear();
		downloadContent.clear();
		extractContent.clear();
		makeReleaseContent.clear();
		ftpContent.clear();
		httpContent.clear();
		directHttpContent.clear();
		deploymentContent.clear();

		generalPanel.setVisible(false);
		initPanel.setVisible(false);
		ftpCfgPanel.setVisible(false);
		httpCfgPanel.setVisible(false);
		downloadPanel.setVisible(false);
		extractPanel.setVisible(false);
		deploymentPanel.setVisible(false);
		makeReleasePanel.setVisible(false);
		releasePanel.setVisible(false);
		
		Map<String, String> processContent = new HashMap<String, String>();
		
		validator.setRefValues(currentFile, map);
		
		// Rebuild of property list
		currentInheritedProperties.clear();
		currentProperties.clear();
		for (String p : propertyHelp.keySet()) {
			currentProperties.add(p);
		}
		
		// Populate properties
		DirectHttpParameterBuilder dhp = new DirectHttpParameterBuilder();
		for (final String key : map.keySet()) { // inherited:type>list|el1,el2_prop
			if (key.endsWith("url.params")) {
				directHttpCfgPanel.setVisible(true);
				downloadPanel.setVisible(true);
			} else if (key.endsWith(".value")) {
				dhp.addParameterValue(key, map.get(key));
			} else if (key.endsWith("db.source")) {
				dependencyBuilder.setDbSource(map.get(key));
			} else if (key.endsWith("ref.release")) {
				dependencyBuilder.setRefRelease(map.get(key));
			} else if (key.endsWith(".files.move")) {
				dependencyBuilder.addParameterValue(key, map.get(key));
			} else {
				buildPropertyLine(key, map.get(key), processContent);
			}
		}
		
		// Dependencies
		vpDependency.clear();
		vpDependency.add(dependencyBuilder.getDependencyPanel());
		validator.setDependenciesParameters(currentFile, dependencyBuilder);
		
		// Direct http
		DisclosurePanel directHttpParameterPanel = new DisclosurePanel("URL parameters");
		directHttpParameterPanel.add(dhp.getComponent());
		directHttpContent.add(directHttpParameterPanel);
		validator.setDirectHttpParameters(currentFile, dhp);
		
		updatePropertyList(null, null);
		
		preProcessEditor.load(processContent, ProcessEditor.PRE);
		postProcessEditor.load(processContent, ProcessEditor.POST);
		removeProcessEditor.load(processContent, ProcessEditor.REMOVE);
	}
	
	/**
	 * Returns a list of elements or null if the string represents
	 * a binary field.
	 * 
	 * @param s
	 * @return
	 */
	private String[] getPropertyElement(String s) {
		//inherited:type>list|el1,el2_prop
		
		String[] split = s.split(">");
		if (split[1].equals("bin"))
			return null;
		else {
			return split[1].split("\\|")[1].split(",");
		}
	}
	
	/**
	 * Displays the dialog with the given bank loaded.
	 * 
	 * @param bank
	 */
	public void show(final String bank) {
		
		/*
		 * Refreshes bank list
		 */
		server.getBankList(login, new AsyncCallback<Collection<String>>() {
			
			@Override
			public void onSuccess(Collection<String> result) {
				localBankList.clear();
				int index = -1;
				int i = 0;
				for (String s : result) {
					localBankList.addItem(s);
					if (index < 0 && s.equals(bank))
						index = i;
					i++;
				}
				
				if (index < 0) { // Bank not found
					SC.warn("Can't load bank. '" + bank + ".properties' does not exist.");
				} else {
					localBankList.setSelectedIndex(index);
					hpSelectedBank.setVisible(true);
					bankPanel.setVisible(true);
					menuSave.setVisible(true);
					
					type = "local";
					getProperties(bank);
					show();
				}
				
			}
			
			@Override
			public void onFailure(Throwable caught) {
				SC.say("Failure !");
			}
		});
	}
	
	/**
	 * 
	 * @param propName
	 */
	private void showPropertyDescriptionPopup(String prop) {
		String value = propertyHelp.get(prop.substring(0, prop.indexOf(':')));
		lblDescValue.setText(value.substring(value.indexOf(':') + 1));
	}
	
	public static BankEditorDialog getInstance(String login) {
		if (instance == null)
			instance = new BankEditorDialog(login);
		else if (!instance.login.equals(login))
			instance = new BankEditorDialog(login);
		
		return instance;
	}
}
