package io.onedev.server.web.editable.servicelocator;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.convert.ConversionException;

import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.buildspec.BuildSpecAware;
import io.onedev.server.buildspec.job.Job;
import io.onedev.server.buildspec.job.JobAware;
import io.onedev.server.model.support.administration.jobexecutor.ServiceLocator;
import io.onedev.server.web.behavior.sortable.SortBehavior;
import io.onedev.server.web.behavior.sortable.SortPosition;
import io.onedev.server.web.component.modal.ModalLink;
import io.onedev.server.web.component.modal.ModalPanel;
import io.onedev.server.web.editable.EmptyValueLabel;
import io.onedev.server.web.editable.PropertyDescriptor;
import io.onedev.server.web.editable.PropertyEditor;
import io.onedev.server.web.editable.PropertyUpdating;

@SuppressWarnings("serial")
class LocatorListEditPanel extends PropertyEditor<List<Serializable>> {

	private final List<ServiceLocator> locators;
	
	public LocatorListEditPanel(String id, PropertyDescriptor propertyDescriptor, IModel<List<Serializable>> model) {
		super(id, propertyDescriptor, model);
		
		locators = new ArrayList<>();
		for (Serializable each: model.getObject()) {
			locators.add((ServiceLocator) each);
		}
	}
	
	private BuildSpec getBuildSpec() {
		BuildSpecAware buildSpecAware = findParent(BuildSpecAware.class);
		if (buildSpecAware != null)
			return buildSpecAware.getBuildSpec();
		else
			return null;
	}

	private Job getJob() {
		JobAware jobAware = findParent(JobAware.class);
		if (jobAware != null)
			return jobAware.getJob();
		else
			return null;
	}
	
