package org.gcube.portlets.widgets.workspacesharingwidget.server;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.gcube.application.framework.core.session.ASLSession;
import org.gcube.common.homelibrary.home.exceptions.HomeNotFoundException;
import org.gcube.common.homelibrary.home.exceptions.InternalErrorException;
import org.gcube.common.homelibrary.home.workspace.Workspace;
import org.gcube.common.homelibrary.home.workspace.WorkspaceFolder;
import org.gcube.common.homelibrary.home.workspace.WorkspaceItem;
import org.gcube.common.homelibrary.home.workspace.WorkspaceItemType;
import org.gcube.common.homelibrary.home.workspace.WorkspaceSharedFolder;
import org.gcube.common.homelibrary.home.workspace.accessmanager.ACLType;
import org.gcube.common.homelibrary.home.workspace.exceptions.InsufficientPrivilegesException;
import org.gcube.common.homelibrary.home.workspace.exceptions.ItemAlreadyExistException;
import org.gcube.common.homelibrary.home.workspace.exceptions.WorkspaceFolderNotFoundException;
import org.gcube.common.homelibrary.home.workspace.exceptions.WrongDestinationException;
import org.gcube.portlets.widgets.workspacesharingwidget.client.ConstantsSharing;
import org.gcube.portlets.widgets.workspacesharingwidget.client.rpc.WorkspaceSharingService;
import org.gcube.portlets.widgets.workspacesharingwidget.server.notifications.NotificationsProducer;
import org.gcube.portlets.widgets.workspacesharingwidget.server.notifications.NotificationsUtil;
import org.gcube.portlets.widgets.workspacesharingwidget.server.util.ScopeUtilFilter;
import org.gcube.portlets.widgets.workspacesharingwidget.server.util.UserUtil;
import org.gcube.portlets.widgets.workspacesharingwidget.server.util.WsUtil;
import org.gcube.portlets.widgets.workspacesharingwidget.shared.CredentialModel;
import org.gcube.portlets.widgets.workspacesharingwidget.shared.FileModel;
import org.gcube.portlets.widgets.workspacesharingwidget.shared.InfoContactModel;
import org.gcube.portlets.widgets.workspacesharingwidget.shared.SessionExpiredException;
import org.gcube.portlets.widgets.workspacesharingwidget.shared.WorkspaceACL;
import org.gcube.vomanagement.usermanagement.GroupManager;
import org.gcube.vomanagement.usermanagement.UserManager;
import org.gcube.vomanagement.usermanagement.impl.liferay.LiferayGroupManager;
import org.gcube.vomanagement.usermanagement.impl.liferay.LiferayUserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class WorkspaceSharingServiceImpl extends RemoteServiceServlet implements
    WorkspaceSharingService {

	protected Logger logger = LoggerFactory.getLogger(WorkspaceSharingServiceImpl.class);
	
	
	protected GWTWorkspaceSharingBuilder getGWTWorkspaceBuilder()
	{
		return WsUtil.getGWTWorkspaceSharingBuilder(this.getThreadLocalRequest().getSession());
	}

	protected Workspace getWorkspace() throws InternalErrorException, HomeNotFoundException, WorkspaceFolderNotFoundException
	{
		return WsUtil.getWorkspace(this.getThreadLocalRequest().getSession());
	}
	
	protected NotificationsProducer getNotificationProducer(){
		
		return WsUtil.getNotificationProducer(WsUtil.getAslSession(this.getThreadLocalRequest().getSession()));
	}
	
	protected ScopeUtilFilter getScopeUtilFilter(){
		
		return WsUtil.getScopeUtilFilter(this.getThreadLocalRequest().getSession());
	}
	
	protected boolean isTestMode(){
		return WsUtil.withoutPortal;
	}
	
	@Override
	public List<InfoContactModel> getAllContacts() throws Exception {
		
		try {
			WsUtil.getAslSession(this.getThreadLocalRequest().getSession()); //THIS FORCE THE SESSION CREATION
			logger.info("Get all contacts");
		
//			WorkspaceItem parent = workspace.getItem(parentFileModel.getIdentifier()); //get item from workspace	
//			workspace.getAllScope();

			GWTWorkspaceSharingBuilder builder = getGWTWorkspaceBuilder();
			
			if(isTestMode()){
				logger.info("WORKSPACE PORTLET IS IN TEST MODE - RETURN TEST USERS");
				logger.warn("WORKSPACE PORTLET IS IN TEST MODE - RETURN TEST USERS");
				List<InfoContactModel> listContactsModel = new ArrayList<InfoContactModel>();
//				//TEST USERS
				listContactsModel.add(new InfoContactModel("federico.defaveri", "federico.defaveri", "Federico de Faveri", false));
				listContactsModel.add(new InfoContactModel("antonio.gioia", "antonio.gioia", "Antonio Gioia", false));
				listContactsModel.add(new InfoContactModel("fabio.sinibaldi", "fabio.sinibaldi", "Fabio Sinibaldi", false));
				listContactsModel.add(new InfoContactModel("pasquale.pagano", "pasquale.pagano", "Pasquale Pagano", false));
//				listContactsModel.add(new InfoContactModel(WsUtil.TEST_USER, WsUtil.TEST_USER, WsUtil.TEST_USER_FULL_NAME));
				listContactsModel.add(new InfoContactModel("francesco.mangiacrapa", "francesco.mangiacrapa", "Francesco Mangiacrapa", false));
				listContactsModel.add(new InfoContactModel("massimiliano.assante", "massimiliano.assante", "Massimiliano Assante", false));
				listContactsModel.add(new InfoContactModel("valentina.marioli", "valentina.marioli", "Valentina Marioli", false));
				listContactsModel.add(new InfoContactModel("leonardo.candela", "leonardo.candela", "Leonardo Candela", false));
				return listContactsModel;
				
			}

			UserManager userManag = new LiferayUserManager();
			GroupManager gm = new LiferayGroupManager();
			String groupId = gm.getRootVO().getGroupId();
			logger.info("user manager getting list users by group: "+groupId);
			return builder.buildGXTListContactsModel(userManag.listUsersByGroup(groupId));
			
		} catch (Exception e) {
			logger.error("Error in server get all contacts ", e);
//			return new ArrayList<InfoContactModel>();
			throw new Exception(e.getMessage());
		}
	}
	
	@Override
	public List<InfoContactModel> getListUserSharedByFolderSharedId(String folderSharedId) throws Exception{
		
		logger.info("getListUserSharedByFolderSharedId "+ folderSharedId);

		try {
			Workspace workspace = getWorkspace();

			WorkspaceItem wsItem = workspace.getItem(folderSharedId);
			
			if(NotificationsUtil.isASharedFolder(wsItem)){
				
				WorkspaceSharedFolder wsFolder = (WorkspaceSharedFolder) wsItem;

				GWTWorkspaceSharingBuilder builder = getGWTWorkspaceBuilder();

				List<String> listPortalLogin = wsFolder.getUsers();

				logger.info("getListUserSharedByFolderSharedId return "+ listPortalLogin.size() + " user");

				if(isTestMode())
					return builder.buildGxtInfoContactFromPortalLoginTestMode(listPortalLogin);
				
				return builder.buildGxtInfoContactsFromPortalLogins(listPortalLogin);
				
			}
			else{
				logger.info("the item with id: "+folderSharedId+ " is not  "+WorkspaceItemType.SHARED_FOLDER);
				
				//DEBUG
				//System.out.println("the item with id: "+folderSharedId+ " is not  "+WorkspaceItemType.SHARED_FOLDER);
			}
			return new ArrayList<InfoContactModel>();
		
		} catch (Exception e) {
			logger.error("Error in getListUserSharedByItemId ", e);
			throw new Exception(e.getMessage());
		}
	}
	
	@Override
	public InfoContactModel getOwnerByItemId(String itemId) throws Exception {
		
		logger.info("get Owner By ItemId "+ itemId);
		try {
			
			Workspace workspace = getWorkspace();

			WorkspaceItem wsItem = workspace.getItem(itemId);

			GWTWorkspaceSharingBuilder builder = getGWTWorkspaceBuilder();
			
			return builder.buildGXTInfoContactModel(wsItem.getOwner());
			
		} catch (Exception e) {
			logger.error("Error in getOwnerByItemId ", e);
			throw new Exception(e.getMessage());
		}
	}
	
	/**
	 * 
	 * @return
	 * @throws Exception
	 */
	@Override
	public List<WorkspaceACL> getACLs() throws Exception{
		try {

			GWTWorkspaceSharingBuilder builder = getGWTWorkspaceBuilder();
			return builder.getWorkspaceACLFromACLs(Arrays.asList(ACLType.values()));
		} catch (Exception e) {
			logger.error("Error in server get ACLs", e);
			String error = ConstantsSharing.SERVER_ERROR +" get ACL rules. "+e.getMessage();
			throw new Exception(error);
		}
	}
	
	@Override
	public String getMyLogin(){
		ASLSession asl = WsUtil.getAslSession(this.getThreadLocalRequest().getSession());
		return asl.getUsername();
	}
	
	@Override
	public FileModel getFileModelByWorkpaceItemId(String itemId) throws Exception {

		try {
			
//			System.out.println("Get file model by itemId: "+itemId);
			
			if(itemId == null || itemId.isEmpty())
				throw new Exception("Identifier is null or empty");	
			
			Workspace workspace = getWorkspace();
			
			logger.info("Get file model by itemId: "+itemId);
			
			WorkspaceItem wsItem =  workspace.getItem(itemId);
			
			GWTWorkspaceSharingBuilder builder = getGWTWorkspaceBuilder();

//			Long startTime =  System.currentTimeMillis();
//		
//			Long endTime = System.currentTimeMillis() - startTime;
//			String time = String.format("%d msc %d sec", endTime, TimeUnit.MILLISECONDS.toSeconds(endTime));
//			logger.debug("get child for Grid by id returning element in " + time);
			
			logger.info("Getting folder parent");
			
			WorkspaceFolder folderParent = null;
			if(wsItem!=null)
				folderParent = wsItem.getParent(); 	//BUILD PARENT
			else
				throw new Exception("Workspace item not found");
			
	
			FileModel parent = null;
			if(folderParent!=null){
				logger.info("Folder parent has id: "+folderParent.getId() + " and name: "+folderParent.getName());
				parent = builder.buildGXTFileModelItem(folderParent, null);
			}
			else
				logger.info("Folder parent for item: "+wsItem.getId() +" is null");
		
			//BUILD ITEM
			return builder.buildGXTFileModelItem(wsItem, parent);
		} catch (Exception e) {
			logger.error("Error in server during item retrieving, getFileModelByWorkpaceItemId", e);
			String error =  ConstantsSharing.SERVER_ERROR + " retrieving the item from workspace, "+e.getMessage();
			throw new Exception(error);
		}
	}
	
	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.workspace.client.rpc.GWTWorkspaceService#isSessionExpired()
	 */
	@Override
	public boolean isSessionExpired() throws Exception {
		return WsUtil.isSessionExpired(this.getThreadLocalRequest().getSession());
	}

	
	// DEBUG
	private void printContacts(List<InfoContactModel> listContacts) {

		boolean testMode = isTestMode();
		if (testMode)
			System.out.println("Contacts: ");
		else
			logger.debug("Contacts:");

		for (InfoContactModel infoContactModel : listContacts) {
			if (testMode)
				System.out.println("User: " + infoContactModel);
			else
				logger.debug("User: " + infoContactModel);
		}
	}

	
	@Override
	public boolean shareFolder(FileModel folder, List<InfoContactModel> listContacts, boolean isNewFolder, WorkspaceACL acl) throws Exception {
		
		if(isSessionExpired())
			throw new SessionExpiredException();
		
		try {

			Workspace workspace = getWorkspace();
			
			logger.info("sharing item id: "+ folder.getIdentifier() 
					+ " name: "+ folder.getName() 
//					+ " parent name: " + folder.getParentFileModel().getName()
					+ " listContacts size: " + listContacts.size());
			
//			//DEBUG
			//System.out.println("shareFolder "+ folder.getIdentifier() + " name: "+ folder.getName() + " parent name: " + folder.getParentFileModel().getName() + " listContacts size: " + listContacts.size());
			
//			for (InfoContactModel infoContactModel : listContacts) {
//				System.out.println("share with "+ infoContactModel.getLogin());
//			}
			
			printContacts(listContacts);

			List<String> listLogin = UserUtil.getListLoginByInfoContactModel(listContacts);
			
			WorkspaceSharedFolder sharedFolder = null;
			
			List<InfoContactModel> listSharedContact = null;
			
			boolean sourceFolderIsShared = folder.isShared();
			
			if(sourceFolderIsShared){ //if source folder is already share... retrieve old list of sharing to notify
				listSharedContact = getListUserSharedByFolderSharedId(folder.getIdentifier());
			}

			if(listLogin.size()>0){
				
				if(!isNewFolder){
					sharedFolder = workspace.shareFolder(listLogin, folder.getIdentifier());
					sharedFolder.setDescription(folder.getDescription()); //SET NEW DESCRIPTION
				}
				else
					sharedFolder = workspace.createSharedFolder(folder.getName(), folder.getDescription(), listLogin, folder.getParentFileModel().getIdentifier());
			}
			
			boolean created = sharedFolder==null?false:true;

			if(acl!=null)
				setACLs(sharedFolder.getId(), listLogin, acl.getId().toString());
			
			if(created){
				NotificationsProducer np = getNotificationProducer();
				if(!sourceFolderIsShared) //if source folder is not already shared
					np.notifyFolderSharing(listContacts, sharedFolder);
				else{
					/*System.out.println("SHARED CONTACS: ");
					printContacts(listSharedContact);
					System.out.println("NEW CONTACS: ");
					printContacts(listContacts);*/
					np.notifyAddedUsersToSharing(listSharedContact, listContacts, sharedFolder);
				}
			}
				
			return created;

		} catch (InsufficientPrivilegesException e) {
			logger.error("Error in shareFolder ", e);
			String error = "An error occurred on creating shared folder. "+ e.getMessage();
			throw new Exception(error);
			
		} catch (ItemAlreadyExistException e) {
			logger.error("Error in shareFolder ", e);
			String error = "An error occurred on creating shared folder. "+ e.getMessage();
			throw new Exception(error);
			
		} catch (WrongDestinationException e) {
			logger.error("Error in shareFolder ", e);
			String error = "An error occurred on creating shared folder. "+ e.getMessage();
			throw new Exception(error);

		} catch (Exception e) {
			logger.error("Error in shareFolder ", e);
			e.printStackTrace();
			String error = ConstantsSharing.SERVER_ERROR+" sharing item.";
			throw new Exception(error);
		}
	}
	

	public void setACLs(String folderId, List<String> listLogins, String aclType) throws Exception{
		try {

			if(folderId == null)
				throw new Exception("Folder id is null");	
				
			if(listLogins==null || listLogins.size()==0)
				throw new Exception("List Logins is null or empty");
			
			logger.info("Setting ACL for folder id: "+folderId);
			logger.info("ACL type is: "+aclType);
				
			Workspace workspace = getWorkspace();
			WorkspaceItem wsItem = workspace.getItem(folderId);
			
			if(wsItem.isShared() && (wsItem.getType().equals(WorkspaceItemType.SHARED_FOLDER))){
				WorkspaceSharedFolder ite = (WorkspaceSharedFolder) workspace.getItemByPath(wsItem.getPath());
				ite.setACL(listLogins, ACLType.valueOf(aclType));
			}else
				throw new Exception("Source item is not shared or shared folder");
			
			logger.info("Setting ACL completed, retuning");
		} catch (Exception e) {
			logger.info("Error in set ACLs", e);
			String error = ConstantsSharing.SERVER_ERROR +" setting permissions. "+e.getMessage();
			throw new Exception(error);
		}
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.widgets.workspacesharingwidget.client.rpc.WorkspaceSharingService#getInfoContactModelsFromCredential(java.util.List)
	 */
	@Override
	public List<InfoContactModel> getInfoContactModelsFromCredential(List<CredentialModel> listAlreadySharedContact) throws Exception {
		
		if(listAlreadySharedContact==null || listAlreadySharedContact.size()==0)
			throw new Exception("Credentials list is null or empty");
		
		
		GWTWorkspaceSharingBuilder builder = getGWTWorkspaceBuilder();
		List<InfoContactModel> listContacts = new ArrayList<InfoContactModel>(listAlreadySharedContact.size());
		for (CredentialModel credentialModel : listAlreadySharedContact) {
			
			if(!credentialModel.isGroup()){
				InfoContactModel contact = builder.buildGxtInfoContactFromPortalLogin(credentialModel.getLogin());
				contact.setReferenceCredential(credentialModel);
				listContacts.add(contact);
			}
		}
		
		return listContacts;
	}


}
