Source: Folder.js

const { coreDataDateToJSDate } = require('./helpers');

/**
 * The Folder class contains Albums and sub-Folders. 
 * It's basically what you see in the sidebar of your Photos app: a folder structure of albums.
 *
 * Read from the database `RKFolder`.
 */
class Folder {
	/**
	 * Create a new Folder class.
	 * @param  {PhotosLibrary} library An instance of PhotosLibrary used for album fetching.
	 * @param  {Object} row A database row from RKFolder.
	 * @return {Folder} The generated Folder instance.
	 */
	constructor(library, row) {
		/**
		 * The library used for media fetching.
		 * @type {PhotosLibrary}
		 */
		this.library = library;

		/**
		 * The ID of the Folder in the database (database field: modelId).
		 * @type {int}
		 */
		this.id = row.modelId;

		/**
		 * The uuid of the Folder in the database (database field: uuid).
		 * @type {string}
		 */
		this.uuid = row.uuid;

		/**
		 * The name of the Folder. (database field: name).
		 * @type {string}
		 */
		this.name = row.name;

		/**
		 * The cloud ID of the Folder. (database field: cloudIdentifier).
		 * @type {string}
		 */
		this.cloudId = row.cloudIdentifier;

		/**
		 * Wether or not the folder has been synced to iCloud. (database field: hasBeenSynced).
		 * @type {Bool}
		 */
		this.synced = row.hasBeenSynced;

		/**
		 * The date the Folder was created. (database field: createDate).
		 *
		 * The database value is converted from a Apple Core Data date to a Javascript Date object.
		 * @type {Date}
		 */
		this.created = coreDataDateToJSDate(row.createDate);

		/**
		 * A Folder is magic if it is generated by Apple Photos and not by the user (e.g. Favorites).
		 * @type {Bool}
		 */
		this.magic = Boolean(row.isMagic);

		/**
		 * The uuid of the parent Folder the Folder is in. (database field: folderUuid).
		 * @type {string}
		 */
		this.parentFolderUuid = row.parentFolderUuid;

		/**
		 * The Albums located in this Folder.
		 *
		 * **Will only be initialized after initFolder() is called!**
		 * 
		 * @type {Array.<Album>}
		 */
		this.albums = []; // will be initialized in `initFolder()` method

		/**
		 * The sub-Folders located in this Folder.
		 *
		 * **Will only be initialized after `initFolder()` is called!**
		 * 
		 * @type {Array.<Folder>}
		 */
		this.folders = [];

		if (row.children) {
			row.children.forEach(child => {
				if (child.isInTrash) return;
				
				const folder = new Folder(this.library, child);
				this.folders.push(folder);
			});	
		}
	}

	/**
	 * Initialize this folder and load Albums and sub-Folders.
	 *
	 * This method needs to be called in order for the albums and folders properties to be populated.
	 */
	async initFolder() {
		this.albums = await this.library.getAlbumsInFolder(this.uuid);

		for (const folder of Object.values(this.folders)) {
			await folder.initFolder();
		}
	}

	/**
	 * Count all the albums in this folder.
	 *
	 * This will recursively call the same function on all subfolders to get a true total.
	 * 
	 * @return {int} Total amount of Albums in the Folder (including subfolders).
	 */
	countAlbums() {
		let count = this.albums.length;

		return this.folders.reduce((total, folder) => {
			return total + folder.countAlbums();
		}, count);
	}

	/**
	 * Prepare the Folder for JSON serialization.
	 *
	 * This function is needed to exclude the library property from JSON serialization.
	 * @return {Object} The filtered object.
	 */
	toJSON() {
		return {
			...this,
			library: undefined
		}
	}
}

module.exports = Folder;