package com.k_int.ciim;

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.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.media.jai.JAI;
import javax.media.jai.OpImage;
import javax.media.jai.RenderedOp;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
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.Context;
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.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.k_int.aggr2.mimsy.data.hdo.CIIMImgResizeFormatHDO;
import com.k_int.aggr2.mimsy.data.hdo.CIIMMediaDescriptionHDO;
import com.k_int.ciim.json.CIIMGroupAttachmentJSON;
import com.k_int.ciim.kernel.CIIMDataManager;
import com.k_int.ciim.ref.Constants;
import com.k_int.mimsy.ref.MediaRecordTypeEnum;
import com.sun.jersey.api.view.ImplicitProduces;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.media.jai.codec.SeekableStream;

/**
 * REST Web Service
 *
 * @author Mark Johnson
 * curl -s -H 'Accept: application/json' http://localhost:8080/Template/CIIM/Attachments
 * ImplicitProduces("text/html;qs=5")   
 */

@XmlRootElement
@ImplicitProduces("application/json;qs=5")
@XmlAccessorType(XmlAccessType.FIELD)
@Path("/CIIM/Attachments")
@Component
public class Attachments implements ApplicationContextAware 
{
	 @XmlTransient
	 private ApplicationContext ctx;
	 
	 @XmlTransient
	 @Context HttpServletRequest request;
	 
	 @XmlTransient
	 private static Log log = LogFactory.getLog(Attachments.class);

	 public void setApplicationContext(ApplicationContext ctx) 
	 {
	    this.ctx = ctx;
	 }
	   
	 @javax.annotation.PreDestroy
	 public void destroy() 
	 {
	 }
	   
	 /** Creates a new instance */
	 public Attachments() 
	 {
		 //log.debug("*** new Objects class contructor");
	 }

	 @javax.annotation.PostConstruct
	 public void init() 
	 {
		 //log.debug("*** new Objects class init");
	 }	 
	 
	 @Path("/")
	 @GET
	 @Produces({MediaType.APPLICATION_JSON})
	 public List<CIIMGroupAttachmentJSON> getJSON(	@DefaultValue("*") @QueryParam("operator") String operator,
			 										@DefaultValue("") @QueryParam("gid") String gid)
	 {
		 List<CIIMGroupAttachmentJSON> retval = new ArrayList<CIIMGroupAttachmentJSON>();
		 
		 CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		 
		 if(operator != null && operator.equalsIgnoreCase("*") && (gid == null || gid.trim().length() == 0))
		 {
			 retval = cdm.getAllAttachments(); 
		 }
		 else if(operator != null && operator.equalsIgnoreCase("not") && gid != null && gid.length() > 0)
		 {
			 retval = cdm.getUnassignedAttachments(gid);
		 }
		 else
		 {
			 retval = cdm.getAttachments(gid);
		 }
		 
		 return retval;
	 }
	 
