package com.k_int.ciim.ui.resources;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.media.jai.JAI;
import javax.media.jai.OpImage;
import javax.media.jai.RenderedOp;
import javax.ws.rs.Consumes;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tika.Tika;
import org.springframework.beans.factory.annotation.Value;

import com.k_int.ciim.core.util.MediaUtils;
import com.k_int.ciim.data.hdo.CIIMAttachmentHDO;
import com.k_int.ciim.ref.MediaRecordTypeEnum;
import com.k_int.ciim.ui.exceptions.NotAuthorisedException;
import com.k_int.ciim.ui.exceptions.ResourceCreationException;
import com.k_int.ciim.ui.json.AttachmentJSON;
import com.k_int.ciim.ui.kernel.AttachmentQueryCore;
import com.k_int.ciim.ui.kernel.AttachmentTransactionCore;
import com.k_int.ciim.ui.ref.RoleDefinitionEnum;
import com.sun.jersey.api.view.ImplicitProduces;
import com.sun.jersey.api.view.Viewable;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataParam;
import com.sun.media.jai.codec.SeekableStream;

@XmlRootElement
@ImplicitProduces("text/html;qs=5") //for FireFox?
@XmlAccessorType(XmlAccessType.FIELD)
@Path("/resources/attachments")
public class AttachmentResources extends AbstractResource 
{
	@XmlTransient
	private static Log log = LogFactory.getLog(AttachmentResources.class);
	
	@XmlTransient
	private AttachmentQueryCore aqc;
	@XmlTransient
	private AttachmentTransactionCore atc;
	
	@Value("${ciim.media.upload.dir}")
	private String upload_dir;

	/* App Context setters */
	public void setAttachmentQueryCore(AttachmentQueryCore aqc) { this.aqc = aqc;	}
	public void setAttachmentTransactionCore(AttachmentTransactionCore atc) { this.atc = atc;	}
	 
	public AttachmentResources(){;}
	
	@GET
	@Produces({MediaType.TEXT_HTML,MediaType.APPLICATION_XHTML_XML})
	public Viewable getHtml() 
	{
		return new Viewable("index",this);
	}
	
	@GET
	@Produces("application/json")
	public List<AttachmentJSON> getJson(@QueryParam("resource_identifier") String resource_identifier) 
	{	
		List<AttachmentJSON> retval = new ArrayList<AttachmentJSON>();
		
		if(resource_identifier != null && resource_identifier.trim().length() > 0)
		{
			retval = convertToJSON(aqc.getAllLinkedToResource(resource_identifier));
		}
		else
		{
			retval = convertToJSON(aqc.getAll());
		}
		
		return retval;
	}
	
	@POST
	@Consumes("multipart/form-data")
	@Produces({MediaType.TEXT_PLAIN})
	public String createAttachment( @FormDataParam("identifier") String identifier,
									@FormDataParam("type") String type,
									@FormDataParam("upload_file") InputStream fileStream,
									@FormDataParam("upload_file") FormDataContentDisposition fileData,
									@FormDataParam("upload_file") FormDataBodyPart newFileBodyPart) 
	{
		String retval = "File Successfully Uploaded.";
		
		log.error("identifier=" + identifier + "&type=" + type);
		/* check header in-case IE has fudged up the filename */
		String[] contentDispositionOpts = newFileBodyPart.getHeaders().getFirst("Content-Disposition").split(";");
		
		String file_name = null;
		
		for (String option : contentDispositionOpts)
		{
			if (option != null && option.trim().startsWith("filename")) 
			{
				String[] name = option.split("=");
 
				String fullName = name[1].trim().replaceAll("\"", "");
								
				if(fullName != null && fullName.indexOf("\\") != -1)
				{
					file_name = fullName.substring(fullName.lastIndexOf("\\") + 1);
				}
				else
				{
					file_name = fullName;
				}
			}
		}

		if(file_name == null) /* last resort */
		{
			file_name = fileData.getFileName();
		}
		
		if(this.meetsRoleRequirement(RoleDefinitionEnum.CONFIGURED.toString()))
		{
			if(fileData != null && fileStream != null)
			{	  	  
				if(upload_dir == null)
				{
					return "Upload Failed, upload directory cannot be found.";
				}
				
				String destination_directory = MediaUtils.sanitisePath(upload_dir);	  
				
				if(file_name != null && file_name.length() > 64)
				{
					return "Upload Failed, filename must not exceed 64 characters.";
				}

				if(destination_directory == null || destination_directory.trim().length() == 0)
				{
					return "Upload Failed, destination does not exist and cannot be created.";
				}
				 
				// add seperator if required...
				if(!destination_directory.endsWith(File.separator))
				{
					destination_directory += File.separatorChar;
				}			
				  
				File upload_file = new File(destination_directory + file_name);
			        
				if(upload_file.exists() == false)
				{
					File destination = new File(destination_directory);
					  
					if(destination.exists() == false)
					{
						destination.mkdirs();
					}
					  
					try
					{
						OutputStream out = new FileOutputStream(upload_file);	  
						byte buffer[] = new byte[1024];							  
						int length;		
						  
						while((length = fileStream.read(buffer))>0)
						{
							out.write(buffer,0,length);
						} 
						  
						out.close();
						fileStream.close();
					}
					catch (Exception ex)
					{
						upload_file.delete();
						log.error("Exception occurred: " + ex);
						retval = "Upload Failed.";  
					}
				}
				else
				{
					log.error("attachment already exists");
					return "Upload Failed, The attachment '" + file_name + "' already exists."; 
				}

				// By this point the file should exist  
				retval = this.createAttachment(upload_file, file_name, destination_directory, identifier, type);
			}
			else
			{
				return "Upload Failed, file stream was empty.";
			}
		}
		else
		{
			return "Upload Failed, " + (new NotAuthorisedException().getMessage());
		}  
		
		return retval;
	}
	
