// Copyright (c) 2015, InfluxFramework LLC
// MIT License. See license.txt

influxframework.provide("influxframework.ui.form.handlers");

window.extend_cscript = (cscript, controller_object) => {
	$.extend(cscript, controller_object);
	if (cscript && controller_object) {
		cscript.__proto__ = controller_object.__proto__;
	}
	return cscript;
};

influxframework.ui.form.get_event_handler_list = function (doctype, fieldname) {
	if (!influxframework.ui.form.handlers[doctype]) {
		influxframework.ui.form.handlers[doctype] = {};
	}
	if (!influxframework.ui.form.handlers[doctype][fieldname]) {
		influxframework.ui.form.handlers[doctype][fieldname] = [];
	}
	return influxframework.ui.form.handlers[doctype][fieldname];
};

influxframework.ui.form.on = influxframework.ui.form.on_change = function (doctype, fieldname, handler) {
	var add_handler = function (fieldname, handler) {
		var handler_list = influxframework.ui.form.get_event_handler_list(doctype, fieldname);

		let _handler = (...args) => {
			try {
				return handler(...args);
			} catch (error) {
				console.error(handler);
				throw error;
			}
		};

		handler_list.push(_handler);

		// add last handler to events so it can be called as
		// frm.events.handler(frm)
		if (cur_frm && cur_frm.doctype === doctype) {
			cur_frm.events[fieldname] = _handler;
		}
	};

	if (!handler && $.isPlainObject(fieldname)) {
		// a dict of handlers {fieldname: handler, ...}
		for (var key in fieldname) {
			var fn = fieldname[key];
			if (typeof fn === "function") {
				add_handler(key, fn);
			}
		}
	} else {
		add_handler(fieldname, handler);
	}
};

// remove standard event handlers
influxframework.ui.form.off = function (doctype, fieldname, handler) {
	var handler_list = influxframework.ui.form.get_event_handler_list(doctype, fieldname);
	if (handler_list.length) {
		influxframework.ui.form.handlers[doctype][fieldname] = [];
	}

	if (cur_frm && cur_frm.doctype === doctype && cur_frm.events[fieldname]) {
		delete cur_frm.events[fieldname];
	}

	if (cur_frm && cur_frm.cscript && cur_frm.cscript[fieldname]) {
		delete cur_frm.cscript[fieldname];
	}
};

influxframework.ui.form.trigger = function (doctype, fieldname) {
	cur_frm.script_manager.trigger(fieldname, doctype);
};

