package hibergui.assoc;

import hibergui.Form;
import hibergui.I18n;
import hibergui.event.ModificationEvent;
import hibergui.event.ModificationListener;

import java.lang.reflect.InvocationTargetException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;

import org.apache.commons.beanutils.PropertyUtils;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

public abstract class M2OEdit_Combo<T> extends M2OEdit<T> {

	private Composite cpt;
    private Combo cmb;

    protected final String editProp;
    private List<T> values;

    private int matchIndex;
    private static enum MatchState {UNAMBIGUOUS, EXACT, NO_MATCH}
    private MatchState state = MatchState.UNAMBIGUOUS;
    
    Collator coll = Collator.getInstance(new Locale("hu", "HU"));
    
    public M2OEdit_Combo(final Form<?> form, final Composite cptParent, int style, String prop, String name, String editProp) {
		super(form, cptParent, style, prop, name);

		this.editProp = editProp;
		
		cmb.addSelectionListener(lsn_WIDGET_MOD);
		cmb.addModifyListener(lsn_WIDGET_MOD);
        cmb.addTraverseListener(lsn_TRAVERSE);

		addModificationListener(new ModificationListener() {
			public void modification(ModificationEvent<?> e) {
				int i = 0, cnt = cmb.getItemCount();
				String text = cmb.getText().toLowerCase();
				while (i < cnt && 
						(cmb.getItem(i).length() < text.length() || 
								coll.compare(text, cmb.getItem(i).substring(0, text.length()).toLowerCase()) > 0))
					++i;
				if (i == cnt || 
						cmb.getItem(i).length() < text.length() || 
						coll.compare(text, cmb.getItem(i).substring(0, text.length()).toLowerCase()) != 0) {
					state = MatchState.NO_MATCH;
					setBackground(hibergui.HiberGUI.COLOR_INVALID);
				}
				else if (i + 1 == cnt || 
						cmb.getItem(i + 1).length() < text.length() || 
						coll.compare(text, cmb.getItem(i + 1).substring(0, text.length()).toLowerCase()) != 0) {
					state = MatchState.EXACT;
					setBackground(hibergui.HiberGUI.COLOR_EXACT);
				}
				else {
					state = MatchState.UNAMBIGUOUS;
//					setBackground(hibergui.HiberGUI.COLOR_UNAMBIGUOUS);
				}
				matchIndex = i;
			}
		});
		
		cmb.addFocusListener(new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent e) {
			}
			@Override
			public void focusLost(FocusEvent e) {
				if (cmb.getSelectionIndex() != -1)
					return;
				if (form.isSearchMode())
					return;
				if (state == MatchState.EXACT) {
					set(values.get(matchIndex));
					cmb.select(matchIndex);
					resetBackground();
				}
				else if (state == MatchState.UNAMBIGUOUS) {
					// TODO
					set(values.get(matchIndex));
					cmb.select(matchIndex);
					resetBackground();
				}
				else {
					if (MessageDialog.openQuestion(getControl().getShell(), "",
							I18n.get("question.do_not_exist.0") + getName() + I18n.get("question.do_not_exist.2"))) {
						dlgAssoc = createAssocDialog(cptParent.getShell());
//						dlgAssoc.getForm().setSearchMode(false);
						dlgAssoc.open();
						if (dlgAssoc.getForm().hasChanged()) {
							refresh();
						}
						T[] result = dlgAssoc.getResult();

						if (result.length != 0)
							setSelection(result);
					}
					else
						clear();
				}
			}
		});
		
		cmb.addFocusListener(lsn_WIDGET_FOCUS);

		addKeyListener(new hibergui.event.KeyListener() {
			public void keyPressed(hibergui.event.KeyEvent e) {
				KeyEvent event = e.wrapped;
				if (event.keyCode == 16777218) {
					if (cmb.getSelectionIndex() != -1)
						return;
					cmb.select(matchIndex);
					resetBackground();
					if ((event.stateMask & SWT.ALT) == 0)
						event.doit = false;
				}
			}
			public void keyReleased(hibergui.event.KeyEvent e) {
			}
		});
		cmb.addKeyListener(lsn_WIDGET_KEY);
	}

    @Override
    protected Control createContents(final Composite cptParent) {
        cpt = new Composite(cptParent, SWT.NONE);
        cpt.setLayout(new FormLayout());
        
        FormData data;
        
        cmb = new Combo(cpt, SWT.BORDER | SWT.DROP_DOWN);
        
        Button crlButton = super.createAssocDialogButton(cpt);
        
        data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(crlButton, -12);
        data.bottom = new FormAttachment(100, 0);
        cmb.setLayoutData(data);
        
        data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        crlButton.setLayoutData(data);
        
        return cpt;
    }

    protected final String getItemText(T item) {
		if (item == null)
			return "<NULL>";
    	try {
    		return PropertyUtils.getProperty(item, editProp).toString();
    	} catch (IllegalAccessException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (InvocationTargetException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (NoSuchMethodException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    	return "<N/A>";
    }
    
    public String getText() {
        return cmb.getText();
    }
    
    private void setComboInput(Collection<T> c) {
        values = new ArrayList<T>(c.size() + 1);
        values.add(0, null);
        values.addAll(c);

        String[] items = new String[values.size()];
       	items[0] = "";
        for (int i = 1; i < values.size(); ++i) {
        	T t = values.get(i);
            items[i] = (t == null) ? " " : getItemText(t);
        }
        cmb.removeModifyListener(lsn_WIDGET_MOD);
        cmb.setItems(items);
        cmb.deselectAll();
        cmb.addModifyListener(lsn_WIDGET_MOD);
    }
    
    @Override
    public void clear() {
    	cmb.deselectAll();
    }
    
    @Override
    public void fill() {
        cmb.select(values.indexOf(value));
    }
    
    @Override
    public T extract() {
    	if (cmb.getSelectionIndex() != -1)
    		return values.get(cmb.getSelectionIndex());
    	if (getText().length() == 0)
    		return null;
        return super.extract();
    }
    
    @Override
    public final void extract(T t) {
    	try {
    		PropertyUtils.setProperty(t, editProp, getText());
    	} catch (IllegalAccessException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (InvocationTargetException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (NoSuchMethodException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    }
    
    @Override
    public void refresh() {
//    	setComboInput(findAll());
    	setComboInput(findAll());
    }
    
    @Override
    public void setBackground(Color clr) {
    	cmb.setBackground(clr);
    }
	
	@Override
    public boolean setFocus() {
        return cmb.setFocus();
    }
    
    @Override
    public boolean isEnabled() {
        return cmb.isEnabled();
    }
    
    @Override
    public void setEnabled(boolean b) {
    	if (!isFixed() || !b) {
    		cmb.setEnabled(b);
    		btnAssoc.setEnabled(b);
    	}
    }

	public void setDefault(String value) {
		int i = 0;
		for (String item: cmb.getItems()) {
			if (item.equals(value)) {
				setDefault(values.get(i));
				return;
			}
			++i;
		}
	}
	
	@Override
	protected void fillForm(Form<T> f) {
		f.fill(editProp, getText());
	}
}
