define(['jquery', 'underscore', '../../UI/ai/image/image-preview.tmpl', '../../UI/ai/image/loading-square.tmpl'], function ($, _, image_preview_template, loading_square_template) {
	var STATE_INITIAL = 'input';
	var STATE_LOADING = 'loading';
	var STATE_PREVIEW = 'preview';
	var STATE_ERROR   = 'error';

	var COLUMNS_PER_ROW = 3;

	var PREVIEW_IMAGE_CLASS        = 'image-preview';
	var PREVIEW_IMAGE_SELECT_CLASS = 'select-preview-image';

	var AiImageGenerator = function () {
		var $this = this;

		this.Initialise = function (config) {
			this.$modal            = $('#ai-image-generator-modal');
			this.$text_prompt_area = this.$modal.find('.js-textarea-prompt');
			this.$submit_button    = this.$modal.find('.js-submit-content');
			this.$clear_button     = this.$modal.find('.js-clear-input-textarea');
			this.$preview_container = this.$modal.find('.js-preview');
			this.$loading_area     = this.$modal.find('.js-loading-area');
			this.$preview_area     = this.$modal.find('.js-preview-area');
			this.$error_area       = this.$modal.find('.js-error-area');

			this.state              = STATE_INITIAL;
			this.feedback_touched   = false;

			this.xhr				= null;

			this.target             = config.target;

			this.ShowState(STATE_INITIAL);

			this.Listeners();

			// Move the modal to the body to prevent it from being
			// directly affected by the "Image Dialog" modal
			this.$modal.appendTo('body');
		};

		this.ShowLoading = function (toggle) {
			if (toggle) {
				this.$submit_button.find('.js-submit-content-spinner').show();

				this.$loading_area.empty();
				this.$loading_area.append(this.CreateRowAndImages(1, Array(COLUMNS_PER_ROW).fill(null)));
				this.$loading_area.show();

				this.$submit_button.prop('disabled', true);
				this.$text_prompt_area.prop('disabled', true);
				this.$clear_button.prop('disabled', true);
				this.ShowValidationError('');

				this.$preview_container.show();
			} else {
				this.$loading_area.hide();
				this.$submit_button.find('.js-submit-content-spinner').hide();
				this.$submit_button.prop('disabled', false);
				this.$text_prompt_area.prop('disabled', false);
				this.$clear_button.prop('disabled', false);
			}
		};

		this.ShowPreview = function (visibility, data) {
			if (!visibility) {
				this.$preview_area.hide();
				return;
			}

			this.$preview_container.show();
			this.$preview_area.empty();
			this.$preview_area.append(this.CreateRowAndImages(Math.ceil(data.images.length / COLUMNS_PER_ROW), data.images));
			this.$preview_area.show();

			if (this.HasTarget()) {
				this.PreviewImagesListener();
			}
		};

		this.ShowError = function (visibility) {
			if (visibility)
			{
				this.$preview_container.show();
				this.$error_area.show();
			}
			else
				this.$error_area.hide();
		}

		this.CreateRowAndImages = function (rows, images) {
			var $rows = [];

			for (var i = 0; i < rows; i++) {
				var $row = $('<div class="row h-100"></div>');

				if (i < rows - 1) { // Add margin-bottom to all rows except the last one
					$row.addClass('mb-4');
				}

				$rows.push($row);
			}

			for (var i = 0; i < images.length; i++) {
				var image  = images[i];
				var $image = this.CreateImage(image);

				var rowIndex = Math.floor(i / COLUMNS_PER_ROW);
				$rows[rowIndex].append($image);
			}

			return $rows;
		};

		this.CreateImage = function (image) {
			if (image) {
				return $(image_preview_template({
					previewImageClass: PREVIEW_IMAGE_CLASS,
					selectImageClass: PREVIEW_IMAGE_SELECT_CLASS,
					mimeType: image.mime_type,
					imageData: image.data
				}));
			}

			// Use the loading square template for placeholder
			var GenerateRandomNumber = function(min, max) {
				return (Math.random() * (max - min) + min).toFixed(2) + 's';
			};

			return $(loading_square_template({
				pulsateDuration: GenerateRandomNumber(0.5, 1.5),
				gradientDuration: GenerateRandomNumber(1.5, 6.5)
			}));
		};

		this.PreviewImagesListener = function () {
			var $images = this.$modal.find('.' + PREVIEW_IMAGE_CLASS);

			$images.on('mouseenter', function (event) {
				$(this).find('img').fadeTo('slow', 0.5);
				$(this).find('.' + PREVIEW_IMAGE_SELECT_CLASS).fadeTo('slow', 1);
			});

			$images.on('mouseleave', function (event) {
				$(this).find('img').fadeTo('slow', 1);
				$(this).find('.' + PREVIEW_IMAGE_SELECT_CLASS).fadeTo('slow', 0);
			});

			$images.find('.' + PREVIEW_IMAGE_SELECT_CLASS).on('click', function () {
				// Get the clicked image element
				var image_element = $(this).parent().find('img');

				// Check if the image is loaded
				if (image_element[0] && image_element[0].complete) {
					image_element = image_element[0];

					var mime_type   = image_element.src.substring(image_element.src.indexOf(':') + 1, image_element.src.indexOf(';'));
					var base64_data = image_element.src.substring(image_element.src.indexOf(',') + 1);

					var uint8_array = new Uint8Array(atob(base64_data).split('').map(function(char) {
						return char.charCodeAt(0);
					}));

					const blob = new Blob([uint8_array], { type: mime_type });

					// Create a File object from the Blob
					const file = new File([blob], 'image.png', { type: mime_type });

					var $input_element = $this.GetTarget();

					if ($input_element == null) {
						console.error('Expected a target but could not find the target.');
						return;
					}

					// Now transfer the file to the target input element
					var file_list = new DataTransfer();
					file_list.items.add(file);

					$input_element.prop('files', file_list.files);

					// Let the target input know there's been a file added
					$input_element.trigger('change');
					$this.$modal.modal('hide');
				} else {
					// Image is not loaded yet
					// Don't do anything
				}
			});
		};

		this.HasTarget = function () {
			if (!this.target) {
				return false;
			}

			var $target = $('#' + this.target);

			if (!$target.length) {
				return false;
			}

			if (!$target.is('input') || $target.attr('type') !== 'file') {
				return false;
			}

			return true;
		};

		this.GetTarget = function () {
			if (!this.HasTarget()) {
				return null;
			}

			return $('#' + this.target);
		};

		this.ShowState = function (stateType, data) {
			switch (stateType) {
				case STATE_INITIAL: {
					this.ResetValidation();
					this.$text_prompt_area.val('');
					this.$preview_container.hide();

					this.ShowError(false);
					this.ShowLoading(false);
					this.ShowPreview(false);
				}
					break;
				case STATE_LOADING: {
					this.ShowError(false);
					this.ShowLoading(true);
					this.ShowPreview(false);
				}
					break;
				case STATE_PREVIEW: {
					this.ShowError(false);
					this.ShowLoading(false);
					this.ShowPreview(true, data);
				}
					break;
				case STATE_ERROR: {
					this.ShowError(true);
					this.ShowLoading(false);
					this.ShowPreview(false);
				}
					break;
			}

			this.state = stateType;

			this.UpdateFooter();
		};

		this.UpdateFooter = function () {
			switch (this.state) {
				case STATE_INITIAL:
					{
						this.$submit_button.show().find('.js-submit-content-text').text(lmsg('common.ai.generative_content.modal.generate'));
					}
					break;
				case STATE_PREVIEW:
					{
						this.$submit_button.show().find('.js-submit-content-text').text(lmsg('common.ai.generative_content.modal.regenerate'));
					}
					break;
			}
		}

		this.GetValidationErrorMessage = function () {
			if (this.$text_prompt_area.val().length < 10) {
				return lmsg('common.ai.prompt.validation.more_than', 10);
			}

			if (this.$text_prompt_area.val().length > 255) {
				return lmsg('common.ai.prompt.validation.less_than', 255);
			}

			return '';
		};

		this.ShowValidationError = function () {
			// Don't show validation on the first attempt
			if (!this.feedback_touched) {
				return;
			}

			var $feed_back    = this.$modal.find('.invalid-feedback');
			var error_message = this.GetValidationErrorMessage();

			$feed_back.text(error_message);

			if (error_message) {
				this.$text_prompt_area.addClass('is-invalid');
			} else {
				this.$text_prompt_area.removeClass('is-invalid');
			}
		};

		this.IsValidationValid = function () {
			return this.GetValidationErrorMessage() == '';
		};

		this.ResetValidation = function () {
			this.feedback_touched = false;
			this.$text_prompt_area.removeClass('is-invalid');
		};

		this.Listeners = function () {
			this.$submit_button.on('click', function (event) {
				event.preventDefault();

				$this.feedback_touched = true;

				if (!$this.IsValidationValid()) {
					$this.ShowValidationError();
					return;
				}

				$this.ShowState(STATE_LOADING);

				$this.xhr = $.post('/api/ai/generate-images', {
					prompt: $this.$text_prompt_area.val()
				}, function (data) {
					$this.ShowState(STATE_PREVIEW, data);
				}).fail(function () {
					$this.ShowState(STATE_ERROR);
				});
			});

			this.$clear_button.on('click', function () {
				$this.$text_prompt_area.val('');
			});

			this.$modal.on('shown.bs.modal', function () {
				if ($this.xhr)
					$this.xhr.abort();

				$this.ShowState(STATE_INITIAL);
			});

			this.$modal.on('hidden.bs.modal', function (event) {
				if ($this.xhr)
					$this.xhr.abort();

				$this.ShowState(STATE_INITIAL);
			});
		};
	};

	return new AiImageGenerator();
});
