
let fileServiceBasics = {
	 
	/*
	 * In this module you will upload files to the file service, create versions of those files, query those files,
	 * and fetch the urls by which you can download versions of the files.
	*/
 
	/*
	 * STEP 1 - CREATE A NEW SCRIPT
	 * 
	 * Expand your test project
	 * You will see nodes for 'Scripts' and 'User Configs'
	 * Right click on Scripts and click 'Create New Script'
	 * In the field at the top of the vscode interface you will be prompted to enter information about the script
	 * For the script name enter 'B06 - File Service Basics'
	 * For the script description enter 'Basic scripts to learn File Service'
	 * For short name enter 'basic_files'
	 * For user type enter 'basic_files'
	 * Your project tree will refresh to show you the new script
	 * 
	 * Copy the entirety of this script into the new Script and save it
	 * Then commit the script to the current version on Twinit and reopen it locally
	 * 
	*/

	/*
	 * STEP 2 - UPLOAD FILES TO FILE SERVICE
	 * 
	 * Twinit supports two ways to uplaod files to the file service: Resumable and Non-Resumable.
	 * A resumable upload will ensure you have the ability restart any interrupted upload and is
	 * the more resilient of the two methods for uploading files. As such we will focus on resumable
	 * file uploads.
	 * 
	 * When using resumable upload you can provide three optional callbacks to capture progress of the
	 * upload, to handle upload errors, and to be informed when the upload completes.
	 * 
	 * Read through the script below to see how you cna use the Item Service to upload files.
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click uploadFiles
	 * -->NOTE: this is directly running the script below!
	 * When prompted select the three supporting files you downloaded from the lesson
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results.
	 * You will see the results of the onUploadProgress and onUploadCOmplete callbacks from the script below.
	 * 
	*/
	async uploadFiles(input, libraries, ctx, callback) {

		const { IafFileSvc } = libraries.PlatformApi
		const { IafLocalFile } = libraries.UiUtils

		// tags that we will apply to the files at upload based on file extension
		const fileTags = {
			jpg: ['image', 'jpg'],
			txt: ['text', 'txt'],
			json: ['data', 'json']
		}

		// select the three files in you downloaded from the course
		let files = await IafLocalFile.selectFiles({ multiple: true, accept: "*" })

		let fileUploadResults = []

		// we will provide an onComplete callback to the upload function so that we can resolve
		// our deferred Promises once the upload completes
		// we'll have this print to the console and the scripts callback
		// you will see the scripts callback content in the script results when the script
		// has completed running
		// the callback you provide for onComplete will be passed to the created file record in the file service
		function onUploadComplete(deferredResolve, file) {
			let message = file._name + ' COMPLETE'
			console.log(message)
			callback(message)
			fileUploadResults.push(file)
			deferredResolve()
		}

		// we will provide an onProgress callback as well to the upload function
		// we'll have this print to the console and the scripts callback
		// you will see the scripts callback content in the script results when the script
		// has completed running
		// the callback you provide for onProgress will be passed the bytes uplaoded so far the total bytes
		function onUploadProgress(bytesUploaded, bytesTotal, file) {
			let percentComplete = (bytesUploaded/bytesTotal * 100).toFixed(1)
			let message = file.name + ': ' + percentComplete
			console.log(message)
			callback(message)
		}

		// we will provide an onError callback as well to the upload function
		// we'll have this print to the console and the scripts callback
		// you will see the scripts callback content in the script results when the script
		// has completed running
		function onUploadError(deferredReject, error, file) {
			let message = file.name + ': ERROR' + error
			console.log(message)
			callback(message)
			deferredReject(error)
		}

		// upload each file async
		let uploadPromises = [] // a collection of deferred promises, 1 for each file we upload
		for (const file of files) {
			// since the file will be uploaded async we create a Promise and only resolve it once the file
			// has been 100% uploaded, making sure that the script does not complete before that.
			// We will pass the deferred resolve method to the onUploadComplete callback
			let deferredResolve, deferredReject
			uploadPromises.push(new Promise((resolve, reject) => {
				deferredResolve = resolve
				deferredReject = reject
			}))

			try {

				let tagsForFile = fileTags[file.name.split('.')[1]]

				// upload the file using resumable upload which can handle interrupts in network and which
				// will allow partial file uploads that can be completed at a later point in time
				//
				// Params:
				// 1. the file Stream, Buffer, or File to upload
				// 2. the project _namespaces to which to uplaod the files
				// 3. the parent folders for the file, if none are provided the root folder for the project will be used
				// 4. the tags to apply to the file
				// 5. the user's ctx uplaoding the files
				// 6. an options object containing
				// 	the filename for the file if not provided on the file
				//		onProgress, onComplete, and onError options callbacks
				//
				// We will upload one file at a time, but you can do parallel uploads by removing await
				// and throttling the number of uploads you allow at one time
				await IafFileSvc.addFileResumable(file.fileObj, ctx._namespaces, [], tagsForFile, ctx, {
						filename: file.name,
						onProgress: (bytesUploaded, bytesTotal) => onUploadProgress(bytesUploaded, bytesTotal, file),
						onComplete: (file) => onUploadComplete(deferredResolve, file), // onComplete will be passed the file record in the file service
						onError: (error) => onUploadError(deferredReject, error, file)
					}
				)
			} catch(e) {
				console.log(e)
				deferredReject(e)
			}

		}

		// wait for the onUploadSuccess callbacks to resolve all our deferred Promises then return from the script
		return await Promise.all(uploadPromises).then(() => {
			return fileUploadResults
    	})

	},
	
	/*
	 * STEP 3 - GET FILES FROM FILE SERVICE
	 * 
	 * Once files are uplaoded you can get then fetch them from the file service.
	 * 
	 * Review the getFiles script below. It does three file searches.
	 * The first is simply a search for any file in the root folder for the project.
	 * The second will search for any files with 'image' in their _tags.
	 * The third will search for any files with 'image' or 'text' in their _tags
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click getFiles
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results.
	 * 
	 * Notice each file has these properties:
	 * 
	 * 	_name: the name of the file wiht its extension
	 * 	_id: the id of the file in the file service
	 * 	_tipVersion: this is the version number of the latest version of the file
	 * 	_tipId: the id of the tip version of the file
	 * 	_type: this indicates that the file is of type file, not a folder
	 * 	_tags: the tags to give to the file on upload
	 * 	_parents: the parent folders the file resides in
	 * 	_status: the upload status of the latest version
	 * 	
	*/
	async getFiles(input, libraries, ctx) {

		const { IafFileSvc } = libraries.PlatformApi

		const allFileSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root' // if no parent is provided 'root' would be used by default
		}

		const imageFileSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root',
			_tags: ['image']
		}

		const imageOrTextFileSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root',
			_tags: ['image', 'text']
		}
		
		// get files from the file service with the various criteria
		let allFiles =  await IafFileSvc.getFiles(allFileSearchCriteria, ctx)
		let imageFiles = await IafFileSvc.getFiles(imageFileSearchCriteria, ctx)
		let imageOrTextFiles = await IafFileSvc.getFiles(imageOrTextFileSearchCriteria, ctx)

		return { allFiles, imageFiles, imageOrTextFiles }

	},

	/*
	 * STEP 4 - UPLOAD NEW VERSIONS OF FILES TO FILE SERVICE
	 * 
	 * New versions of files can be created by uploading a file with same name.
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click uploadFiles
	 * When prompted select the three supporting files you downloaded from the lesson
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click getFiles
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * REview the result and notice that the version information has updated.
	*/

	/*
	 * STEP 5 - GET VERSIONS OF FILES FROM FILE SERVICE
	 * 
	 *	If you need information about an older version of a file you can use getVersions.
	 *
	 * Review the script below to see how get older version information.
	 *
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click getVersions
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results
	 * Notice that you now have the information specific to each version of City.jpg
	*/
	async getVersions(input, libraries, ctx) {

		const { IafFileSvc } = libraries.PlatformApi

		// get the file record for City.jpg
		const allFileSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root', 
			_name: 'City.jpg' // get version of City.jpg
		 }
		 
		 // get files from the file service
		 let fileRes = await IafFileSvc.getFiles(allFileSearchCriteria, ctx)

		 let cityFile = fileRes._list[0]

		 // get versions of the file
		 return await IafFileSvc.getFileVersions(cityFile._id, ctx)
	},

	/*
	 * STEP 6 - GET DOWNLOAD URL FOR FILES
	 * 
	 * To download files from the file service you must fetch a download url.
	 * Download urls are good for 24 hours. After that you will need to fetch a new download url.
	 * 
	 * You can fetch urls for any file and any version of a file.
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click getDownloadUrls
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results
	 * You can CTRL-Click the urls in the results to download the file
	 * 
	*/
	async getDownloadUrls(input, libraries, ctx) {

		const { IafFileSvc } = libraries.PlatformApi

		const allFileSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root'
		 }
		 
		// get files from the file service and get their urls
		// the fourth parameter of getFiles is a Boolean, that if true will return the urls
		// for the tip version of each file
		let urlsForAllTipFiles = (await IafFileSvc.getFiles(allFileSearchCriteria, ctx, null, true))._list

		const cityFileSearchCriteria = {
		_namespaces: ctx._namespaces,
		_parents: 'root',
		_name: 'City.jpg'
		}
		 
		// get the download for the first version of City.jpg
		let fileRes = await IafFileSvc.getFiles(cityFileSearchCriteria, ctx)

		let cityFile = fileRes._list[0]

		let cityVersions = await IafFileSvc.getFileVersions(cityFile._id, ctx)

		let firstVersion = cityVersions._list.find(v => v._version === 1)

		let cityFirstVersion = await IafFileSvc.getFileVersionUrl(cityFile._id, firstVersion._id, ctx)

		return { urlsForAllTipFiles, cityFirstVersion }
	},

	/*
	 * STEP 7 - DELETING FILES
	 * 
	 * You can delete files from the file service. Deleting a file deletes all versions of the file.
	 * 
	 * In the script below we will delete all the files we have uploaded.
	 * We upload them again in the next step.
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click deleteAllFiles
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results
	 * 
	*/
	async deleteAllFiles(input, libraries, ctx) {
		
		const { IafFileSvc } = libraries.PlatformApi

		const allFileSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root'
		 }
		 
		 // get files from the file service
		 let allFiles = await IafFileSvc.getFiles(allFileSearchCriteria, ctx)

		 let allRes = []
		 for (const file of allFiles._list) {

			let delRes = await IafFileSvc.deleteFile(file._id, ctx)
			allRes.push(file._name + ': ' + delRes)

		 }

		 return allRes
	},

	/*
	 * STEP 7 - ADD FOLDERS
	 * 
	 * You can organize your files in folders in the File Service.
	 * 
	 * In the script below we will create a folder structure that int he next step we will
	 * upload files into.
	 * 
	 * The folder structure we create will be this one:
	 * 
	 * - SHARED
	 * - Internal
	 * 	- City
	 * 	- Images
	 * 	- Other
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click addFolderStructure
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results.
	 * The resutls contain each folder that is created.
	 * Notice that the City, Images, and Other folder all have _parents
	 * 
	*/
	async addFolderStructure(input, libraries, ctx) {

		const { IafFileSvc } = libraries.PlatformApi

		const topLevelFolders = ['SHARED', 'Internal']
		const internalFolders = ['City', 'Data']

		let allFolders = [], internalFolder
		for (const folderName of topLevelFolders) {

			// to create a folder you use
			// 1. the name of the folder you want to create
			// 2. the user's project namespace
			// 3. a list of parent folder id
			// 4. the user's ctx
			// Here we are creating the two top level folders so we will not provide a parent id
			// because we want the folders to be created in the root
			let newFolder = await IafFileSvc.addFolder(folderName, ctx._namespaces, null, ctx)
			allFolders.push(newFolder)
			if (folderName === 'Internal') internalFolder = newFolder

		}

		for (const folderName of internalFolders) {

			// Here while creating the Internal folder contents
			// we will pass the internalFolder's _id as the parent
			// so that the City, Images, and Others folders will be nested inside the Internal folder
			let newFolder = await IafFileSvc.addFolder(folderName, ctx._namespaces, [internalFolder._id], ctx)
			allFolders.push(newFolder)
			if (folderName === 'Internal') internalFolder = newFolder
			
		}

		return allFolders
	},

	/*
	 * STEP 7 - UPLOAD FILES TO FOLDERS
	 * 
	 * You can now upload files to one folder.
	 * 
	 * In the below script we will upload the three supporting files again, but we will put the
	 * City.jpg file into the Internal/City, the sample.json file into the internal/Data folder and
	 * the Moby Dick.txt file into the SHARED folder.
	 * 
	 * Review the script below.
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click uploadFilesToFolders
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results.
	 * 
	*/
	async uploadFilesToFolders(input, libraries, ctx, callback) {

		const { IafFileSvc } = libraries.PlatformApi
		const { IafLocalFile } = libraries.UiUtils

		// First we need to get all of our folders so we can use them as parents
		const rootFolderSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: 'root',
			_type: 'dir'
		}

		let rootFolders =  (await IafFileSvc.getFiles(rootFolderSearchCriteria, ctx))._list

		let internalFolder = rootFolders.find(f => f._name === 'Internal')
		let sharedFolder = rootFolders.find(f => f._name === 'SHARED')

		const internalFolderSearchCriteria = {
			_namespaces: ctx._namespaces,
			_parents: [internalFolder._id],
			_type: 'dir'
		}

		let internalFolders = (await IafFileSvc.getFiles(internalFolderSearchCriteria, ctx))._list

		let cityFolder = internalFolders.find(f => f._name === 'City')
		let dataFolder = internalFolders.find(f => f._name === 'Data')

		// select the three files in you downloaded from the course
		let files = await IafLocalFile.selectFiles({ multiple: true, accept: "*" })

		// tags that we will apply to the files at upload based on file extension
		const fileTags = {
			jpg: ['image', 'jpg'],
			txt: ['text', 'txt'],
			json: ['data', 'json']
		}

		let fileUploadResults = []

		// we will provide an onComplete callback to the upload function so that we can resolve
		// our deferred Promises once the upload completes
		// we'll have this print to the console and the scripts callback
		// you will see the scripts callback content in the script results when the script
		// has completed running
		// the callback you provide for onComplete will be passed the created file record in the file service
		function onUploadComplete(deferredResolve, file) {
			let message = file._name + ' COMPLETE'
			console.log(message)
			callback(message)
			fileUploadResults.push(file)
			deferredResolve()
		}

		// we will provide an onProgress callback as well to the upload function
		// we'll have this print to the console and the scripts callback
		// you will see the scripts callback content in the script results when the script
		// has completed running
		// the callback you provide for onProgress will be passed the bytes uplaoded so far the total bytes
		function onUploadProgress(bytesUploaded, bytesTotal, file) {
			let percentComplete = (bytesUploaded/bytesTotal * 100).toFixed(1)
			let message = file.name + ': ' + percentComplete
			console.log(message)
			callback(message)
		}

		// we will provide an onError callback as well to the upload function
		// we'll have this print to the console and the scripts callback
		// you will see the scripts callback content in the script results when the script
		// has completed running
		function onUploadError(deferredReject, error, file) {
			let message = file.name + ': ERROR' + error
			console.log(message)
			callback(message)
			deferredReject(error)
		}

		// upload each file async
		let uploadPromises = [] // a collection of deferred promises, 1 for each file we upload
		for (const file of files) {
			// since the file will be uploaded async we create a Promise and only resolve it once the file
			// has been 100% uploaded, making sure that the script does not complete before that.
			// We will pass the deferred resolve method to the onUploadComplete callback
			let deferredResolve, deferredReject
			uploadPromises.push(new Promise((resolve, reject) => {
				deferredResolve = resolve
				deferredReject = reject
			}))

			try {

				let tagsForFile = fileTags[file.name.split('.')[1]]

				// get the folder parents depending on the file being uploaded
				let parentsForFile = []
				if (file.name === 'City.jpg') {
					parentsForFile = [cityFolder._id]
				} else if (file.name === 'sample.json') {
					parentsForFile = [dataFolder._id]
				} else {
					parentsForFile = [sharedFolder._id]
				}

				// upload the file using resumable upload which can handle interrupts in network and which
				// will allow partial file uploads that can be completed at a later point in time
				//
				// Params:
				// 1. the file Stream, Buffer, or File to upload
				// 2. the project _namespaces to which to uplaod the files
				// 3. the parent folders for the file
				// 4. the tags to apply to the file
				// 5. the user's ctx uplaoding the files
				// 6. an options object containing
				// 	the filename for the file if not provided on the file
				//		onProgress, onComplete, and onError options callbacks
				//
				// We will upload one file at a time, but you can do parallel uploads by removing await
				// and throttling the number of uploads you allow at one time
				await IafFileSvc.addFileResumable(file.fileObj, ctx._namespaces, parentsForFile, tagsForFile, ctx, {
						filename: file.name,
						onProgress: (bytesUploaded, bytesTotal) => onUploadProgress(bytesUploaded, bytesTotal, file),
						onComplete: (file) => onUploadComplete(deferredResolve, file), // onComplete will be passed the file record in the file service
						onError: (error) => onUploadError(deferredReject, error, file),
						onUploadError: (error) => onUploadError(deferredReject, error, file)
					}
				)
			} catch(e) {
				console.log(e)
				deferredReject(e)
			}

		}

		// wait for the onUploadSuccess callbacks to resolve all our deferred Promises then return from the script
		return await Promise.all(uploadPromises).then(() => {
			return fileUploadResults
    	})

	},

	/*
	 * STEP 8 - GET ALL FILE AND FOLDERS
	 * 
	 * Let's now recursively get all the folders and the files contained in each one.
	 * 
	 * Right click the 'B06 - File Service Basics' script and click Run Script
	 * From the dropdown at the top of the vscode window click getAllFoldersAndFiles
	 * -->NOTE: this is directly running the script below!
	 * When the script is completed it will open a new document with the results of the script
	 * 
	 * Review the results
	 * 
	 * Notice that the folders have a 'contents' property.
	 * If you expand the 'contents' results you can find contents of each folder.
	 * 
	*/
	async getAllFoldersAndFiles(input, libraries, ctx) {

		const { IafFileSvc } = libraries.PlatformApi

		// recursively gets a folder's contents and any child folder contents
		async function getFolderContents(folderid) {

			const rootFolderSearchCriteria = {
				_namespaces: ctx._namespaces,
				_parents: folderid
			}
	
			// get the contents of the folder
			let myContents =  (await IafFileSvc.getFiles(rootFolderSearchCriteria, ctx))._list

			// recursively get the contents of all child folders
			for (let item of myContents) {
				
				if (item._type === 'dir') {
					item.contents = await getFolderContents(item._id)
				}

			}

			return myContents
		}

		// start retrieving folder and file contents at the root
		let root = {
			contents: await getFolderContents('root')
		}

		return {root}
	}
 
 }
 
 export default fileServiceBasics