	@Override
	protected void onInitialize() {
		super.onInitialize();
		add(new ModalLink("addNew") {

			@Override
			protected Component newContent(String id, ModalPanel modal) {
				return new LocatorEditPanel(id, locators, -1) {

					@Override
					protected void onCancel(AjaxRequestTarget target) {
						modal.close();
					}

					@Override
					protected void onSave(AjaxRequestTarget target) {
						markFormDirty(target);
						modal.close();
						onPropertyUpdating(target);
						target.add(LocatorListEditPanel.this);
					}

					@Override
					public BuildSpec getBuildSpec() {
						return LocatorListEditPanel.this.getBuildSpec();
					}

					@Override
					public Job getJob() {
						return LocatorListEditPanel.this.getJob();
					}

				};
			}
			
		});
		
		List<IColumn<ServiceLocator, Void>> columns = new ArrayList<>();
		
		columns.add(new AbstractColumn<ServiceLocator, Void>(Model.of("")) {

			@Override
			public void populateItem(Item<ICellPopulator<ServiceLocator>> cellItem, String componentId, IModel<ServiceLocator> rowModel) {
				cellItem.add(new Label(componentId, "<span class=\"drag-indicator fa fa-reorder\"></span>").setEscapeModelStrings(false));
			}
			
			@Override
			public String getCssClass() {
				return "minimum actions";
			}
			
		});		
		
		columns.add(new AbstractColumn<ServiceLocator, Void>(Model.of("Applicable Names")) {

			@Override
			public void populateItem(Item<ICellPopulator<ServiceLocator>> cellItem, String componentId, IModel<ServiceLocator> rowModel) {
				if (rowModel.getObject().getServiceNames() != null) {
					cellItem.add(new Label(componentId, rowModel.getObject().getServiceNames()));
				} else {
					try {
						cellItem.add(new EmptyValueLabel(componentId, ServiceLocator.class.getDeclaredMethod("getServiceNames")));
					} catch (NoSuchMethodException | SecurityException e) {
						throw new RuntimeException(e);
					}
				}
			}
		});		
		
		columns.add(new AbstractColumn<ServiceLocator, Void>(Model.of("Applicable Images")) {

			@Override
			public void populateItem(Item<ICellPopulator<ServiceLocator>> cellItem, String componentId, IModel<ServiceLocator> rowModel) {
				if (rowModel.getObject().getServiceImages() != null) {
					cellItem.add(new Label(componentId, rowModel.getObject().getServiceImages()));
				} else {
					try {
						cellItem.add(new EmptyValueLabel(componentId, ServiceLocator.class.getDeclaredMethod("getServiceImages")));
					} catch (NoSuchMethodException | SecurityException e) {
						throw new RuntimeException(e);
					}
				}
			}
		});	
		
		columns.add(new AbstractColumn<ServiceLocator, Void>(Model.of("#Node Selector Entries")) {

			@Override
			public void populateItem(Item<ICellPopulator<ServiceLocator>> cellItem, String componentId, IModel<ServiceLocator> rowModel) {
				cellItem.add(new Label(componentId, rowModel.getObject().getNodeSelector().size()));
			}
			
		});		
		
		columns.add(new AbstractColumn<ServiceLocator, Void>(Model.of("")) {

			@Override
			public void populateItem(Item<ICellPopulator<ServiceLocator>> cellItem, String componentId, IModel<ServiceLocator> rowModel) {
				Fragment fragment = new Fragment(componentId, "actionColumnFrag", LocatorListEditPanel.this);
				fragment.add(new ModalLink("edit") {

					@Override
					protected Component newContent(String id, ModalPanel modal) {
						return new LocatorEditPanel(id, locators, cellItem.findParent(Item.class).getIndex()) {

							@Override
							protected void onCancel(AjaxRequestTarget target) {
								modal.close();
							}

							@Override
							protected void onSave(AjaxRequestTarget target) {
								markFormDirty(target);
								modal.close();
								onPropertyUpdating(target);
								target.add(LocatorListEditPanel.this);
							}

							@Override
							public BuildSpec getBuildSpec() {
								return LocatorListEditPanel.this.getBuildSpec();
							}

							@Override
							public Job getJob() {
								return LocatorListEditPanel.this.getJob();
							}

						};
					}
					
				});
				
				fragment.add(new AjaxLink<Void>("delete") {

					@Override
					public void onClick(AjaxRequestTarget target) {
						markFormDirty(target);
						locators.remove(rowModel.getObject());
						onPropertyUpdating(target);
						target.add(LocatorListEditPanel.this);
					}
					
				});
				cellItem.add(fragment);
			}

			@Override
			public String getCssClass() {
				return "actions";
			}
			
		});		
		
		IDataProvider<ServiceLocator> dataProvider = new ListDataProvider<ServiceLocator>() {

			@Override
			protected List<ServiceLocator> getData() {
				return locators;			
			}

		};
		
		DataTable<ServiceLocator, Void> dataTable;
		add(dataTable = new DataTable<ServiceLocator, Void>("locators", columns, dataProvider, Integer.MAX_VALUE));
		dataTable.addTopToolbar(new HeadersToolbar<Void>(dataTable, null));
		dataTable.addBottomToolbar(new NoRecordsToolbar(dataTable));
		
		dataTable.add(new SortBehavior() {

			@Override
			protected void onSort(AjaxRequestTarget target, SortPosition from, SortPosition to) {
				int fromIndex = from.getItemIndex();
				int toIndex = to.getItemIndex();
				if (fromIndex < toIndex) {
					for (int i=0; i<toIndex-fromIndex; i++) 
						Collections.swap(locators, fromIndex+i, fromIndex+i+1);
				} else {
					for (int i=0; i<fromIndex-toIndex; i++) 
						Collections.swap(locators, fromIndex-i, fromIndex-i-1);
				}
				onPropertyUpdating(target);
				target.add(LocatorListEditPanel.this);
			}
			
		}.sortable("tbody"));
	}

	@Override
	public void onEvent(IEvent<?> event) {
		super.onEvent(event);
		
		if (event.getPayload() instanceof PropertyUpdating) {
			event.stop();
			onPropertyUpdating(((PropertyUpdating)event.getPayload()).getHandler());
		}		
	}

	@Override
	protected List<Serializable> convertInputToValue() throws ConversionException {
		List<Serializable> value = new ArrayList<>();
		for (ServiceLocator each: locators)
			value.add(each);
		return value;
	}

}