influxframework.ui.form.ScriptManager = class ScriptManager {
	constructor(opts) {
		$.extend(this, opts);
	}
	make(ControllerClass) {
		this.frm.cscript = extend_cscript(
			this.frm.cscript,
			new ControllerClass({ frm: this.frm })
		);
	}
	trigger(event_name, doctype, name) {
		// trigger all the form level events that
		// are bound to this event_name
		let me = this;
		doctype = doctype || this.frm.doctype;
		name = name || this.frm.docname;

		let tasks = [];
		let handlers = this.get_handlers(event_name, doctype);

		// helper for child table
		this.frm.selected_doc = influxframework.get_doc(doctype, name);

		let runner = (_function, is_old_style) => {
			let _promise = null;
			if (is_old_style) {
				// old style arguments (doc, cdt, cdn)
				_promise = me.frm.cscript[_function](me.frm.doc, doctype, name);
			} else {
				// new style (frm, doctype, name)
				_promise = _function(me.frm, doctype, name);
			}

			// if the trigger returns a promise, return it,
			// or use the default promise influxframework.after_ajax
			if (_promise && _promise.then) {
				return _promise;
			} else {
				return influxframework.after_server_call();
			}
		};

		// make list of functions to be run serially
		handlers.new_style.forEach((_function) => {
			if (event_name === "setup") {
				// setup must be called immediately
				runner(_function, false);
			} else {
				tasks.push(() => runner(_function, false));
			}
		});

		handlers.old_style.forEach((_function) => {
			if (event_name === "setup") {
				// setup must be called immediately
				runner(_function, true);
			} else {
				tasks.push(() => runner(_function, true));
			}
		});

		// run them serially
		return influxframework.run_serially(tasks);
	}
	has_handlers(event_name, doctype) {
		let handlers = this.get_handlers(event_name, doctype);
		return handlers && (handlers.old_style.length || handlers.new_style.length);
	}
	get_handlers(event_name, doctype) {
		// returns list of all functions to be called (old style and new style)
		let me = this;
		let handlers = {
			old_style: [],
			new_style: [],
		};
		if (influxframework.ui.form.handlers[doctype] && influxframework.ui.form.handlers[doctype][event_name]) {
			$.each(influxframework.ui.form.handlers[doctype][event_name], function (i, fn) {
				handlers.new_style.push(fn);
			});
		}
		if (this.frm.cscript[event_name]) {
			handlers.old_style.push(event_name);
		}
		if (this.frm.cscript["custom_" + event_name]) {
			handlers.old_style.push("custom_" + event_name);
		}
		return handlers;
	}
	setup() {
		const doctype = this.frm.meta;
		const me = this;
		let client_script = doctype.__js;

		// append the custom script for this form's layout
		if (this.frm.doctype_layout?.client_script) {
			// add a newline to avoid conflict with doctype JS
			client_script += `\n${this.frm.doctype_layout.client_script}`;
		}

		if (client_script) {
			eval(client_script);
		}

		if (!this.frm.doctype_layout && doctype.__custom_js) {
			try {
				eval(doctype.__custom_js);
			} catch (e) {
				influxframework.msgprint({
					title: __("Error in Client Script"),
					indicator: "orange",
					message: '<pre class="small"><code>' + e.stack + "</code></pre>",
				});
			}
		}

		function setup_add_fetch(df) {
			let is_read_only_field =
				[
					"Data",
					"Read Only",
					"Text",
					"Small Text",
					"Currency",
					"Check",
					"Text Editor",
					"Attach Image",
					"Code",
					"Link",
					"Float",
					"Int",
					"Date",
					"Select",
					"Duration",
				].includes(df.fieldtype) ||
				df.read_only == 1 ||
				df.is_virtual == 1;

			if (is_read_only_field && df.fetch_from && df.fetch_from.indexOf(".") != -1) {
				var parts = df.fetch_from.split(".");
				me.frm.add_fetch(parts[0], parts[1], df.fieldname, df.parent);
			}
		}

		// setup add fetch
		$.each(this.frm.fields, function (i, field) {
			setup_add_fetch(field.df);
			if (influxframework.model.table_fields.includes(field.df.fieldtype)) {
				$.each(
					influxframework.meta.get_docfields(field.df.options, me.frm.docname),
					function (i, df) {
						setup_add_fetch(df);
					}
				);
			}
		});

		// css
		doctype.__css && influxframework.dom.set_style(doctype.__css);

		this.trigger("setup");
	}
	log_error(caller, e) {
		influxframework.show_alert({ message: __("Error in Client Script."), indicator: "error" });
		console.group && console.group();
		console.log("----- error in client script -----");
		console.log("method: " + caller);
		console.log(e);
		console.log("error message: " + e.message);
		console.trace && console.trace();
		console.log("----- end of error message -----");
		console.group && console.groupEnd();
	}
	copy_from_first_row(parentfield, current_row, fieldnames) {
		var data = this.frm.doc[parentfield];
		if (data.length === 1 || data[0] === current_row) return;

		if (typeof fieldnames === "string") {
			fieldnames = [fieldnames];
		}

		$.each(fieldnames, function (i, fieldname) {
			influxframework.model.set_value(
				current_row.doctype,
				current_row.name,
				fieldname,
				data[0][fieldname]
			);
		});
	}
};
