package info.bukova.isspst.ui.requirement;

import info.bukova.isspst.Constants;
import info.bukova.isspst.data.RequirementType;
import info.bukova.isspst.data.Role;
import info.bukova.isspst.data.Workflow;
import info.bukova.isspst.services.requirement.RequirementTypeService;
import info.bukova.isspst.services.users.RoleService;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.DropEvent;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Window;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RequirementTypesVM {
	
	@WireVariable
	private RequirementTypeService reqTypeService;
	@WireVariable
	private RoleService roleService;
	private List<RequirementType> reqTypes;
	private List<Role> centreRoles;
	private List<Role> workgroupRoles;
	private RequirementType selected;
	private Workflow wgSelWorkflow;
	private Workflow centreSelWorkflow;
	
	@Init
	public void init() {
		reqTypes = reqTypeService.getAll();
		initRoles();
	}
	
	private void initRoles() {
		String moduleId;
		
		if (selected != null) {
			if (selected.getType().equals(Constants.REQTYPE_BUSINESSTRIP)) {
				moduleId = Constants.MOD_TRIPREQUIREMENTS;
			} else {
				moduleId = Constants.MOD_REQUIREMENTS;
			}
			
			centreRoles = new ArrayList<Role>(roleService.getRolesWithPermission(Constants.PERM_APPROVE, moduleId, true));
			workgroupRoles = new ArrayList<Role>(roleService.getRolesWithPermission(Constants.PERM_APPROVE, moduleId, false));
		} else {
			centreRoles = null;
			workgroupRoles = null;
		}
	}
	
	@Command
	@NotifyChange({"selected", "centreRoles", "workgroupRoles"})
	public void addRoleWg(@BindingParam("event") DropEvent event) {
		Role r;
		try {
			r = ((Listitem)event.getDragged()).getValue();
		} catch (ClassCastException e) {
			return;
		}
		addRole(r, false);
	}
	
	@Command
	@NotifyChange({"selected", "centreRoles", "workgroupRoles"})
	public void addRoleCentre(@BindingParam("event") DropEvent event) {
		Role r;
		try {
			r = ((Listitem)event.getDragged()).getValue();
		} catch (ClassCastException e) {
			return;
		}
		addRole(r, true);
	}
	
	private void addRole(Role r, boolean centre) {
		Workflow w = new Workflow();
		w.setRole(r);
		w.setCentre(centre);
		w.setOrder(selected.getWorkflow().size());
		
		if (!selected.getWorkflow().contains(w)) {
			selected.getWorkflow().add(w);
		}
		
		if (centre) {
			centreRoles.remove(r);
		} else {
			workgroupRoles.remove(r);
		}
		
		reqTypeService.update(selected);
	}
	
	@Command
	@NotifyChange({"selected", "centreRoles", "workgroupRoles"})
	public void removeRoleWg(@BindingParam("event") DropEvent event) {
		removeWorkflow((Workflow) ((Listitem)event.getDragged()).getValue());	
	}
	
	@Command
	@NotifyChange({"selected", "centreRoles", "workgroupRoles"})
	public void removeRoleCentre(@BindingParam("event") DropEvent event) {
		removeWorkflow((Workflow) ((Listitem)event.getDragged()).getValue());
	}
	
	private void removeWorkflow(Workflow w) {
		selected.getWorkflow().remove(w);
		
		if (w.getCentre()) {
			centreRoles.add(w.getRole());
		} else {
			workgroupRoles.add(w.getRole());
		}
		
		resetOrder();
		
		reqTypeService.update(selected);
	}
	
	@Command
	@NotifyChange({"selected", "centreRoles", "workgroupRoles"})
	public void reorderWg(@BindingParam("event") DropEvent event) {
		reorder(event, false);
	}

	@Command
	@NotifyChange({"selected", "centreRoles", "workgroupRoles"})
	public void reorderCentre(@BindingParam("event") DropEvent event) {
		reorder(event, true);
	}
	
	private void reorder(DropEvent event, boolean centre) {
		Workflow w;
		
		try {
			w = ((Listitem)event.getDragged()).getValue();
		} catch (ClassCastException e) {
			if (centre) {
				addRoleCentre(event);
			} else {
				addRoleWg(event);
			}
			return;
		}
		
		Workflow target = ((Listitem)event.getTarget()).getValue();
		List<Workflow> wf = selected.getWorkflow();
		int dragIndex = wf.indexOf(w);
		int dropIndex = wf.indexOf(target);
		
		if (dragIndex > dropIndex) {
			wf.remove(w);
			wf.add(dropIndex, w);
		} else {
			for (int i = dragIndex; i < dropIndex; i++) {
				wf.add(i, wf.get(i + 1));
				wf.remove(i + 1);
			}

			wf.remove(w);
			wf.remove(dropIndex);
			wf.add(dropIndex, w);
		}
		
		w.setOrder(wf.indexOf(w));
		resetOrder();
		
		reqTypeService.update(selected);
	}
	
	private void resetOrder() {
		for (int i = 0; i < selected.getWorkflow().size(); i++) {
			selected.getWorkflow().get(i).setOrder(i);
		}
	}
	
	public List<RequirementType> getReqTypes() {
		return reqTypes;
	}

	public List<Role> getCentreRoles() {
		return centreRoles;
	}

	public List<Role> getWorkgroupRoles() {
		return workgroupRoles;
	}

	public RequirementType getSelected() {
		return selected;
	}

	@NotifyChange({"selected", "workgroupRoles", "centreRoles"})
	public void setSelected(RequirementType selected) {
		if (selected == null) {
			return;
		}
		
		this.selected = selected;
		
		initRoles();
		
		for (Workflow w : selected.getWorkflow()) {
			if (w.getCentre()) {
				centreRoles.remove(w.getRole());
			} else {
				workgroupRoles.remove(w.getRole());
			}
		}
	}
	
	@Command
	@NotifyChange({"selected", "centreSelWorkflow", "wgSelWorkflow"})
	public void overLimit(@BindingParam("workflow") Workflow workflow) {
		if (workflow.getLimit() != null && !workflow.getLimit().equals(BigDecimal.ZERO)) {
			workflow.setLimit(null);
			reqTypeService.update(selected);
			return;
		}
		
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("workflow", workflow);
		Window win = (Window) Executions.createComponents("/settings/workflow/limit.zul", null, params);
		win.doModal();
	}

	@Command
	@NotifyChange({"selected", "centreSelWorkflow", "wgSelWorkflow"})
	public void toggleSignature(@BindingParam("workflow") Workflow workflow) {
		if (workflow.getSignature() != null && workflow.getSignature()) {
			workflow.setSignature(false);
		} else {
			workflow.setSignature(true);
		}

		reqTypeService.update(selected);
	}
	
	@GlobalCommand
	@NotifyChange("selected")
	public void refresh() {
		reqTypeService.update(selected);
	}
	
	@Command
	public void centresDialog() {
		Map<String, RequirementType> param = new HashMap<String, RequirementType>();
		param.put("type", selected);
		Window win = (Window) Executions.createComponents("/settings/workflow/offeredCentres.zul", null, param);
		win.doModal();
	}
	
	@Command
	@NotifyChange("selected")
	public void clearCentres() {
		if (!selected.getLimitCentres()) {
			selected.getOfferedCentres().clear();
			refresh();
		}
	}

	public Workflow getWgSelWorkflow() {
		return wgSelWorkflow;
	}

	public void setWgSelWorkflow(Workflow wgSelWorkflow) {
		this.wgSelWorkflow = wgSelWorkflow;
	}

	public Workflow getCentreSelWorkflow() {
		return centreSelWorkflow;
	}

	public void setCentreSelWorkflow(Workflow centreSelWorkflow) {
		this.centreSelWorkflow = centreSelWorkflow;
	}

}