	 @Path("/CreateMediaRecord")
	 @POST
	 @Produces({MediaType.TEXT_PLAIN})
	 public String createMediaRecord(	@FormParam("attachment_id") Long attachment_id,
			  							@FormParam("caption") String caption,
										@FormParam("credit_line") String credit_line, 
										@FormParam("type") String type, 
										@FormParam("from_date") String from_date, 
										@FormParam("to_date") String to_date,
										@FormParam("holder") String holder, 
										@FormParam("details") String details,
										@FormParam("image_format") String format)
	 {
		 String retval = null;
		 
		 CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		  		  
		 CIIMMediaDescriptionHDO lAttachment = cdm.getAttachment(attachment_id);
		  
		 if(lAttachment != null && lAttachment.getLocation() != null && lAttachment.getName() != null)
		 {	  
			 HttpClient client = new HttpClient();
			 
			 //PostMethod method = new PostMethod("http://192.168.1.25:8080/restapi/media?action=generate"); - RB MACHINE
			 PostMethod method = new PostMethod("http://localhost:8080/restapi/media?action=generate");
			 method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,  new DefaultHttpMethodRetryHandler(0, false));
			  
			 try
			 {
				 if(lAttachment.getMediaType().toString() != null && lAttachment.getMediaType().toString().equalsIgnoreCase("image"))
				 {
					 CIIMImgResizeFormatHDO hdo = cdm.getImgResizeFormat(format);
					 
					 Part[] parts = 
					 { 
						new StringPart("originalPath", lAttachment.getLocation() + lAttachment.getName(), "utf-8"),
						new StringPart("type", lAttachment.getMediaType().toString(), "utf-8"),
						new StringPart("scaleDimension", hdo.getResize_dimension().toString(), "utf-8"),
						new StringPart("smallDim", hdo.getSmall_size().toString() , "utf-8"),
						new StringPart("midDim", hdo.getMedium_size().toString() , "utf-8"),
						new StringPart("largeDim", hdo.getLarge_size().toString() , "utf-8"),					
					 }; 
					 
					 method.setRequestEntity(new MultipartRequestEntity(parts,  method.getParams()));
				 }
				 else
				 {
					 Part[] parts = 
					 { 
						new StringPart("originalPath", lAttachment.getLocation() + lAttachment.getName(), "utf-8"),
						new StringPart("type", lAttachment.getMediaType().toString(), "utf-8"),				
					 }; 
					 
					 method.setRequestEntity(new MultipartRequestEntity(parts,  method.getParams()));
				 }

				 int statusCode = client.executeMethod(method);
				  
				 if(statusCode >= HttpStatus.SC_OK && statusCode <= HttpStatus.SC_ACCEPTED)
			     {
					 String response = method.getResponseBodyAsString();
					  
					 HashMap<String,String> lNameValuePairs = parseJSONtoHashMap(response);
					 
					 if(lNameValuePairs.get("successful").equalsIgnoreCase("true"))
					 {  						         
						 /* Get Media Directory from properties file */
						 String base_dir = this.getMediaLocation();
						  
						 if(base_dir == null)
						 {
							 throw new Exception("No Media directory found so cannot create media record");
						 }
						  
						 String small_path_abs = lNameValuePairs.get("smallPathAbs").replace("\\\\", "\\");
						 String small_path_rel = lNameValuePairs.get("smallPathRelative");
						 String medium_path_abs = lNameValuePairs.get("mediumPathAbs").replace("\\\\", "\\");;
						 String medium_path_rel = lNameValuePairs.get("mediumPathRelative");
						 String large_path_abs = lNameValuePairs.get("largePathAbs").replace("\\\\", "\\");;
						 String large_path_rel = lNameValuePairs.get("largePathRelative");
						 String donor_path_abs = lNameValuePairs.get("originalImagePathAbs").replace("\\\\", "\\");;
						 String donor_path_rel = lNameValuePairs.get("originalImagePathRelative");
					  
						 if(cdm.createMediaRecord(attachment_id, caption, credit_line, type, from_date, to_date, holder, details, small_path_abs, small_path_rel, medium_path_abs, medium_path_rel, large_path_abs, large_path_rel, donor_path_abs, donor_path_rel, format, request))
						 {
							 retval = "success";
						 }
					 }	 
					  
					 System.out.println("successful : " + lNameValuePairs.get("successful"));
					 System.out.println("messages : " + lNameValuePairs.get("message"));
			     }
				 else
				 {
					 System.err.println("Image processing failed");                                
			    	 System.err.println("Got HTTP status code " + statusCode);          
				 }
			 }
			 catch(IOException ioe)
			 {
				 System.err.println("IOException : " + ioe);  
			 }
			 catch(Exception e)
			 {
				 System.err.println("Exception : " + e);  
			 }
			 finally
			 {
				 method.releaseConnection();
			 }
		 }
		 
