// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

// new re-re-factored Listing object
// now called BaseList
//
// opts:
//   parent

//   method (method to call on server)
//   args (additional args to method)
//   get_args (method to return args as dict)

//   show_filters [false]
//   doctype
//   filter_fields (if given, this list is rendered, else built from doctype)

//   query or get_query (will be deprecated)
//   query_max
//   buttons_in_frame

//   no_result_message ("No result")

//   page_length (20)
//   hide_refresh (False)
//   no_toolbar
//   new_doctype
//   [function] render_row(parent, data)
//   [function] onrun
//   no_loading (no ajax indicator)

frappe.provide('frappe.ui');

frappe.ui.BaseList = Class.extend({
	init: function (opts) {
		this.opts = opts || {};
		this.set_defaults();
		if (opts) {
			this.make();
		}
	},
	set_defaults: function () {
		this.page_length = 20;
		this.start = 0;
		this.data = [];
	},
	make: function (opts) {
		if (opts) {
			this.opts = opts;
		}
		this.prepare_opts();

		$.extend(this, this.opts);

		// make dom
		this.wrapper = $(frappe.render_template('listing', this.opts));
		this.parent.append(this.wrapper);

		this.set_events();

		if (this.page) {
			this.wrapper.find('.list-toolbar-wrapper').hide();
		}

		if (this.show_filters) {
			this.make_filters();
		}
	},
	prepare_opts: function () {
		if (this.opts.new_doctype) {
			if (!frappe.boot.user.can_create.includes(this.opts.new_doctype)) {
				this.opts.new_doctype = null;
			}
		}
		if (!this.opts.no_result_message) {
			this.opts.no_result_message = __('Nothing to show');
		}
		if (!this.opts.page_length) {
			this.opts.page_length = this.user_settings && this.user_settings.limit || 20;
		}
		this.opts._more = __('More');
	},
	add_button: function (label, click, icon) {
		if (this.page) {
			return this.page.add_menu_item(label, click, icon)
		} else {
			this.wrapper.find('.list-toolbar-wrapper').removeClass('hide');
			return $('<button class="btn btn-default"></button>')
				.appendTo(this.wrapper.find('.list-toolbar'))
				.html((icon ? ('<i class="' + icon + '"></i> ') : '') + label)
				.click(click);
		}
	},
	set_events: function () {
		var me = this;

		// next page
		this.wrapper.find('.btn-more').click(function () {
			me.run(true);
		});

		this.wrapper.find(".btn-group-paging").on('click', '.btn', function () {
			me.page_length = cint($(this).attr("data-value"));

			me.wrapper.find(".btn-group-paging .btn-info").removeClass("btn-info");
			$(this).addClass("btn-info");

			// always reset when changing list page length
			me.run();
		});

		// select the correct page length
		if (this.opts.page_length !== 20) {
			this.wrapper.find(".btn-group-paging .btn-info").removeClass("btn-info");
			this.wrapper
				.find(".btn-group-paging .btn[data-value='" + this.opts.page_length + "']")
				.addClass('btn-info');
		}

		// title
		if (this.title) {
			this.wrapper.find('h3').html(this.title).show();
		}

		// new
		this.set_primary_action();

		if (me.no_toolbar || me.hide_toolbar) {
			me.wrapper.find('.list-toolbar-wrapper').hide();
		}
	},

	set_primary_action: function () {
		var me = this;
		if (this.new_doctype) {
			this.page.set_primary_action(
				__("New"),
				me.make_new_doc.bind(me, me.new_doctype),
				"octicon octicon-plus"
			);
		} else {
			this.page.clear_primary_action();
		}
	},

	make_new_doc: function (doctype) {
		var me = this;
		frappe.model.with_doctype(doctype, function () {
			if (me.custom_new_doc) {
				me.custom_new_doc(doctype);
			} else {
				if (me.filter_list) {
					frappe.route_options = {};
					me.filter_list.get_filters().forEach(function (f, i) {
						if (f[2] === "=" && !frappe.model.std_fields_list.includes(f[1])) {
							frappe.route_options[f[1]] = f[3];
						}
					});
				}
				frappe.new_doc(doctype, true);
			}
		});
	},

	make_filters: function () {
		this.filter_list = new frappe.ui.FilterList({
			base_list: this,
			parent: this.wrapper.find('.list-filters').show(),
			doctype: this.doctype,
			filter_fields: this.filter_fields,
			default_filters: this.default_filters || []
		});
		// default filter for submittable doctype
		if (frappe.model.is_submittable(this.doctype)) {
			this.filter_list.add_filter(this.doctype, "docstatus", "!=", 2);
		}
	},

	clear: function () {
		this.data = [];
		this.wrapper.find('.result-list').empty();
		this.wrapper.find('.result').show();
		this.wrapper.find('.no-result').hide();
		this.start = 0;
		this.onreset && this.onreset();
	},

	set_filters_from_route_options: function ({clear_filters=true} = {}) {
		var me = this;
		if(clear_filters) {
			this.filter_list.clear_filters();
		}

		for(var field in frappe.route_options) {
			var value = frappe.route_options[field];
			var doctype = null;

			// if `Child DocType.fieldname`
			if (field.includes(".")) {
				doctype = field.split(".")[0];
				field = field.split(".")[1];
			}

			// find the table in which the key exists
			// for example the filter could be {"item_code": "X"}
			// where item_code is in the child table.

			// we can search all tables for mapping the doctype
			if (!doctype) {
				doctype = frappe.meta.get_doctype_for_field(me.doctype, field);
			}

			if (doctype) {
				if ($.isArray(value)) {
					me.filter_list.add_filter(doctype, field, value[0], value[1]);
				} else {
					me.filter_list.add_filter(doctype, field, "=", value);
				}
			}
		}
		frappe.route_options = null;
	},

	run: function (more) {
		var me = this;
		if (!more) {
			this.start = 0;
			this.onreset && this.onreset();
		}

		var args = this.get_call_args();
		this.save_user_settings_locally(args);

		// user_settings are saved by db_query.py when dirty
		$.extend(args, {
			user_settings: frappe.model.user_settings[this.doctype]
		});

		return frappe.call({
			method: this.opts.method || 'frappe.desk.query_builder.runquery',
			type: "GET",
			freeze: this.opts.freeze !== undefined ? this.opts.freeze : true,
			args: args,
			callback: function (r) {
				me.dirty = false;
				me.render_results(r);
			},
			no_spinner: this.opts.no_loading
		});
	},
	save_user_settings_locally: function (args) {
		if (this.opts.save_user_settings && this.doctype && !this.docname) {
			// save list settings locally
			var user_settings = frappe.model.user_settings[this.doctype];
			var different = false;

			if (!user_settings) {
				return;
			}

			if (!frappe.utils.arrays_equal(args.filters, user_settings.filters)) {
				// settings are dirty if filters change
				user_settings.filters = args.filters;
				different = true;
			}

			if (user_settings.order_by !== args.order_by) {
				user_settings.order_by = args.order_by;
				different = true;
			}

			if (user_settings.limit !== args.limit_page_length) {
				user_settings.limit = args.limit_page_length || 20
				different = true;
			}

			// save fields in list settings
			if (args.save_user_settings_fields) {
				user_settings.fields = args.fields;
			}

			if (different) {
				user_settings.updated_on = moment().toString();
			}
		}
	},
	get_call_args: function () {
		// load query
		if (!this.method) {
			var query = this.get_query && this.get_query() || this.query;
			query = this.add_limits(query);
			var args = {
				query_max: this.query_max,
				as_dict: 1
			}
			args.simple_query = query;
		} else {
			var args = {
				start: this.start,
				page_length: this.page_length
			}
		}

		// append user-defined arguments
		if (this.args)
			$.extend(args, this.args)

		if (this.get_args) {
			$.extend(args, this.get_args());
		}
		return args;
	},
	render_results: function (r) {
		if (this.start === 0)
			this.clear();

		this.wrapper.find('.btn-more, .list-loading').hide();

		var values = [];

		if (r.message) {
			values = this.get_values_from_response(r.message);
		}

		var show_results = true;
		if(this.show_no_result) {
			if($.isFunction(this.show_no_result)) {
				show_results = !this.show_no_result()
			} else {
				show_results = !this.show_no_result;
			}
		}

		// render result view when
		// length > 0 OR
		// explicitly set by flag
		if (values.length || show_results) {
			this.data = this.data.concat(values);
			this.render_view(values);
			this.update_paging(values);
		} else if (this.start === 0) {
			// show no result message
			this.wrapper.find('.result').hide();

			var msg = '';
			var no_result_message = this.no_result_message;
			if(no_result_message && $.isFunction(no_result_message)) {
				msg = no_result_message();
			} else if(typeof no_result_message === 'string') {
				msg = no_result_message;
			} else {
				msg = __('No Results')
			}

			this.wrapper.find('.no-result').html(msg).show();
		}

		this.wrapper.find('.list-paging-area')
			.toggle(values.length > 0|| this.start > 0);

		// callbacks
		if (this.onrun) this.onrun();
		if (this.callback) this.callback(r);
		this.wrapper.trigger("render-complete");
	},

	get_values_from_response: function (data) {
		// make dictionaries from keys and values
		if (data.keys && $.isArray(data.keys)) {
			return frappe.utils.dict(data.keys, data.values);
		} else {
			return data;
		}
	},

	render_view: function (values) {
		// override this method in derived class
	},

	update_paging: function (values) {
		if (values.length >= this.page_length) {
			this.wrapper.find('.btn-more').show();
			this.start += this.page_length;
		}
	},

	refresh: function () {
		this.run();
	},
	add_limits: function (query) {
		return query + ' LIMIT ' + this.start + ',' + (this.page_length + 1);
	},
	set_filter: function (fieldname, label, no_run, no_duplicate) {
		var filter = this.filter_list.get_filter(fieldname);
		if (filter) {
			var value = cstr(filter.field.get_parsed_value());
			if (value.includes(label)) {
				// already set
				return false

			} else if (no_duplicate) {
				filter.set_values(this.doctype, fieldname, "=", label);
			} else {
				// second filter set for this field
				if (fieldname == '_user_tags' || fieldname == "_liked_by") {
					// and for tags
					this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
				} else {
					// or for rest using "in"
					filter.set_values(this.doctype, fieldname, 'in', value + ', ' + label);
				}
			}
		} else {
			// no filter for this item,
			// setup one
			if (['_user_tags', '_comments', '_assign', '_liked_by'].includes(fieldname)) {
				this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
			} else {
				this.filter_list.add_filter(this.doctype, fieldname, '=', label);
			}
		}
		if (!no_run)
			this.run();
	},
	init_user_settings: function () {
		this.user_settings = frappe.model.user_settings[this.doctype] || {};
	},
	call_for_selected_items: function (method, args) {
		var me = this;
		args.names = this.get_checked_items().map(function (item) {
			return item.name;
		});

		frappe.call({
			method: method,
			args: args,
			freeze: true,
			callback: function (r) {
				if (!r.exc) {
					if (me.list_header) {
						me.list_header.find(".list-select-all").prop("checked", false);
					}
					me.refresh();
				}
			}
		});
	}
});