package com.k_int.aggregator.dpp.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;

import eu.medsea.mimeutil.MimeType;
import eu.medsea.mimeutil.MimeUtil;

import com.k_int.aggregator.core.AggregatorService;
import com.k_int.aggregator.datamodel.AggregatorCollectionHDO;
import com.k_int.aggregator.datamodel.AggregatorResourceHDO;
import com.k_int.aggregator.datamodel.AggregatorSourceHDO;
import com.k_int.discover.datamodel.AttachedResourceHDO;
import com.k_int.discover.datamodel.AttachedResourceTypeHDO;
import com.k_int.discover.datamodel.CultureGrid_BaseHDO;
import com.k_int.discover.datamodel.CultureGrid_CollectionHDO;
import com.k_int.discover.datamodel.CultureGrid_InstitutionHDO;
import com.k_int.discover.datamodel.CultureGrid_ItemHDO;
import com.k_int.discover.datamodel.dto.CultureGrid_BaseDTO;
import com.k_int.discover.datamodel.dto.CultureGrid_CollectionDTO;
import com.k_int.discover.datamodel.dto.CultureGrid_InstitutionDTO;
import com.k_int.discover.datamodel.dto.CultureGrid_ItemDTO;
import com.k_int.discover.datamodel.ref.LinkType;
import com.k_int.discover.util.CollectionParser;
import com.k_int.peoples_record.repository.Repository;
import com.k_int.peoples_record.repository.StoreResult;

public class DepositUtils {
    
	public static Log log = LogFactory.getLog(DepositUtils.class);
	
	public static List<Long> depositSetOfRecords(AggregatorService aggregator, Repository repository, Session sess, AggregatorCollectionHDO collection, AggregatorSourceHDO source, String user_id, LinkedHashMap<String,CultureGrid_BaseDTO> recordMap, String pathToAttachments, String repositoryPublicPath) {
		List<Long> returnValue = new ArrayList<Long>();
		
		if ( recordMap != null ) {
			// Iterate through the passed map of records and import them one by one
			Set<String> keyset = recordMap.keySet();
			Iterator<String> keysetIter = keyset.iterator();
			
			while(keysetIter.hasNext()) {
				String key = keysetIter.next();

				CultureGrid_BaseDTO record = recordMap.get(key);
				
				if ( record != null ) {
					
					log.debug("About to deposit a record with datamodel identifier: " + record.getIdentifier());
					
					// We have a record - need to deposit it into the system
					Long id = depositRecord(aggregator, repository, sess, collection, source, user_id, record, pathToAttachments, repositoryPublicPath);
					if ( id != null && !id.equals(0l) ) {
						// We have a resource ID - remember it
						returnValue.add(id);
					} else {
						log.error("No resource ID returned from depositing record with identifier " + record.getIdentifier() + " stopping processing");
						break;
					}
					
				}
				
			}
		}
		
		return returnValue;
	}
	