		 return retval;
	 }
	 
	 @Path("/Upload")
	 @POST
	 @Consumes("multipart/form-data")
	 @Produces({MediaType.TEXT_PLAIN})
	 public String upload(	@FormParam("uploadfile") InputStream fileStream,
							@FormParam("uploadfile") FormDataContentDisposition fileData)
	 {
		 String retval = "Upload Failed";
		 		 
		 if(request != null && (request.isUserInRole(Constants.CIIM_EDITOR_ROLE) || request.isUserInRole(Constants.CIIM_ADMIN_ROLE)))
		 {
			 if(fileData != null && fileData.getFileName() != null && fileData.getFileName().length() > 0 && fileStream != null)
			 {	  
				  File op_file = null;  	  
				  String dir = null;	  
				  String file_name = fileData.getFileName().replace(":", "_");
				         
				  try
				  {
					  /* Get Media Directory from properties file */
					  dir = this.getMediaLocation();
					  
					  if(dir == null)
					  {
						  retval = "Upload Failed. No location found to save file!";
						  throw new Exception("No location found to save file!");
					  }
					  
					  dir += "\\";
					  
					  op_file = new File(dir + file_name);
				        
					  if(op_file.exists() == false)
					  {
						  File f = new File(dir);
						  f.mkdirs();
						  
						  if(f.exists())
						  { 
							  OutputStream out = new FileOutputStream(op_file);	  
							  byte buffer[] = new byte[1024];
							  
							  int length;		
							  
							  while((length = fileStream.read(buffer))>0)
							  {
								  out.write(buffer,0,length);
								  
							  } 
							  
							  out.close();
							  fileStream.close();
						  }
						  else
						  {
							  retval = "Upload Failed. Unable to create file " + dir + file_name + ".";
							  throw new Exception("failed making directory" + dir);
						  }
					  }
				  }
				  catch (IOException e)
				  {
					  System.out.println("IOException occurred  :" + e);
					  op_file.delete();
				  }
				  catch (Exception ex)
				  {
					  System.out.println("Exception occurred  :" + ex);
					  op_file.delete();
				  }
				  finally
				  {
					  //if file successfully created
					  if(op_file != null && op_file.exists())
					  {
						  retval = this.processFile(op_file, file_name, dir);
					  }  
				  }
			  }
		  }
		  else
		  {
			  retval = "You do not have the correct user permissions to upload attachments.";
		  }
		  
		 
		 return retval;
	 }
	 
	 private String processFile(File file, String file_name, String dir)
	 {
		 String retval = "Upload Failed";
		 
		 CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		 
		 String lFileExtension = file_name.substring(file_name.indexOf("."));
		  
		 MediaRecordTypeEnum lType = Constants.EXTENSION_MAPPINGS.get(lFileExtension);
		  
		 if(lType == null)
		 {
			 lType = MediaRecordTypeEnum.DOCUMENT;
		 }		  
		 if(lType == 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(cdm.createCIIMMediaDescription(file_name, dir, Constants.EXTENSION_MAPPINGS.get(lFileExtension), request, new Long(originalImage.getHeight()), new Long(originalImage.getWidth())))
				 {
					 retval = file_name + " Successfully Uploaded";
				 }
			 }
			 catch(IOException ioe)
			 {
				 file.delete();
			 }  
		 }
		 else
		 {
			 if(cdm.createCIIMMediaDescription(file_name, dir, lType, request, null, null))
			 {
				 retval = file_name + " Successfully Uploaded";
			 }
		 }
		 
		 return retval;
	 }
	 
	 @Path("/Delete")
	 @POST
	 @Produces({MediaType.TEXT_PLAIN})
	 public String deleteAttachment( @FormParam("id") Long id )
	 {
		 String retval = "Failed to delete attachment.";
		 
		 if(id != null)
		 {
			 CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		  
			 if(cdm.deleteAttachment(id, request))
			 {
				 retval = "Successfully deleted attachment.";
			 }
			 else
			 {
				 retval += " The attachment must first be removed from all groups.";
			 }
		 }
		 
		 return retval;
	 } 
	 
	 @Path("/Remove")
	 @POST
	 public void removeAttachment( 	@FormParam("id") Long id,
			  						@FormParam("gid") String gid )
	 {
		 if(id != null && gid != null)
		 {
			 CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		  
			 cdm.removeAttachment(gid, id, request);
		 }
	 }
	 
	 @Path("/Add")
	 @POST
	 public void addAttachment( @FormParam("id") Long id,
		  						@FormParam("gid") String gid )
	 {
		 if(id != null && gid != null)
		 {
			 CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		  
			 cdm.addAttachment(id, gid, request);
		 }
	 }
	 
	 private final String getMediaLocation()
	 {
		 String retval = null;	 
		 ResourceBundle bundle = null;
		 
		 try
		 {
			 bundle = ResourceBundle.getBundle("ciim", Locale.getDefault()); 
			 
			 /* Get Media Directory from properties file */
			 retval = bundle.getString("ciim_media_location");
		 } 
		 catch (MissingResourceException mre) 
		 {
			 log.error(mre);
		 }
		 
		 return retval;     			  
	 }
	 
	 //TODO : change this method to parse JSON into hashmap using ObjectMapper
	 private HashMap<String,String> parseJSONtoHashMap(String jsonString)
	 {
		 HashMap<String,String> lReturn = null;
		  
		 if(jsonString != null)
		 {
			 lReturn = new HashMap<String,String>();
			  
			 jsonString = jsonString.replace("{\"", "");
			 jsonString = jsonString.replace("\"}", "");
			  
			 //Parse the JSON into a HashMap
			 String[] splitResponse = jsonString.split("\",\"");
			  
			 for(String lResponsePair : splitResponse )
			 {
				 if(lResponsePair != null)
				 {
					 String[] lValues = lResponsePair.split("\":\"");
					  
					 if(lValues != null && lValues.length == 2)
					 {
						 lReturn.put(lValues[0], lValues[1]);
					 }
				 }
			 }
		 }
		  
		 return lReturn;
		
	  }
}
