// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridReport {
	get_item_warehouse(warehouse, item) {
		if (!this.item_warehouse[item]) this.item_warehouse[item] = {};
		if (!this.item_warehouse[item][warehouse])
			this.item_warehouse[item][warehouse] = {
				balance_qty: 0.0,
				balance_value: 0.0,
				fifo_stack: [],
			};
		return this.item_warehouse[item][warehouse];
	}

	get_value_diff(wh, sl, is_fifo) {
		// value
		let value_diff = 0;

		if (sl.qty > 0) {
			// incoming - rate is given
			let rate = sl.incoming_rate;
			let add_qty = sl.qty;
			if (wh.balance_qty < 0) {
				// negative valuation
				// only add value of quantity if
				// the balance goes above 0
				add_qty = wh.balance_qty + sl.qty;
				if (add_qty < 0) {
					add_qty = 0;
				}
			}

			if (sl.serial_no) {
				value_diff = this.get_serialized_value_diff(sl);
			} else {
				value_diff = rate * add_qty;
			}

			if (add_qty) wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
		} else {
			// called everytime for maintaining fifo stack
			var fifo_value_diff = this.get_fifo_value_diff(wh, sl);

			// outgoing
			if (sl.serial_no) {
				value_diff = -1 * this.get_serialized_value_diff(sl);
			} else if (is_fifo) {
				value_diff = fifo_value_diff;
			} else {
				// average rate for weighted average
				let rate = wh.balance_qty.toFixed(2) == 0.0 ? 0 : flt(wh.balance_value) / flt(wh.balance_qty);

				// no change in value if negative qty
				if ((wh.balance_qty + sl.qty).toFixed(2) >= 0.0) value_diff = rate * sl.qty;
				else value_diff = -wh.balance_value;
			}
		}

		// update balance (only needed in case of valuation)
		wh.balance_qty += sl.qty;
		wh.balance_value += value_diff;
		return value_diff;
	}
	get_fifo_value_diff(wh, sl) {
		// get exact rate from fifo stack
		var fifo_stack = (wh.fifo_stack || []).reverse();
		var fifo_value_diff = 0.0;
		var qty = -sl.qty;

		for (var i = 0, j = fifo_stack.length; i < j; i++) {
			var batch = fifo_stack.pop();
			if (batch[0] >= qty) {
				batch[0] = batch[0] - qty;
				fifo_value_diff += qty * batch[1];

				qty = 0.0;
				if (batch[0]) {
					// batch still has qty put it back
					fifo_stack.push(batch);
				}

				// all qty found
				break;
			} else {
				// consume this batch fully
				fifo_value_diff += batch[0] * batch[1];
				qty = qty - batch[0];
			}
		}
		// reset the updated stack
		wh.fifo_stack = fifo_stack.reverse();
		return -fifo_value_diff;
	}

	get_serialized_value_diff(sl) {
		var me = this;

		var value_diff = 0.0;

		$.each(sl.serial_no.trim().split("\n"), function (i, sr) {
			if (sr) {
				value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]);
			}
		});

		return value_diff;
	}

	get_serialized_buying_rates() {
		var serialized_buying_rates = {};

		if (frappe.report_dump.data["Serial No"]) {
			$.each(frappe.report_dump.data["Serial No"], function (i, sn) {
				serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate);
			});
		}

		return serialized_buying_rates;
	}
};
