/**
 * This is the base class for all reports.
 */
class Report {
	/**
	 * Returns an object containing objects with the friendly field names and other properties.
	 * Field keys not found in this object will be displayed directly to the user.
	 */
	get fieldInformation() {
		return { };
	}

	/**
	 * Returns an object with the default field information that will apply if no specific rules are set for a column in the field information object.
	 */
	get defaultFieldInformation() {
		return {
			displayName: "unnamed",
			type: "string"
		};
	}

	/**
	 * Returns an array containing filter groups.
	 * The groups are just for optical purposes. All filters of all groups will be treated as AND conditions.
	 * Each group is an object containing the filter names as keys with filter objects as values.
	 * Filter objects look like this:
	 * invoiced: {
	 *     type: "select",
	 *     default: "all",
	 *     label: "Show",
	 *     values: {
	 *         all: "All",
	 *         invoiced_only: "Invoiced only",
	 *         not_invoiced_only: "Not yet invoiced only"
	 *     }
	 * }
	 * The allowed types are: "select", "date", "checkbox"
	 */
	get filterParameters() {
		return [ ];
	}

	/**
	 * Returns the request path of this report. It is relative to the application's request path.
	 */
	get requestPath() {
		return "";
	}

	/**
	 * Returns the friendly name of this report.
	 */
	get name() {
		return "Unnamed report";
	}

	constructor() {
		//Construct the empty data array.
		var titleRow = Object.keys(this.fieldInformation);
		this.data = [ titleRow ];
	}

	/**
	 * Returns the matching field name for the given field key.
	 * Will return the field key itself if no matching friendly name was found.
	 */
	getFieldName(key) {
		if (key in this.fieldNames) {
			return this.fieldNames[key].displayName;
		}

		return key;
	}

	/**
	 * Constructs the table title cell with the given field name and field information.
	 * The element that is passed as the third parameter is the cell element that may be filled to the developer's heart's content.
	 */
	buildTitleCell(fieldName, fieldInformation, cell) {
		cell.innerText = fieldInformation.displayName;
		cell.style.textAlign = this.getTextAlignForFieldType(fieldInformation.type);

		if (this.getTextOrientationForField(fieldName, fieldInformation) == "vertical") {
			cell.style.textAlign = "center";

			var labelDiv = document.createElement("div");
			labelDiv.innerText = cell.innerText;
			labelDiv.style.writingMode = "vertical-rl";
			labelDiv.style.lineBreak = "strict";
			labelDiv.style.marginLeft = "-0.3em";
			labelDiv.style.display = "inline-block";
			labelDiv.style.verticalAlign = "text-bottom";
			cell.innerText = "";
			cell.appendChild(labelDiv);
		}
	}

	/**
	 * Constructs the table cell with the given field name, value and field information.
	 * The element that is passed as the fourth parameter is the cell element that may be filled to the developer's heart's content.
	 * The index parameter is the current row index of the cell. Notice that 0 is the header row and 1 is the actual first row.
	 */
	buildCell(fieldName, value, fieldInformation, cell, index) {
		cell.innerText = value;
		cell.style.textAlign = this.getTextAlignForFieldType(fieldInformation.type);
	}

	/**
	 * Generates the raw text content with the given field name, value and field information.
	 * Returns the raw value by default. This behaviour may be overridden by other reports.
	 */
	buildCSVCell(fieldName, value, fieldInformation, index) {
		return value;
	}

	/**
	 * Constructs the table cell with the given value for use in the aggregation row.
	 */
	buildAggregationCell(fieldName, value, fieldInformation, cell) {
		cell.innerText = value;
		cell.style.textAlign = this.getTextAlignForFieldType(fieldInformation.type);
	}

	/**
	 * Returns the appropriate text alignment for the given field type as a string ("left", "right", "center").
	 * The default alignment is "left".
	 */
	getTextAlignForFieldType(type) {
		if (type == "boolean") {
			return "center";
		}
		else if ([ "number", "currency", "time" ].indexOf(type) != -1) {
			return "right";
		}

		return "left";
	}

	/**
	 * Returns either "horizontal" or "vertical", depending on how the given field should be displayed.
	 */
	getTextOrientationForField(fieldName, fieldInformation) {
		return fieldInformation.orientation ? fieldInformation.orientation : "horizontal";
	}

	/**
	 * Returns the field information for the column with the given field name.
	 */
	getFieldInformation(fieldName) {
		//Get the default field information that is used when no specific rules are defined.
		var fieldInformation = this.defaultFieldInformation;

		//Use the raw field name as the default display name.
		fieldInformation.displayName = fieldName;

		//Override the default field information with the custom rules, if applicable.
		if (fieldName in this.fieldInformation) {
			var customFieldInformation = this.fieldInformation[fieldName];

			for (var property in customFieldInformation) {
				fieldInformation[property] = customFieldInformation[property];
			}
		}

		return fieldInformation;
	}
}