	public static Long depositRecord(AggregatorService aggregator, Repository repository, Session sess, AggregatorCollectionHDO collection, AggregatorSourceHDO source, String user_id, CultureGrid_BaseDTO record, String pathToAttachments, String repositoryPublicPath) {
		
		// Actually store the document in the aggregator
		log.debug("About to store in the aggregator...");

		// Register the mime-type detector
		MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");

        // Check the specified repository public path ends in a / and add one if not
        if ( !repositoryPublicPath.endsWith("/") ) {
            repositoryPublicPath = repositoryPublicPath + "/";
        }
		
		Long resourceId = 0l;
		boolean okToCreateAndSetup = true;
        boolean shouldWorkoutCollections = true;
		
		// First check there isn't already a record with the same reference for this source
		AggregatorResourceHDO existingRes = AggregatorResourceHDO.lookup(sess, source, record.getIdentifier());
		if ( existingRes != null ) {
			// Existing resource already!
			log.debug("Existing res! id: " + existingRes.getId());

			// Only determine the collections if we do not already have collections associated with this resource
			Set<AggregatorCollectionHDO> collectionHDOs = existingRes.getCollections();
			if ((collectionHDOs != null) && !collectionHDOs.isEmpty()) {
				shouldWorkoutCollections = false;
			}
                        
			resourceId = existingRes.getId();
			
			// Go and get the related datamodel object and find out whether or not it has been edited in the system
			CultureGrid_BaseHDO baseDMObj = CultureGrid_BaseHDO.lookupByResourceId(sess, existingRes.getId());
			if ( baseDMObj == null || !baseDMObj.getEdited() ) {
				// Object doesn't exist in the datamodel yet, or hasn't been edited - OK to import this new version
				log.debug("Existing resource found but it isn't in the datamodel, or hasn't been edited if it is - therefore ok to continue and import new data");
			} else {
				// Object has been edited in the system - can't overwrite
				resourceId = existingRes.getId();
				okToCreateAndSetup = false;
				log.debug("Existing resource that has been edited - can't overwrite it! Existing res ID = " + resourceId);
			}
		}
		
                
		if (okToCreateAndSetup) {
			// No existing resource (or only partial or unedited) - need to create one
			Transaction tx = null;
			
			tx = sess.beginTransaction();
				
			AggregatorResourceHDO newRes = AggregatorResourceHDO.lookupOrCreate(sess, source, record.getIdentifier(), false, user_id);
			if ( newRes != null ) {
				// Resource created - remember the information about the created resource
				resourceId = newRes.getId();
				log.debug("Setting the datamodel object's resource Id to " + resourceId);
				record.setResourceId(resourceId);
				
				// Add the collections to the resource (only if a new resource - if an existing resource then
                // just leave the collection information as it is
                if ( shouldWorkoutCollections ) {

                    Set<AggregatorCollectionHDO> collectionList = CollectionParser.getCollCodesAsHDO(sess, collection.getCollectionCode(), record);

//                  Set<AggregatorCollectionHDO> collsForRec = new HashSet<AggregatorCollectionHDO>();
//                  collsForRec.add(collection);
                    newRes.setCollections(collectionList);
                    sess.update(newRes);

                }
                                
                                
				// Now add the rest of the information into the datamodel
				CultureGrid_BaseHDO datamodelHDO = null;
                String recordType = "UNKNOWN";

                log.debug("About to convert the record into an HDO");
				if ( record instanceof CultureGrid_ItemDTO ) {
                    recordType = "ITEM";
                    log.debug("Creating the item datamodel object");
                    CultureGrid_ItemHDO itemHDO = CultureGrid_ItemHDO.lookupOrCreate(sess, newRes.getId(), "CultureGrid_Item");
                    CultureGrid_ItemDTO itemDTO = (CultureGrid_ItemDTO)record;
                    itemHDO = itemDTO.convertDTOToHDO(sess, itemHDO);
                    datamodelHDO = itemHDO;
				} else if ( record instanceof CultureGrid_CollectionDTO ) {
                    recordType = "COLLECTION";
                    log.debug("Creating the collection datamodel object");
                    CultureGrid_CollectionHDO collHDO = CultureGrid_CollectionHDO.lookupOrCreate(sess, newRes.getId(), "CultureGrid_Collection");
                    CultureGrid_CollectionDTO collDTO = (CultureGrid_CollectionDTO)record;
                    collHDO = collDTO.convertDTOToHDO(sess, collHDO, collHDO.getInstitution(), collHDO.getLocationInstitution());
                    datamodelHDO = collHDO;
				} else if ( record instanceof CultureGrid_InstitutionDTO ) {
                    recordType = "INSTITUTION";
                    log.debug("Creating the institution datamodel object");
                    CultureGrid_InstitutionHDO instHDO = CultureGrid_InstitutionHDO.lookupOrCreate(sess, newRes.getId(), "CultureGrid_Institution");
                    CultureGrid_InstitutionDTO instDTO = (CultureGrid_InstitutionDTO)record;
                    instHDO = instDTO.convertDTOToHDO(sess, instHDO);
                    datamodelHDO = instHDO;
                }
                    
                // Save the updates
                log.debug("About to save the changes back into the HDO");
                sess.update(datamodelHDO);
                log.debug("Finished saving HDO changes");

                // Now handle attaching the files to this record
					
				if ( datamodelHDO.getTempLocalFilename() != null && !"".equals(datamodelHDO.getTempLocalFilename().trim()) ) {
					// There is a file to attach
					String filename = datamodelHDO.getTempLocalFilename();
					
						if ( filename  != null && !"".equals(filename.trim()) ) {
							// A file to attach..
							String fullFilePath = pathToAttachments + File.separatorChar + filename.trim();
							log.debug("file retrieved from HDO: " + filename + ". Path to file: " + fullFilePath);
							
							// First read the file into a byte array to pass into the repository
							File actualFile = new File(fullFilePath);
							if ( actualFile.exists() ) {
								// Ok to do something - the file's there!
									
								// First get the content type
								@SuppressWarnings("unchecked")
								Collection<MimeType> mimeTypes = (Collection<MimeType>)MimeUtil.getMimeTypes(actualFile);
								Iterator<MimeType> typeIter = mimeTypes.iterator();
								String contentType = "unknown";
								if ( typeIter.hasNext() ) {
									MimeType val = typeIter.next();
									contentType = val.getMediaType() + "/" + val.getSubType();
								}

								// Now store the file
								log.debug("About to attach " + actualFile.getName() + " with content type: " + contentType);
								ArrayList<StoreResult> tempStoreResults = UploadUtils.uploadAndStore(repository, actualFile, contentType, actualFile.getName(), newRes.getId());
									
								log.debug("Just stored " + actualFile.getName());
                                                                    
                                if ( contentType.contains("image") ) {
                                    log.debug("content type for file worked out to be: " + contentType + " which we think is an image so trying to attach it for use as a thumbnail..");
                                    // The file is an image - set it as the relevant thumbnail (if suitable)
                                    if ("ITEM".equals(recordType) ) {
                                        // Item record..
                                        log.debug("Item record type so setting the thumbnail path..");
                                        boolean thumbnailSet = false;
                                        
                                        for(StoreResult res: tempStoreResults) {
                                            // Only consider the original version of the file
                                            if ( res != null && res.getSuccess() && res.getType().startsWith("original-") ) {
                                                log.debug("Considering the original version of the file (type: " + res.getType() + "). Location: " + res.getLocation());
                                                CultureGrid_ItemHDO itemHDO = (CultureGrid_ItemHDO)datamodelHDO;
                                                itemHDO.updateLink(LinkType.THUMBNAIL, repositoryPublicPath + res.getLocation());
                                                thumbnailSet = true;
                                                sess.update(itemHDO);
                                            }
                                        }
                                        
                                        // If we've not set the thumbnail then it's possible that we've previously stored the file and so didn't do it here
                                        // Look up the file and create if required
                                        if ( !thumbnailSet ) {
                                            AttachedResourceTypeHDO originalType = AttachedResourceTypeHDO.lookup(sess, "original-" + contentType);
                                            if ( originalType != null ) {
                                                AttachedResourceHDO attachmentHDO = AttachedResourceHDO.lookupByResourceFilenameAndType(sess, newRes.getId(), originalType, actualFile.getName());
                                                if ( attachmentHDO != null ) {
                                                    // We have found a previous matching attachment - link to it
                                                    log.debug("Existing attachment found - linking to it (location: repositoryPublicPath" + attachmentHDO.getLocation());
                                                    CultureGrid_ItemHDO itemHDO = (CultureGrid_ItemHDO)datamodelHDO;
                                                    itemHDO.updateLink(LinkType.THUMBNAIL, repositoryPublicPath + attachmentHDO.getLocation());
                                                    sess.update(itemHDO);
                                                } else {
                                                    // No matching attachment - can't create a thumbnail link (no problem)
                                                }
                                            } else {
                                                // Unable to find the original type that we'd expect..
                                                log.error("Attempting to use an attached image as a thumbnail by looking up previously attached files - unable to find a type in the database with identifier: original-" + contentType);
                                                // TODO - should this return an error?
                                            }
                                        }

                                    } else {
                                        // Non-item record - don't try and link to a thumbnail at the moment..
                                        log.debug("Local file specified for a record of type: " + recordType + " - not going to try and use it as a thumbnail");
                                    }
                                } else {
                                    log.debug("content type worked out to be : " + contentType + " which we don't think is an image so not trying to use it as a thumbnail");
                                }
								
								// Remember the details of the attachments in the datamodel
								for(StoreResult res: tempStoreResults) {
									if ( res != null ) {
										log.debug("result: " + res.toString());
										if ( !res.getSuccess() ) {
											// Something has gone wrong with an upload
											
											
											// TODO
										} else {
											// Add an entry in the database for this object
											AttachedResourceTypeHDO typeHDO = AttachedResourceTypeHDO.lookupOrCreate(sess, res.getMimeType(), res.getType());
											@SuppressWarnings("unused")
											AttachedResourceHDO attachmentHDO = AttachedResourceHDO.lookupOrCreate(sess, newRes.getId(), typeHDO, res.getOriginalFilename(), res.getLocation());
										}
									}
										
								}
							} else {
								// 	No file... Nothing to attach
							}
						}
						
				} else {
					// No file - nothing to attach
					log.debug("no file to attach according to the record itself");
				}                                
                                
                // Everything's done - commit the changes
                tx.commit();
					

			} else {
				// Unable to create a resource - problem
				log.error("An error occurred creating a new resource.. (not sure what though)");
				tx.rollback();
				// TODO
			}
		}
		
		return resourceId;
	}

}
