/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.ui.attribute.editor;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import org.jboss.tools.common.model.ui.IListEditor;
import org.jboss.tools.common.model.ui.IValueProvider;
import org.jboss.tools.common.model.ui.attribute.IListContentProvider;
import org.jboss.tools.common.model.ui.attribute.adapter.DefaultValueAdapter;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.jboss.tools.common.model.ui.widgets.IWidgetSettings;

public class MultipleChoiceFieldEditor extends ExtendedFieldEditor 
        implements IPropertyFieldEditor, IListEditor, IFieldEditor, PropertyChangeListener {
	protected IPropertyEditor propertyEditor;
	protected IValueProvider valueProvider;
	protected ILabelProvider labelProvider;
	protected IListContentProvider listContentProvider;
	
	protected Composite list;
	protected Button[] boxes = new Button[0];
	
	protected Composite buttonsParent;
	Map<String,Choice> choicesMap = new HashMap<String,Choice>();
	ArrayList<Choice> choicesArray = new ArrayList<Choice>();
	
	public MultipleChoiceFieldEditor() {}
	
	public MultipleChoiceFieldEditor(IWidgetSettings settings) {
		super(settings);
	}
	
	private void addChoice(Choice c) {
		choicesMap.put(c.name, c);
		choicesArray.add(c);
	}
	private void removeChoice(Choice c) {
		choicesMap.remove(c.name);
		choicesArray.remove(c);
	}
	
	class Choice extends SelectionAdapter {
		String name;
		boolean selected;
		boolean allowed = true;
		Button box;
		
		public Choice(String name) {
			this.name = name;
			addChoice(this);
		}
		
		public void setSelected(boolean b) {
			selected = b;
			if(box != null && !box.isDisposed()) box.setSelection(selected);
		}
		
		public void setAllowed(boolean b) {
			allowed = b;
			if(box != null && !box.isDisposed()) {
				int color = (allowed) ? SWT.COLOR_BLACK : SWT.COLOR_DARK_RED;
				box.setForeground(Display.getDefault().getSystemColor(color));
			} 
		}
		
		public void widgetSelected(SelectionEvent e) {
			selected = box.getSelection();
			fireValueChange();
		}
		
		public void create() {
			if(buttonsParent == null || buttonsParent.isDisposed()) return;
			if(box == null || box.isDisposed()) {
				box = new Button(buttonsParent, SWT.CHECK);
				box.setText(name);
				box.addSelectionListener(this);
			}
		}
		
		public void dispose() {
			if(box != null && !box.isDisposed()) {
				box.dispose();
				box = null;
				removeChoice(this);
			}
		}
		
	}
	
	protected Choice getChoice(String name, boolean create) {
		Choice c = (Choice)choicesMap.get(name);
		if(c == null && create) c = new Choice(name);
		if(c != null) c.create();
		return c;
	}

	public void setPropertyEditor(IPropertyEditor propertyEditor) {
		this.propertyEditor = propertyEditor;
		if (propertyEditor!=null) {
			valueProvider = (IValueProvider)propertyEditor.getAdapter(IValueProvider.class);
			listContentProvider = (IListContentProvider)propertyEditor.getAdapter(IListContentProvider.class);
			labelProvider = (ILabelProvider)propertyEditor.getAdapter(ILabelProvider.class);
		}
		valueProvider.addValueChangeListener(this);
	}

	protected void adjustForNumColumns(int numColumns) {
		Control control = getLabelComposite();
		((GridData)control.getLayoutData()).horizontalSpan = numColumns;
		((GridData)list.getLayoutData()).horizontalSpan = numColumns - 1;
	}

	protected void doFillIntoGrid(Composite parent, int numColumns) {
		Control control = getLabelComposite(parent);
		GridData gd = new GridData();
		gd.horizontalSpan = numColumns;
		control.setLayoutData(gd);
		list = createListControl(parent);
		gd = new GridData(GridData.FILL_BOTH);
		gd.horizontalSpan = numColumns - 1;
		gd.grabExcessHorizontalSpace = true;
		list.setLayoutData(gd);
	}

	protected void doLoad() {}

	protected void doLoadDefault() {}

	protected void doStore() {}

	public int getNumberOfControls() {
		return 2;
	}

	public void setLabelProvider(ILabelProvider labelProvider) {
		this.labelProvider = labelProvider;
	}

	public void setListContentProvider(IListContentProvider listContentProvider) {
		this.listContentProvider = listContentProvider;
	}

	public Control[] getControls(Composite parent) {
		return new Control[] {getLabelComposite(parent), createListControl(parent)};
	}

	boolean propertyChangeEnabled = true;
	public void propertyChange(PropertyChangeEvent evt) {
		if(!propertyChangeEnabled) return;
		super.propertyChange(evt);
		valueProvider.removeValueChangeListener(this);
		if (IPropertyEditor.VALUE.equals(evt.getPropertyName())) {
			updateChoices();
		} else if (IPropertyEditor.LIST_CONTENT.equals(evt.getPropertyName())) {
			DefaultValueAdapter a = (DefaultValueAdapter)propertyEditor.getInput();
			a.load();
			String newValue = (String)valueProvider.getValue();
			valueProvider.setValue(""); //$NON-NLS-1$
			resetChoices();
			valueProvider.setValue(newValue);
		}
		valueProvider.addValueChangeListener(this);
	}

	protected Control getListControl() {
		return list;
	}

	protected Composite createListControl(Composite parent) {
		if (list == null ||list.isDisposed()) {
			ScrolledComposite sp = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
			sp.setLayoutData(new GridData(GridData.FILL_BOTH));
			list = sp;
		
			sp.setLayout(new GridLayout());
			Composite c = new Composite(sp, SWT.NONE);
			buttonsParent = c;
			c.setLayoutData(new GridData(GridData.FILL_BOTH));
			sp.setContent(c);
			c.setLayout(new GridLayout());
			resetChoices();
		} else {
			checkParent(list, parent);
		}
		return list;
	}
	
	protected void updateChoices() {
		Set<String> m = new HashSet<String>();
		m.addAll(choicesMap.keySet());
		String[] es = (String[])listContentProvider.getElements(null);
		Set<String> ess = new HashSet<String>();
		for (int i = 0; i < es.length; i++) {
			ess.add(es[i]);
		}
		String value = (String)valueProvider.getValue();
		if(value == null) value = ""; //$NON-NLS-1$
		StringTokenizer st = new StringTokenizer(value, ",;"); //$NON-NLS-1$
		String[] vs = new String[st.countTokens()];
		Set<String> vss = new HashSet<String>();
		for (int i = 0; i < vs.length; i++) {
			vs[i] = st.nextToken();
			vss.add(vs[i]);
		}
		for (int i = 0; i < vs.length; i++) {
			Choice c = getChoice(vs[i], true);
			c.setSelected(true);
			c.setAllowed(ess.contains(vs[i]));
			m.remove(vs[i]);
		}
		for (int i = 0; i < es.length; i++) {
			Choice c = getChoice(es[i], true);
			c.setSelected(vss.contains(es[i]));
			c.setAllowed(true);
			m.remove(es[i]);
		}
		String[] obs = (String[]) m.toArray(new String[0]);
		for (int i = 0; i < obs.length; i++) {
			Choice c = getChoice(obs[i], false);
			if(c != null) {
				c.dispose();
				choicesMap.remove(obs[i]);
			}
		}
		boolean isEmpty = (es.length == 0 && vs.length == 0);
		updateEmpty(isEmpty);
	}
	
	Label empty = null;
	
	void updateEmpty(boolean isEmpty) {
		if(isEmpty == (empty != null && !empty.isDisposed())) return;
		if(isEmpty) {
			empty = new Label(buttonsParent, SWT.NONE);
			empty.setText("Choice list is empty.");
		} else {
			if(empty != null) {
				if(!empty.isDisposed()) empty.dispose();
				empty = null;
			}
		}
	}
	
	public void resetChoices() {
		updateChoices();
		buttonsParent.pack();
		buttonsParent.layout();
	}

	protected void fireValueChange() {
		StringBuffer sb = new StringBuffer();
		Choice[] cs= (Choice[])choicesArray.toArray(new Choice[0]);
		for (int i = 0; i < cs.length; i++) {
			if(!cs[i].selected) continue;
			if(sb.length() > 0) sb.append(',');
			sb.append(cs[i].name);
		}
		propertyChangeEnabled = false;
		valueProvider.setValue(sb.toString());
		propertyChangeEnabled = true;
	}

	public void setEnabled(boolean enabled){
		// TODO;
		super.setEnabled(enabled);
		if (getListControl()!=null) {
			getListControl().setEnabled(enabled);
		}
	}

	public void setFocus() {
		if(list != null && !list.isDisposed()) {
			list.setFocus();
		}
	}

	public void cut() {}

	public void copy() {}

	public void paste() {}

	public void delete() {}

}
