Source: scripts/handle/transact.js

const {getCurrentTimestamp, createUniqueTimestamp} = require(__dirname + '/../utils/dateHandler.js');

/**
 * Handles (non recurring) transactions.
 */
class Transact {
	/**
	 * @param {Storage} storage Storage object.
	 */
	constructor(storage) {
		this.storage = storage;
	}

	/**
	 * Stores a new earning entry in the corresponding data file.
	 * 
	 * @param {object} earningObj The entry to add.
	 * @param {bool} allocationOn Specifies whether the entry
	 * should be spread across multiple budgets.
	 */
	addEarning(earningObj, allocationOn) {
		earningObj.date = createUniqueTimestamp(earningObj.date, this.storage);

		if (allocationOn) {
			this.addEarningSplit(earningObj);
		} else {
			this.addEarningSingle(earningObj);
		}
	}

	/**
	 * Stores a new earning entry for a single budget.
	 * 
	 * @param {object} earningObj The entry to add.
	 */
	addEarningSingle(earningObj) {
		this.storage.storeData(earningObj);

		this.updateMainStorageArr('budgets', earningObj.budget, earningObj.amount);
		this.updateMainStorageArr('allTimeEarnings', earningObj.budget, earningObj.amount);	
	}

	/**
	 * Stores a new earning entry which is spread across multiple budgets.
	 * 
	 * @param {object} earningObj The entry to add.
	 */
	addEarningSplit(earningObj) {
		let allocation = this.storage.readMainStorage('allocation');

		let totalAmount = 0; // To avoid rounding deviations.
		allocation.forEach((budget, index) => { // budget = [name, percentage]
			if (budget[1] > 0) { // Only handle entries with percentage > 0.
				let objCopy = Object.assign({}, earningObj);
				objCopy.amount = Math.round((objCopy.amount * budget[1] / 100) * 100) / 100;
				objCopy.budget = budget[0];
				totalAmount += objCopy.amount;

				if (index === allocation.length - 1) { // Correct deviations in the last iteration.
					objCopy.amount += (earningObj.amount - totalAmount);
				}

				this.addEarningSingle(objCopy);
			}
		});
	}

	/**
	 * Stores a new spending entry in the corresponding data file.
	 * 
	 * @param {object} spendingObj The entry to add.
	 */
	addSpending(spendingObj) {
		spendingObj.date = createUniqueTimestamp(spendingObj.date, this.storage);

		this.storage.storeData(spendingObj);

		this.updateMainStorageArr('budgets', spendingObj.budget, -spendingObj.amount);
		this.updateMainStorageArr('allTimeSpendings', spendingObj.budget, spendingObj.amount);
	}

	/**
	 * Transfers money from one budget to another by adding transfer entries for them.
	 * 
	 * @param {string} from The budget from which money should be transferred.
	 * @param {string} to The budget to which money should be transferred.
	 * @param {number} amount The amount which should be transferred.
	 */
	addTransferEntries(from, to, amount) {
		let obj = {
			date: createUniqueTimestamp(getCurrentTimestamp(), this.storage),
			name: 'Transfer',
			amount: amount,
			category: 'Transfer'
		};

		this.addEarningSingle(Object.assign({type: 'earning', budget: to}, obj));
		this.addSpending(Object.assign({type: 'spending', budget: from}, obj));
	}

	/**
	 * Updates an array in the mainstorage, i.e., reads the value for a given budget and adds a
	 * given number to it.
	 * 
	 * @param {string} arrayKey The field of the mainstorage to update
	 * (e.g. budgets, allTimeEarnings, allTimeSpendings).
	 * @param {string} budgetName The name of the budget which should be updated.
	 * @param {number} valueToAdd The number to add.
	 */
	updateMainStorageArr(arrayKey, budgetName, valueToAdd) {
		let array = this.storage.readMainStorage(arrayKey);

		array.forEach((budget, index) => { // budget = [name, amount]
			if (budget[0] === budgetName) {
				array[index][1] = Math.round((parseFloat(array[index][1]) + parseFloat(valueToAdd)) * 100) / 100;
			}
		});

		this.storage.writeMainStorage(arrayKey, array);
	}
}

module.exports = Transact;