/* 
 * Unity - Communities
 * 
 * Community is critical to the student experience--but building community is 
 * just plain hard. Built on Communities and designed specifically for higher ed, 
 * Unity is a powerful networking tool to help you generate engagement and 
 * connect your campus.
 * 
 * Copyright (C) 2015 Motivis Learning Systems Inc.
 * 
 * This program is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * To contact Motivis Learning Systems Inc.
 * 25 Pelham Road
 * Salem, NH 03790
 * unity@motivislearning.com
 */

public with sharing class CommunityGroupFilesController {

	public transient List<FileWrapper> fileList { get; set; }
	public Id contentDetailId { get; set; }
	public String feedNavNumber { get; set; }
	public String mode { get { return mode == NULL ? 'initial' : mode; } set; }
	public String modal { get { return modal == NULL ? 'none' : modal; } set; }
	public String stack { get; set; }

	public Integer filesPageNumber { get { return filesPageNumber == NULL ? 1 : filesPageNumber; } set; }
	public Integer filesPagesTotal { get { return (filesPagesTotal == NULL || filesPagesTotal == 0) ? 1 : filesPagesTotal; } set; }
	public Integer filesTotal { get { return filesTotal == NULL ? 0 : filesTotal; } set; }
	public Boolean filesHasNext { get { return filesPageNumber < filesPagesTotal; } }
	public Boolean filesHasPrevious { get { return filesPageNumber > 1; } }
	public String fileQuery { get; set; }
	private Integer FILES_PAGE_SIZE { get { return mode == 'initial' ? 5 : 10; } }
	public Boolean allOk { get; set; }
	public Boolean enableShare { get; set; }

	public String parentId { get; set; }
	public ParentWrapper parent { get; set; }

	public ContentVersion cvRecord { get; set; }

	public CommunityGroupFilesController() {}

	public void initFromPage() {
		allOk = true;
		feedNavNumber = '1';
		String detailParam = ApexPages.currentPage().getParameters().get('f');
		mode = String.isBlank(detailParam) ? 'list' : 'detail';
		contentDetailId = (mode == 'detail') ? Id.valueOf(detailParam) : contentDetailId;
		parentId = String.isBlank(parentId) ? ApexPages.currentPage().getParameters().get('p') : parentId;

		String gr = ApexPages.currentPage().getParameters().get('gr');
		String gc = ApexPages.currentPage().getParameters().get('gc');
		if (String.isBlank(parentId) && (String.isNotBlank(gr) || String.isNotBlank(gc))) {
			parentId = String.isNotBlank(gr) ? gr : gc;
		}
		String fi = ApexPages.currentPage().getParameters().get('fi');
		if (String.isBlank(parentId) && String.isNotBlank(fi)) {
			List<FeedItem> fItems = [SELECT Id, ParentId FROM FeedItem WHERE Id = :fi];
			parentId = fItems[0].ParentId;
		}

		Map<String,String> keys = new Map<String,String>();
		Map<String,Schema.SobjectType> describe = Schema.getGlobalDescribe();
		for (String s:describe.keyset()) {
			keys.put(describe.get(s).getDescribe().getKeyPrefix(),s);
		}

		String parentObjectType = String.isNotBlank(parentId) ? keys.get(String.valueOf(parentId).substring(0,3)) : '';
		String pp = CommunityUtils.getPackagePrefix();
		parentObjectType = String.isNotBlank(pp) ? parentObjectType.replace(pp,'') : parentObjectType;
		if (String.isNotBlank(parentObjectType)) {
			if (parentObjectType.equalsIgnoreCase('CollaborationGroup') || parentObjectType.equalsIgnoreCase('Community_Group_Control__c')) {
				gr = String.isBlank(gr) && parentObjectType.equalsIgnoreCase('CollaborationGroup') ? parentId : gr;
				gc = String.isBlank(gr) && String.isBlank(gc) && parentObjectType.equalsIgnoreCase('Community_Group_Control__c') ? parentId : gc;
				Community_Group_Control__c control = CommunityGroupManageController.pullGroupControlRecord(false, gr, gc);
				if (control != NULL && control.Chatter_Group_ID__c != NULL) {
					CollaborationGroup chatterGroup = [SELECT Id, FullPhotoUrl, CollaborationType FROM CollaborationGroup WHERE Id = :control.Chatter_Group_ID__c];
					parent = new ParentWrapper(chatterGroup.Id, control.Name, Label.LBL_Groups, prepareUrl(Page.CommunityGroups.getUrl()), prepareUrl(Page.CommunityGroupDetailPage.getUrl())+'?gr='+chatterGroup.Id, chatterGroup.FullPhotoUrl);
					allOk = chatterGroup.Id != NULL && Community_Settings__c.getInstance().Chatter_Action_Disable_Group_Files__c != true;
					if (allOk) {
						ConnectApi.GroupMembershipType groupRole = ConnectApi.ChatterGroups.getGroup(Network.getNetworkId(), chatterGroup.Id).MyRole;
						allOk = control.Type__c == 'Public' || (groupRole != ConnectApi.GroupMembershipType.NotAMember && groupRole != ConnectApi.GroupMembershipType.NotAMemberPrivateRequested);
						enableShare = chatterGroup != NULL && chatterGroup.CollaborationType == 'Public';
					}
				}
				feedNavNumber = '5';
			}
			else if (parentObjectType.equalsIgnoreCase('Community_News__c')) {
				List<Community_News__c> cNews = [SELECT Id, Title__c, Image_Path__c FROM Community_News__c WHERE Id = :parentId];
				parent = new ParentWrapper(cNews[0].Id, cNews[0].Title__c, Label.LBL_News, prepareUrl(Page.CommunityNews.getUrl()), prepareUrl(Page.CommunityNewsDetail.getUrl())+'?id='+cNews[0].Id, CommunityUtils.checkUrl(cNews[0].Image_Path__c));
			}
			else if (parentObjectType.equalsIgnoreCase('Community_Events__c')) {
				List<Community_Events__c> cEvents = [SELECT Id, Name__c FROM Community_Events__c WHERE Id = :parentId];
				parent = new ParentWrapper(cEvents[0].Id, cEvents[0].Name__c, Label.LBL_Events, prepareUrl(Page.CommunityEventsList.getUrl()), prepareUrl(Page.CommunityEventDetail.getUrl())+'?id='+cEvents[0].Id, null);
			}
			else if (parentObjectType.equalsIgnoreCase('User') || parentObjectType.equalsIgnoreCase('Contact')) {
				List<User> uList = [SELECT Id, Name, FullPhotoUrl FROM User WHERE Id = :parentId OR ContactId = :parentId];
				parent = new ParentWrapper(uList[0].Id, uList[0].Name, Label.LBL_People, prepareUrl(Page.CommunityFeed_People.getUrl()), prepareUrl(Page.CommunityProfilePage.getUrl())+'?id='+uList[0].Id, uList[0].FullPhotoUrl);
			}
		}
		if (allOk) {
			init();
			enableShare = Community_Settings__c.getInstance().Disable_Share__c != true && enableShare != false;
		}
	}
	

	public void init() {
		fileList = new List<FileWrapper>();
		
		List<String> feedElementsIds = new List<String>();
		List<ContentVersion> cvList = new List<ContentVersion>();
		Set<String> collectedVersionIds = new Set<String>();

		parentId = String.isBlank(parentId) && parent != NULL && String.isNotBlank(parent.id) ? parent.id : parentId;
		if (String.isNotBlank(parentId)) {
			for (FeedItem fi : CommunityHelperWithoutSharing.getFeeds(parentId)) {
				feedElementsIds.add(fi.Id);
			}
		}
		List<ConnectApi.FeedElement> elements = new List<ConnectApi.FeedElement>();
		Set<String> subscribedToContent = new Set<String>();
		Map<String, String> contentDocumentToVersionMap = new Map<String, String>();
		Map<String, ContentDocument> cdMap = new Map<String, ContentDocument>();

		for (ConnectApi.BatchResult batchResult : ConnectApi.ChatterFeeds.getFeedElementBatch(Network.getNetworkId(), feedElementsIds)) {
			if (batchResult.isSuccess() && batchResult.getResult() instanceof ConnectApi.FeedElement) {
				ConnectApi.FeedElement fe = (ConnectApi.FeedElement)batchResult.getResult();
				if (fe.capabilities != NULL && fe.capabilities.content != NULL && fe.capabilities.content.versionId != NULL) {
					collectedVersionIds.add(fe.capabilities.content.versionId);
					elements.add(fe);
				}
			}
		}
		if (cvList.isEmpty()) {
			String cvQuery = 'SELECT Id, ContentDocumentId, ContentDocument.Id, ContentDocument.OwnerId, ContentDocument.Owner.Name, ContentDocument.LastModifiedDate, ContentDocument.Description FROM ContentVersion';
			String whQuery = !collectedVersionIds.isEmpty() ? ' Id IN :collectedVersionIds' : '';
			if (mode == 'detail') {
				whQuery += (String.isNotBlank(whQuery) ? ' AND' : '') + ' ContentDocumentId = \'' + contentDetailId + '\'';
			}
			if (mode == 'list' && String.isNotBlank(fileQuery)) {
				whQuery += (String.isNotBlank(whQuery) ? ' AND' : '') + ' ContentDocument.Title LIKE \'%' + String.escapeSingleQuotes(fileQuery) + '%\'';
			}
			cvQuery += String.isNotBlank(whQuery) ? ' WHERE' + whQuery : '';
			cvList = Database.query(cvQuery);
		}
		for (ContentVersion cvItem : cvList) {
			contentDocumentToVersionMap.put(cvItem.ContentDocumentId, cvItem.Id);
			cdMap.put(cvItem.Id, cvItem.ContentDocument);
		}
		for (EntitySubscription esItem : [SELECT ParentId FROM EntitySubscription WHERE SubscriberId =:Userinfo.getUserId() AND ParentId IN :contentDocumentToVersionMap.keySet() LIMIT 1000]) {
			subscribedToContent.add(contentDocumentToVersionMap.get(esItem.ParentId));
		}

		Set<String> filtredContentVersionsId = new Set<String>();
		filtredContentVersionsId.addAll(contentDocumentToVersionMap.values());
		Set<String> checkForDuplicatingSet = new Set<String>();
		List<ConnectApi.FeedElement> filteredElements = new List<ConnectApi.FeedElement>();
		for (ConnectApi.FeedElement fe3 : elements) {
			if (filtredContentVersionsId.contains(fe3.capabilities.content.versionId) && !checkForDuplicatingSet.contains(fe3.capabilities.content.versionId)) {
				filteredElements.add(fe3);
				checkForDuplicatingSet.add(fe3.capabilities.content.versionId);
			}
		}
		Integer fqlSize = filteredElements.size();
		filesTotal = fqlSize;
		filesPagesTotal = fqlSize / FILES_PAGE_SIZE + ((Math.mod(fqlSize, FILES_PAGE_SIZE) == 0) ? 0 : 1);
		if (filesPageNumber > filesPagesTotal) {
			filesPageNumber = filesPagesTotal;
		}
		Integer startCopyInterval = filesPageNumber > 1 ? ((filesPageNumber - 1) * FILES_PAGE_SIZE) : 0;
		Integer endCopyInterval = (fqlSize <= (startCopyInterval + FILES_PAGE_SIZE)) ? fqlSize : (startCopyInterval + FILES_PAGE_SIZE);
		Boolean modifyAllDataAccess = CommunityUtils.isModifyAllData();
		for (Integer ii = startCopyInterval; ii < endCopyInterval; ii++) {
			String vId = filteredElements[ii].capabilities.content.versionId;
			Boolean subscribed = subscribedToContent.contains(vId);
			ContentDocument cdItem = cdMap.get(vId);
			Boolean modifiable = modifyAllDataAccess || cdItem.OwnerId == UserInfo.getUserId();
			fileList.add(new FileWrapper(filteredElements[ii], subscribed, modifiable, cdItem));
		}
	}

	public FileWrapper getFileDetail() {
		return (mode == 'detail' && fileList != NULL && fileList.size() > 0) ? fileList[0] : NULL;
	}

	public void searchAction() {
		filesPageNumber = 1;
		init();
	}

	public void modeAction() {
		shareChoice = NULL;
		shareComment = NULL;
		cvRecord = NULL;
		if (modal == 'update') {
			cvRecord = [SELECT Id, Title, Description FROM ContentVersion WHERE Id = :stack];
		}
	}

	public String getCommunityId() {
		return Network.getNetworkId();
	}

	public String getParentDocumentId() {
		String result;
		if (modal == 'newversion') {
			result = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :stack].ContentDocumentId;
		}
		return result;
	}

	public void filesNextPage() {
		filesPageNumber++;
		init();
	}

	public void filesLastPage() {
		filesPageNumber = filesPagesTotal;
		init();
	}

	public void filesPreviousPage() {
		filesPageNumber--;
		init();
	}

	public void filesFirstPage() {
		filesPageNumber = 1;
		init();
	}

	public String shareChoice { get; set; }
	public String shareComment { get; set; }

	public void shareAction() {
		List<ConnectApi.BatchInput> feedElements = new List<ConnectApi.BatchInput>();
		String documentIdToShare = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :stack].ContentDocumentId;
		for (String sId : shareChoice.split(',')) {
			if (String.isNotBlank(sId)) {
				ConnectApi.FeedElementCapabilitiesInput feci = new ConnectApi.FeedElementCapabilitiesInput();
				feci.content = new ConnectApi.ContentCapabilityInput();
				feci.content.contentDocumentId = documentIdToShare;
				ConnectApi.FeedItemInput fei = new ConnectApi.FeedItemInput();
				fei.capabilities = feci;
				fei.feedElementType = ConnectApi.FeedElementType.FeedItem;
				fei.subjectId = sId;
				if (String.isNotBlank(shareComment)) {
					fei.body = new ConnectApi.MessageBodyInput();
					fei.body.messageSegments = new List<ConnectApi.MessageSegmentInput>();
					ConnectApi.TextSegmentInput tsi = new ConnectApi.TextSegmentInput();
					tsi.text = shareComment;
					fei.body.messageSegments.add(tsi);
				}
				feedElements.add(new ConnectApi.BatchInput(fei));
			}
		}
		if (feedElements.size() > 0) {
			ConnectApi.ChatterFeeds.postFeedElementBatch(Network.getNetworkId(), feedElements);
		}
		modal = 'none';
		modeAction();
	}

	public void updateAction() {
		update cvRecord;
		modal = 'none';
		modeAction();
		init();
	}

	public PageReference deleteAction() {
		ContentDocument cdDelete = [SELECT Id FROM ContentDocument WHERE Id IN (SELECT ContentDocumentId FROM ContentVersion WHERE Id = :stack)];
		delete cdDelete;
		PageReference pr;
		if (mode == 'detail') {
			pr = Page.CommunityGroupFiles;
			String groupControlIdParam = ApexPages.currentPage().getParameters().get('gc');
			String chatterGroupIdParam = ApexPages.currentPage().getParameters().get('gr');
			String groupParam = String.escapeSingleQuotes(String.isBlank(chatterGroupIdParam) ? groupControlIdParam : chatterGroupIdParam);
			pr.getParameters().put('gr', groupParam);
			pr.setRedirect(true);
		}
		else {
			init();
		}
		return pr;
	}

	@RemoteAction
	public static String getGroupsSelector(String query) {
		List<BlockWrapper> result = pullGroups(query);
		return result.size() == 0 ? NULL : JSON.serialize(result);
	}

	@RemoteAction
	public static String getPeoplesSelector(String query) {
		List<BlockWrapper> result = new List<BlockWrapper>();
		String escapedQuery = String.escapeSingleQuotes(query) + '%';
		List<User> uList = [
			SELECT Id, Name FROM User 
			WHERE (ContactId != NULL OR Community_Contact_ID__c != NULL) AND isActive = true AND Id != :UserInfo.getUserId()
			AND (FirstName LIKE :escapedQuery OR LastName LIKE :escapedQuery OR Name LIKE :escapedQuery)
			ORDER BY Name ASC
				];
		for (User uItem : uList) {
			result.add(new BlockWrapper(uItem.Id, uItem.Name));
		}
		return result.size() == 0 ? NULL : JSON.serialize(result);
	}

	@RemoteAction
	public static String subscriberAction(String versionId, Boolean inf) {
		String cdId = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :versionId].ContentDocumentId;
		CommunityUtils.subscriberUser(cdId, !inf);
		return '';
	}

	private static List<BlockWrapper> pullGroups(String inQuery) {
		List<BlockWrapper> resultList = new List<BlockWrapper>();
		Set<String> filtredChatterGroups = new Set<String>();
		for (CollaborationGroupMember cgmItem : [SELECT CollaborationGroupId FROM CollaborationGroupMember WHERE CollaborationGroup.NetworkId = :Network.getNetworkId() AND MemberId = :UserInfo.getUserId()]) {
			filtredChatterGroups.add(cgmItem.CollaborationGroupId);
		}
		if (filtredChatterGroups.size() > 0) {
			String queryString = 'SELECT Id, Name, Chatter_Group_ID__c FROM Community_Group_Control__c WHERE Chatter_Group_ID__c IN :filtredChatterGroups AND Status__c = \'Approved\'';
			if (String.isNotBlank(inQuery)) {
				queryString += ' AND Name LIKE \'' + String.escapeSingleQuotes(inQuery) + '%\'';
			}
			queryString += ' ORDER BY Name';
			for (Community_Group_Control__c cgcItem : Database.query(queryString)) {
				resultList.add(new BlockWrapper(cgcItem.Chatter_Group_ID__c, cgcItem.Name));
			}
		}
		return resultList;
	}

	public List<BlockWrapper> getGroups() {
		return (modal == 'sharegroup') ? pullGroups(NULL) : NULL;
	}

	private static String prepareUrl(String inUncouthUrl) {
		return String.isNotBlank(Site.getPathPrefix()) ? inUncouthUrl.replace('/apex/', Site.getPathPrefix() + '/') : inUncouthUrl;
	}

	public class FileWrapper {
		public String name { get { return name == NULL ? 'undefined' : name; } set; }
		public String lastModified { get { return lastModified == NULL ? '' : lastModified; } set; }
		public String ownerId { get { return ownerId == NULL ? '' : ownerId; } set; }
		public String ownerName { get { return ownerName == NULL ? 'undefined' : ownerName; } set; }
		public String aicon { get { return aicon == NULL ? 'fa-file-o' : aicon; } set; }
		public String previewUrl { get; set; }
		public String downloadUrl { get; set; }
		public String versionId { get; set; }
		public String documentId { get; set; }
		public String description { get; set; }
		public Boolean following { get; set; }
		public Boolean modifiable { get; set; }

		public String getShortName() {
			return name.abbreviate(20);
		}
		public FileWrapper(ConnectApi.FeedElement inchf, Boolean inf, Boolean inm, ContentDocument incd) {
			if (inchf.capabilities != NULL && inchf.capabilities.content != NULL) {
				ConnectApi.ContentCapability cc = inchf.capabilities.content;
				previewUrl = cc.renditionUrl720By480;
				downloadUrl = cc.downloadUrl;
				name = cc.title;
				aicon = !CommunitySearchController.AWESOME_FILE_TYPES.containsKey(cc.fileExtension) ? 'fa-file-o' : CommunitySearchController.AWESOME_FILE_TYPES.get(cc.fileExtension);
				versionId = cc.versionId;
				following = inf;
				modifiable = inm && String.isNotBlank(cc.versionId);
				ownerId = incd.OwnerId;
				ownerName = incd.Owner.Name;
				lastModified = CommunityFeedController.convertTimeDiff(incd.LastModifiedDate);
				description = incd.Description;
				documentId = incd.Id;
			}
		}
	}

	public class BlockWrapper {
		public String id { get; set; }
		public String name { get; set; }

		public BlockWrapper(String inid, String inname) {
			id = inid;
			name = inname;
		}
	}

	public class ParentWrapper {
		public Id id { get; set; }
		public String name { get; set; }
		public String type { get; set; }
		public String page { get; set; }
		public String detailPage { get; set; }
		public String photoUrl { get; set; }

		public ParentWrapper(Id inId, String inName, String inType, String inPage, String inDetailPage, String inPhotoUrl) {
			id = inId;
			name = inName;
			type = inType;
			page = inPage;
			detailPage = inDetailPage;
			photoUrl = inPhotoUrl;
		}
	}
}
