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

frappe.provide('frappe.views');

// Renders customized list
// usually based on `in_list_view` property

frappe.views.ListRenderer = Class.extend({
	name: 'List',
	init: function (opts) {
		$.extend(this, opts);
		this.meta = frappe.get_meta(this.doctype);

		this.init_settings();
		this.set_defaults();
		this.set_fields();
		this.set_columns();
		this.setup_cache();
	},
	set_defaults: function () {
		var me = this;
		this.page_title = __(this.doctype);

		this.set_wrapper();
		this.prepare_render_view();

		// flag to enable/disable realtime updates in list_view
		this.no_realtime = false;

		// set false to render view even if no results
		// e.g Calendar
		this.show_no_result = true;

		// hide sort selector
		this.hide_sort_selector = false;

		// default settings
		this.order_by = this.order_by || 'modified desc';
		this.filters = this.filters || [];
		this.page_length = this.page_length || 20;
	},
	setup_cache: function () {
		frappe.provide('frappe.views.list_renderers.' + this.doctype);
		frappe.views.list_renderers[this.doctype][this.list_view.current_view] = this;
	},
	init_settings: function () {
		this.settings = frappe.listview_settings[this.doctype] || {};
		this.init_user_settings();

		this.order_by = this.user_settings.order_by || this.settings.order_by;
		this.filters = this.user_settings.filters || this.settings.filters;
		this.page_length = this.settings.page_length;

		// default filter for submittable doctype
		if(frappe.model.is_submittable(this.doctype) && (!this.filters || !this.filters.length)) {
			this.filters = [[this.doctype, "docstatus", "!=", 2]];
		}
	},
	init_user_settings: function () {
		frappe.provide('frappe.model.user_settings.' + this.doctype + '.' + this.name);
		this.user_settings = frappe.get_user_settings(this.doctype)[this.name];
	},
	after_refresh: function() {
		// called after refresh in list_view
	},
	before_refresh: function() {
		// called before refresh in list_view
	},
	should_refresh: function() {
		return this.list_view.current_view !== this.list_view.last_view;
	},
	set_wrapper: function () {
		this.wrapper = this.list_view.wrapper && this.list_view.wrapper.find('.result-list');
	},
	set_fields: function () {
		var me = this;
		var tabDoctype = '`tab' + this.doctype + '`.';
		this.fields = [];
		this.stats = ['_user_tags'];

		var add_field = function (fieldname) {
			if (!fieldname.includes('`tab')) {
				fieldname = tabDoctype + '`' + fieldname + '`';
			}
			if (!me.fields.includes(fieldname))
				me.fields.push(fieldname);
		}

		var defaults = [
			'name',
			'owner',
			'docstatus',
			'_user_tags',
			'_comments',
			'modified',
			'modified_by',
			'_assign',
			'_liked_by',
			'_seen'
		];
		defaults.map(add_field);

		// add title field
		if (this.meta.title_field) {
			this.title_field = this.meta.title_field;
			add_field(this.meta.title_field);
		}

		// enabled / disabled
		if (frappe.meta.has_field(this.doctype, 'enabled')) { add_field('enabled'); }
		if (frappe.meta.has_field(this.doctype, 'disabled')) { add_field('disabled'); }

		// add workflow field (as priority)
		this.workflow_state_fieldname = frappe.workflow.get_state_fieldname(this.doctype);
		if (this.workflow_state_fieldname) {
			if (!frappe.workflow.workflows[this.doctype]['override_status']) {
				add_field(this.workflow_state_fieldname);
			}
			this.stats.push(this.workflow_state_fieldname);
		}

		this.meta.fields.forEach(function (df, i) {
			if (df.in_list_view && frappe.perm.has_perm(me.doctype, df.permlevel, 'read')) {
				if (df.fieldtype == 'Image' && df.options) {
					add_field(df.options);
				} else {
					add_field(df.fieldname);
				}
				// currency field for symbol (multi-currency)
				if (df.fieldtype == 'Currency' && df.options) {
					if (df.options.includes(':')) {
						add_field(df.options.split(':')[1]);
					} else {
						add_field(df.options);
					}
				}
			}
		});

		// additional fields
		if (this.settings.add_fields) {
			this.settings.add_fields.forEach(add_field);
		}
		// kanban column fields
		if (me.meta.__kanban_column_fields) {
			me.meta.__kanban_column_fields.map(add_field);
		}
	},
	set_columns: function () {
		var me = this;
		this.columns = [];
		var name_column = {
			colspan: this.settings.colwidths && this.settings.colwidths.subject || 6,
			type: 'Subject',
			title: 'Name'
		};
		if (this.meta.title_field) {
			name_column.title = frappe.meta.get_docfield(this.doctype, this.meta.title_field).label;
		}
		this.columns.push(name_column);

		if (frappe.has_indicator(this.doctype)) {
			// indicator
			this.columns.push({
				colspan: this.settings.colwidths && this.settings.colwidths.indicator || 3,
				type: 'Indicator',
				title: 'Status'
			});
		}

		// total_colspans
		this.total_colspans = this.columns.reduce(function (total, curr) {
			return total + curr.colspan;
		}, 0);

		// overridden
		var overridden = (this.settings.add_columns || []).map(function (d) {
			return d.content;
		});

		// custom fields in list_view
		var docfields_in_list_view =
			frappe.get_children('DocType', this.doctype, 'fields', { 'in_list_view': 1 })
				.sort(function (a, b) {
					return a.idx - b.idx
				});

		docfields_in_list_view.forEach(function (d) {
			if (overridden.includes(d.fieldname) || d.fieldname === me.title_field) {
				return;
			}
			if (me.total_colspans < 12) {
				me.add_column(d);
			}
		});

		// additional columns
		if (this.settings.add_columns) {
			this.settings.add_columns.forEach(function (d) {
				if (me.total_colspans < 12) {
					if (typeof d === 'string') {
						me.add_column(frappe.meta.get_docfield(me.doctype, d));
					} else {
						me.columns.push(d);
						me.total_colspans += parseInt(d.colspan);
					}
				}
			});
		}


		// distribute remaining columns
		var empty_cols = flt(12 - this.total_colspans);
		while (empty_cols > 0) {
			this.columns = this.columns.map(function (col) {
				if (empty_cols > 0) {
					col.colspan = cint(col.colspan) + 1;
					empty_cols = empty_cols - 1;
				}
				return col;
			});
		}

		// Remove duplicates
		this.columns = this.columns.uniqBy(col => col.title);

		// Remove TextEditor field columns
		this.columns = this.columns.filter(col => col.fieldtype !== 'Text Editor')

		// Limit number of columns to 4
		this.columns = this.columns.slice(0, 4);
	},
	add_column: function (df) {
		// field width
		var colspan = 3;
		if (in_list(['Int', 'Percent'], df.fieldtype)) {
			colspan = 2;
		} else if (in_list(['Check', 'Image'], df.fieldtype)) {
			colspan = 1;
		} else if (in_list(['name', 'subject', 'title'], df.fieldname)) {
			// subjects are longer
			colspan = 4;
		} else if (df.fieldtype == 'Text Editor' || df.fieldtype == 'Text') {
			colspan = 4;
		}

		if (df.columns && df.columns > 0) {
			colspan = df.columns;
		} else if (this.settings.column_colspan && this.settings.column_colspan[df.fieldname]) {
			colspan = this.settings.column_colspan[df.fieldname];
		} else {
			colspan = 2;
		}
		this.total_colspans += parseInt(colspan);
		var col = {
			colspan: colspan,
			content: df.fieldname,
			type: df.fieldtype,
			df: df,
			fieldtype: df.fieldtype,
			fieldname: df.fieldname,
			title: __(df.label)
		};
		if (this.settings.column_render && this.settings.column_render[df.fieldname]) {
			col.render = this.settings.column_render[df.fieldname];
		}
		this.columns.push(col);
	},

	setup_filterable: function () {
		var me = this;
		this.wrapper.on('click', '.filterable', function (e) {
			var filters = $(this).attr('data-filter').split('|');
			var added = false;

			filters.forEach(function (f) {
				f = f.split(',');
				if (f[2] === 'Today') {
					f[2] = frappe.datetime.get_today();
				} else if (f[2] == 'User') {
					f[2] = frappe.session.user;
				}
				var new_filter = me.list_view.filter_list
					.add_filter(me.doctype, f[0], f[1], f.slice(2).join(','));

				if (new_filter) {
					// set it to true if atleast 1 filter is added
					added = true;
				}
			});
			if (added) {
				me.list_view.refresh(true);
			}
		});
		this.wrapper.on('click', '.list-item', function (e) {
			// don't open in case of checkbox, like, filterable
			if ($(e.target).hasClass('filterable')
				|| $(e.target).hasClass('octicon-heart')
				|| $(e.target).is(':checkbox')) {
				return;
			}

			var link = $(this).parent().find('a.list-id').get(0);
			window.location.href = link.href;
			return false;
		});
	},

	render_view: function (values) {
		var me = this;
		var $list_items = $(`
			<div class="list-items">
			</div>
		`);
		me.wrapper.append($list_items);

		values.map(value => {
			const $item = $(this.get_item_html(value));
			const $item_container = $('<div class="list-item-container">').append($item);

			$list_items.append($item_container);

			if (this.settings.post_render_item) {
				this.settings.post_render_item(this, $item_container, value);
			}

			this.render_tags($item_container, value);
		});

		this.setup_filterable();
	},

	// returns html for a data item,
	// usually based on a template
	get_item_html: function (data) {
		var main = this.columns.map(column =>
			frappe.render_template('list_item_main', {
				data: data,
				col: column,
				value: data[column.fieldname],
				formatters: this.settings.formatters,
				subject: this.get_subject_html(data, true),
				indicator: this.get_indicator_html(data),
			})
		).join("");

		return frappe.render_template('list_item_row', {
			data: data,
			main: main,
			settings: this.settings,
			meta: this.meta,
			indicator_dot: this.get_indicator_dot(data),
		})
	},

	get_header_html: function () {
		var main = this.columns.map(column =>
			frappe.render_template('list_item_main_head', {
				col: column,
				_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable)
					&& !this.no_delete)
			})
		).join("");

		return frappe.render_template('list_item_row_head', { main: main, list: this });
	},

	render_tags: function (element, data) {
		var me = this;
		var tag_row = $(`<div class='tag-row'>
			<div class='list-tag hidden-xs'></div>
			<div class='clearfix'></div>
		</div>`).appendTo(element);

		if (!me.list_view.tags_shown) {
			tag_row.addClass('hide');
		}

		// add tags
		var tag_editor = new frappe.ui.TagEditor({
			parent: tag_row.find('.list-tag'),
			frm: {
				doctype: this.doctype,
				docname: data.name
			},
			list_sidebar: me.list_view.list_sidebar,
			user_tags: data._user_tags,
			on_change: function (user_tags) {
				data._user_tags = user_tags;
			}
		});
		tag_editor.wrapper.on('click', '.tagit-label', function () {
			me.list_view.set_filter('_user_tags', $(this).text());
		});
	},

	get_subject_html: function (data, without_workflow) {
		data._without_workflow = without_workflow;
		return frappe.render_template('list_item_subject', data);
	},

	get_indicator_html: function (doc) {
		var indicator = frappe.get_indicator(doc, this.doctype);
		if (indicator) {
			return `<span class='indicator ${indicator[1]} filterable'
				data-filter='${indicator[2]}'>
				${__(indicator[0])}
			<span>`;
		}
		return '';
	},

	get_indicator_dot: function (doc) {
		var indicator = frappe.get_indicator(doc, this.doctype);
		if (!indicator) {
			return '';
		}
		return `<span class='indicator ${indicator[1]}' title='${__(indicator[0])}'></span>`;
	},

	prepare_data: function (data) {
		if (data.modified)
			this.prepare_when(data, data.modified);

		// nulls as strings
		for (var key in data) {
			if (data[key] == null) {
				data[key] = '';
			}
		}

		data.doctype = this.doctype;
		data._liked_by = JSON.parse(data._liked_by || '[]');
		data._checkbox = (frappe.model.can_delete(this.doctype) || this.settings.selectable) && !this.no_delete

		data._doctype_encoded = encodeURIComponent(data.doctype);
		data._name = data.name.replace(/'/g, '\'');
		data._name_encoded = encodeURIComponent(data.name);
		data._submittable = frappe.model.is_submittable(this.doctype);

		var title_field = this.meta.title_field || 'name';
		data._title = strip_html(data[title_field] || data.name);
		data._full_title = data._title;


		data._workflow = null;
		if (this.workflow_state_fieldname) {
			data._workflow = {
				fieldname: this.workflow_state_fieldname,
				value: data[this.workflow_state_fieldname],
				style: frappe.utils.guess_style(data[this.workflow_state_fieldname])
			}
		}

		data._user = frappe.session.user;

		if(!data._user_tags) data._user_tags = "";

		data._tags = data._user_tags.split(',').filter(function (v) {
			// filter falsy values
			return v;
		});

		data.css_seen = '';
		if (data._seen) {
			var seen = JSON.parse(data._seen);
			if (seen && in_list(seen, data._user)) {
				data.css_seen = 'seen'
			}
		}

		// whether to hide likes/comments/assignees
		data._hide_activity = 0;

		data._assign_list = JSON.parse(data._assign || '[]');

		// prepare data in settings
		if (this.settings.prepare_data)
			this.settings.prepare_data(data);

		return data;
	},

	prepare_when: function (data, date_str) {
		if (!date_str) date_str = data.modified;
		// when
		data.when = (frappe.datetime.str_to_user(date_str)).split(' ')[0];
		var diff = frappe.datetime.get_diff(frappe.datetime.get_today(), date_str.split(' ')[0]);
		if (diff === 0) {
			data.when = comment_when(date_str);
		}
		if (diff === 1) {
			data.when = __('Yesterday')
		}
		if (diff === 2) {
			data.when = __('2 days ago')
		}
	},

	// for views which require 3rd party libs
	required_libs: null,

	prepare_render_view: function () {
		var me = this;
		this._render_view = this.render_view;

		var lib_exists = (typeof this.required_libs === 'string' && this.required_libs)
			|| ($.isArray(this.required_libs) && this.required_libs.length);

		this.render_view = function (values) {
			// prepare data before rendering view
			values = values.map(me.prepare_data.bind(this));
			// remove duplicates
			values = values.uniqBy(value => value.name);

			if (lib_exists) {
				me.load_lib(function () {
					me._render_view(values);
				});
			} else {
				me._render_view(values);
			}
		}.bind(this);
	},

	load_lib: function (callback) {
		frappe.require(this.required_libs, callback);
	},

	render_bar_graph: function (parent, data, field, label) {
		var args = {
			percent: data[field],
			label: __(label)
		}
		$(parent).append(`<span class='progress' style='width: 100 %; float: left; margin: 5px 0px;'> \
			<span class='progress- bar' title='${args.percent}% ${args.label}' \
				style='width: ${args.percent}%;'></span>\
		</span>`);
	},
	render_icon: function (parent, icon_class, label) {
		var icon_html = `<i class='${icon_class}' title='${__(label) || ''}'></i>`;
		$(parent).append(icon_html);
	},
	make_no_result: function () {
		var new_button = frappe.boot.user.can_create.includes(this.doctype)
			? (`<p><button class='btn btn-primary btn-sm'
				list_view_doc='${this.doctype}'>
					${__('Make a new {0}', [__(this.doctype)])}
				</button></p>`)
			: '';
		var no_result_message =
			`<div class='msg-box no-border'>
				<p>${__('No {0} found', [__(this.doctype)])}</p>
				${new_button}
			</div>`;

		return no_result_message;
	},
});