	private String createAttachment(File file, String file_name, String location, String identifier, String type)
	{
		String retval = "File Successfully Uploaded.";
		
		String mime_type = null;
		//identify mime type using apache tika
		try
		{
			Tika t = new Tika();	
			mime_type = t.detect(file);	
		}
		catch(IOException ioe)
		{
			log.error("Exception occurred during tika detection: " + ioe);
			file.delete();
			return "Upload Failed, unable to detect MIME type of upload.";	
		}
		
		String file_extension = file_name.substring(file_name.lastIndexOf(".") + 1);
		  
		MediaRecordTypeEnum media_type = MediaRecordTypeEnum.valueOf(type.toUpperCase());
						  
		/* If type is unknown then default to Document */
		if(media_type == null)
		{
			media_type = MediaRecordTypeEnum.DOCUMENT;
		}		  
		
		try
		{
			if(media_type == MediaRecordTypeEnum.IMAGE)
			{
				try
				{
					System.setProperty("com.sun.media.jai.disableMediaLib", "true"); 
					FileInputStream fis = new FileInputStream(file);
					  
					SeekableStream seekableImageStream = SeekableStream.wrapInputStream(fis, true);
					RenderedOp originalImage = JAI.create("stream", seekableImageStream);
					((OpImage) originalImage.getRendering()).setTileCache(null);
	
					if(atc.create(file_name, location, media_type, file_extension, new Long(originalImage.getHeight()), new Long(originalImage.getWidth()), identifier, mime_type) == false)
					{
						file.delete();
						return "Upload Failed, unable to detect MIME type of upload.";
					}
				}
				catch(IOException ioe)
				{
					log.error("Exception occurred: " + ioe);
					file.delete();
					return "Upload Failed, unable to finalise attachment creation.";
				}  
			}
			else
			{
				if(atc.create(file_name, location, media_type, file_extension, null, null, identifier, mime_type) == false)
				{
					file.delete();
					return "Upload Failed, unable to detect MIME type of upload.";
				}
			}
		}
		catch(ResourceCreationException rce)
		{
			file.delete();
			return "Upload failed, " + rce.getMessage();
		}
		
		return retval;
	}
		
	/*
	 * Converts a list of CIIMMediaDescriptionHDO objects to a list of AttachmentJSON objects
	 */
	private List<AttachmentJSON> convertToJSON(List<CIIMAttachmentHDO> attachments)
	{
		List<AttachmentJSON> retval = new ArrayList<AttachmentJSON>();
		
		if(attachments != null && attachments.size() > 0)
		{
			// Iterate through hdo's ...
			for(CIIMAttachmentHDO hdo : attachments)
			{
				// ...and convert them to JSON
				retval.add(new AttachmentJSON(hdo));
			}
		}
		
		return retval;
	}
}
