package com.k_int.ciim.ui.resources;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import com.k_int.ciim.core.update.UpdateService;
import com.k_int.ciim.data.hdo.ContextLinkHDO;
import com.k_int.ciim.data.hdo.SchemaHDO;
import com.k_int.ciim.ref.ProgressEnum;
import com.k_int.ciim.ref.UpdateOperationEnum;
import com.k_int.ciim.ui.exceptions.MissingResourceException;
import com.k_int.ciim.ui.exceptions.NotAuthorisedException;
import com.k_int.ciim.ui.exceptions.ResourceCreationException;
import com.k_int.ciim.ui.json.ContextLinkJSON;
import com.k_int.ciim.ui.kernel.ContextLinkQueryCore;
import com.k_int.ciim.ui.kernel.ContextLinkTransactionCore;
import com.k_int.ciim.ui.kernel.SchemaQueryCore;
import com.k_int.ciim.ui.ref.PermissionTypeEnum;
import com.k_int.ciim.ui.ref.RoleDefinitionEnum;
import com.sun.jersey.api.view.ImplicitProduces;
import com.sun.jersey.api.view.Viewable;

@XmlRootElement
@ImplicitProduces("text/html;qs=5") //for FireFox?
@XmlAccessorType(XmlAccessType.FIELD)
@Path("/resources/context_links/{id}")
public class ContextLinkResource extends AbstractResource
{	
	@XmlTransient
	private ContextLinkQueryCore clqc = null;
	@XmlTransient
	private ContextLinkTransactionCore cltc = null;
	@XmlTransient
	private SchemaQueryCore sqc = null;
	@XmlTransient
	private UpdateService update_service = null;
	
	private ContextLinkJSON resource = null;
	
	
	/* App Context setters */
	public void setContextLinkQueryCore(ContextLinkQueryCore clqc) {	this.clqc = clqc;	}
	public void setContextLinkTransactionCore(ContextLinkTransactionCore cltc) {	this.cltc = cltc;	}
	public void setSchemaQueryCore(SchemaQueryCore sqc) {	this.sqc = sqc;	}
	public void setUpdateService(UpdateService update_service) { this.update_service = update_service; }
	
	public ContextLinkResource(){;}
	
	@GET
	@Produces({MediaType.TEXT_HTML,MediaType.APPLICATION_XHTML_XML})
	public Viewable getHtml(@PathParam("id") Long id) 
	{
		resource = new ContextLinkJSON(clqc.get(id), null);
		
		return new Viewable("index",this);
	}
	
	@GET
	@Produces({MediaType.APPLICATION_JSON})
	public ContextLinkJSON getJSON(@PathParam("id") Long id,
								   @DefaultValue("false") @QueryParam("outline") Boolean outline)
	{
		
		ContextLinkHDO hdo = clqc.get(id);
		
		SchemaHDO schema_hdo = null;
		
		if(outline.booleanValue() == false)
		{
			schema_hdo = sqc.get(hdo.getContext().getId(), hdo.getSourceDataType());
			// ...and convert them to JSON
		}
		
		return new ContextLinkJSON(hdo, schema_hdo);
	}
	
