package org.orienteer.core.service.impl;

import com.google.common.base.Function;
import com.google.common.collect.Ordering;
import org.apache.wicket.Component;
import org.apache.wicket.markup.IMarkupFragment;
import org.apache.wicket.markup.Markup;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.util.lang.Generics;
import org.orienteer.core.component.structuretable.StructureTable;
import org.orienteer.core.service.IMarkupProvider;
import org.wicketstuff.select2.Select2Choice;
import org.wicketstuff.select2.Select2MultiChoice;

import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Implementation of {@link IMarkupProvider}
 */
@Singleton
public class MarkupProvider implements IMarkupProvider
{
	private ConcurrentHashMap<Class<? extends Component>, IMarkupFragment> markupsCache = Generics.newConcurrentHashMap();
	private ConcurrentHashMap<Class<? extends Component>, IMarkupFragment> markupsMap = Generics.newConcurrentHashMap();
	
	public MarkupProvider()
	{
		registerMarkupContent(DropDownChoice.class, "<select wicket:id=\"component\" class=\"custom-select\"></select>");
		registerMarkupContent(ListMultipleChoice.class, "<select wicket:id=\"component\" class=\"form-control\"></select>");
		registerMarkupContent(CheckBox.class, "<input type=\"checkbox\" wicket:id=\"component\"/>");
		registerMarkupContent(TextField.class, "<input type=\"text\" wicket:id=\"component\" class=\"form-control\"/>");
		registerMarkupContent(NumberTextField.class, "<input type=\"number\" wicket:id=\"component\" class=\"form-control\"/>");
		registerMarkupContent(TextArea.class, "<textarea wicket:id=\"component\" class=\"form-control\"></textarea>");
		registerMarkupContent(FormComponentPanel.class, "<div wicket:id=\"component\"></div>");
		registerMarkupContent(Panel.class, "<div wicket:id=\"component\"></div>");
		registerMarkupContent(AbstractLink.class, "<a wicket:id=\"component\"></a>");
		registerMarkupContent(StructureTable.class, "<table wicket:id=\"component\"></table>");
		registerMarkupContent(Select2MultiChoice.class, "<select wicket:id=\"component\" class=\"form-control\"/>");
		registerMarkupContent(Select2Choice.class, "<select wicket:id=\"component\" class=\"form-control\"/>");
	}
	
	@Override
	public IMarkupFragment provideMarkup(Component component) {
		return provideMarkup(component.getClass());
	}

	@Override
	public IMarkupFragment provideMarkup(
			final Class<? extends Component> componentClass) {
		IMarkupFragment ret = markupsCache.get(componentClass);
		if(ret==null)
		{
			ret = markupsMap.get(componentClass);
			if(ret==null)
			{
				List<Class<? extends Component>> candidates = new ArrayList<Class<? extends Component>>();
				for (Map.Entry<Class<? extends Component>, IMarkupFragment> entry : markupsMap.entrySet())
				{
					if(entry.getKey().isAssignableFrom(componentClass)) candidates.add(entry.getKey());
				}
				if(candidates.size()==1)
				{
					ret = markupsMap.get(candidates.get(0));
				}
				else if(candidates.size()>1)
				{
					Class<? extends Component> minParents = Ordering.<Integer>natural().onResultOf(new Function<Class<? extends Component>, Integer>() {

						@Override
						public Integer apply(Class<? extends Component> input) {
							int ret = 0;
							Class<?> thisClass = componentClass;
							while(thisClass!=null && !input.equals(thisClass))
							{
								thisClass = thisClass.getSuperclass();
								ret++;
							}
							return ret;
						}
					}).min(candidates);
					ret = markupsMap.get(minParents);
				}
			}
			if(ret==null) ret = Markup.NO_MARKUP;
			markupsCache.put(componentClass, ret);
		}
		return ret!=Markup.NO_MARKUP?ret:null;
	}

	@Override
	public void registerMarkupContent(
			Class<? extends Component> componentClass, String content) {
		registerMarkupContent(componentClass, Markup.of(content));
	}

	@Override
	public void registerMarkupContent(
			Class<? extends Component> componentClass, IMarkupFragment markup) {
		markupsMap.put(componentClass, markup);
		markupsCache.clear();
	}


}