	@POST 
	@Consumes("application/x-www-form-urlencoded")
	@Produces({MediaType.TEXT_PLAIN})
	public String augment(	@PathParam("id") Long id, 
							MultivaluedMap<String, String> form_params)
	{
		String retval = "Failed to augment resource.";
		
		//get context link first
		ContextLinkHDO hdo = clqc.get(id);
		
		if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString()) 
		&& this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.EDIT))
		{
			//if null we have no validation errors
			if(validate(form_params) == null)
			{
				try
				{
					//...so augment, returns true if successful
					if(cltc.augment(id, form_params, request.getUserPrincipal().getName()))
					{
						retval = "Successfully augmented resource.";
					}
				}
				catch(MissingResourceException rce)
				{
					throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(rce.getMessage()).type("text/plain").build());
				}
			}
			else
			{
				//return validation error message
				throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(validate(form_params)).type("text/plain").build());
			}
		}
		else
		{
			throw new NotAuthorisedException();
		}
		
		return retval;
	}
	
	@POST
	@Produces({MediaType.TEXT_PLAIN})
	public String actions(	@PathParam("id") Long id,
							@QueryParam("action") String action,
							@QueryParam("note") String note)
	{
		String retval = null;
				
		if(action != null && action.equalsIgnoreCase("publish"))
		{
			retval = publish(id);
		}
		if(action != null && action.equalsIgnoreCase("archive"))
		{
			retval = archive(id);
		}
		if(action != null && action.equalsIgnoreCase("reject"))
		{
			retval = reject(id, note);
		}
		
		return retval;
	}
	
	
	private String publish(Long id)
	{
		String retval = "Failed to publish context item";
		
		//get context link first
		ContextLinkHDO hdo = clqc.get(id);
		
		if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString()) 
		&& this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.PUBLISH))
		{
			boolean is_running = update_service.running();
			
			if(cltc.publish(id, request.getUserPrincipal().getName()))
			{
				if(is_running)
				{
					retval = "Queued for publication";
				}
				else
				{
					retval = "Sent for processing";
				}
			}
		}
		else
		{
			if(request.getUserPrincipal() != null 
			&& request.getUserPrincipal().getName() != null 
			&& cltc.updateOperationRequest(id, UpdateOperationEnum.PUBLISH, ProgressEnum.WAITING, null))
			{
				throw new NotAuthorisedException("A request for this augmentation data to be published has been raised.");
			}
			else
			{
				throw new NotAuthorisedException();
			}
		}

		return retval;
	}
	
	private String archive(Long id)
	{
		String retval = "Failed to archive data";
		
		//get context link first
		ContextLinkHDO hdo = clqc.get(id);
		
		if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString()) 
		&& this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.DELETE))
		{
			boolean is_running = update_service.running();
			
			if(cltc.archive(id, request.getUserPrincipal().getName()))
			{
				if(is_running)
				{
					retval = "Successfully queued for archiving";
				}
				else
				{
					retval = "Archive successful";
				}
			}
		}
		else
		{
			if(request.getUserPrincipal() != null 
			&& request.getUserPrincipal().getName() != null 
			&& cltc.updateOperationRequest(id, UpdateOperationEnum.ARCHIVE, ProgressEnum.WAITING, null))
			{
				throw new NotAuthorisedException("A request for this data to be archived has been raised.");
			}
			else
			{
				throw new NotAuthorisedException();
			}
		}

		return retval;
	}
	
	private String reject(Long id, String note)
	{
		String retval = "Failed to reject operation request";
		
		//get context link first
		ContextLinkHDO hdo = clqc.get(id);
		
		if(hdo != null && hdo.getMaintenanceData().getOpRequest() != null)
		{
			UpdateOperationEnum op_req =  hdo.getMaintenanceData().getOpRequest();
			retval = "Failed to reject " + op_req.toString() + " request.";
			
			if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString())
			&& ((op_req == UpdateOperationEnum.PUBLISH && this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.PUBLISH))
			|| (op_req == UpdateOperationEnum.ARCHIVE && this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.DELETE))))
			{
				if(cltc.updateOperationRequest(id, null, ProgressEnum.DECLINED, note))
				{
					retval = "Successfully rejected " + op_req.toString() + " request.";
				}
			}
			else
			{
				throw new NotAuthorisedException();
			}
		}

		return retval;
	}
	
	private String validate(MultivaluedMap<String, String> form_params)
	{
		String retval = null;
		
		return retval;
	}
	
	public ContextLinkJSON getResource() 
	{
		return resource;
	}
	
	public void setResource(ContextLinkJSON resource)
	{
		this.resource = resource;
	}
	
	@POST
	@Path("/ref_links")
	@Produces({MediaType.TEXT_PLAIN})
	public String addRefLink(	@PathParam("id") Long id,
								@QueryParam("identifier") String identifier,
								@QueryParam("text") String text,
								@QueryParam("type") String type)
	{
		String retval = "Failed to add Reference Link";
		
		ContextLinkHDO hdo = clqc.get(id);
		
		if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString()) 
		&& this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.EDIT))
		{
			try
			{
				if(cltc.addRefLink(id, type, identifier, text, request.getUserPrincipal().getName()))
				{
					retval = "Successfully linked resource";
				}
			}
			catch(MissingResourceException mre)
			{
				throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(mre.getMessage()).type("text/plain").build());
			}
			catch(ResourceCreationException rce)
			{
				throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(rce.getMessage()).type("text/plain").build());
			}
		}
		else
		{
			throw new NotAuthorisedException();
		}
		
		return retval;
	}
	
	@POST
	@Path("/ref_links/{ref_id}")
	@Produces({MediaType.TEXT_PLAIN})
	public String addRefLink(	@PathParam("id") Long id,
								@PathParam("ref_id") Long ref_id,
								@QueryParam("action") String action,
								@QueryParam("position") Integer position)
	{
		String retval = "Failed to remove Reference Link";
		
		ContextLinkHDO hdo = clqc.get(id);
		
		if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString()) 
		&& this.hasPermissions(hdo.getContext().getId(), PermissionTypeEnum.EDIT))
		{
			try
			{
				if(action.equalsIgnoreCase("delete"))
				{
					if(cltc.removeRefLink(id, ref_id, request.getUserPrincipal().getName()))
					{
						retval = "Successfully removed linked resource";
					}
				}
				else if(action.equalsIgnoreCase("reorder"))
				{
					if(cltc.reorderRefLink(id, ref_id, position, request.getUserPrincipal().getName()))
					{
						retval = "Successfully removed linked resource";
					}
				}
					
			}
			catch(MissingResourceException mre)
			{
				throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(mre.getMessage()).type("text/plain").build());
			}
		}
		else
		{
			throw new NotAuthorisedException();
		}
		
		return retval;
	}
	
}
