<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id: wikiplugin_exercise.php 66866 2018-07-01 19:41:58Z rjsmelo $

function wikiplugin_exercise_info()
{
	return [
		'name' => tra('Exercise'),
		'documentation' => tra('PluginExercise'),
		'description' => tra('Create an exercise/test with questions and grade'),
		'prefs' => ['wikiplugin_exercise'],
		'filter' => 'text',
		'format' => 'html',
		'iconname' => 'education',
		'introduced' => 9,
		'tags' => ['basic'],
		'params' => [
			'answer' => [
				'required' => false,
				'name' => tr('Answer'),
				'description' => tr('Used inline to specify the right answer to the question and propose an input field.'),
				'since' => '9.0',
				'filter' => 'text',
			],
			'incorrect' => [
				'required' => false,
				'name' => tr('Incorrect'),
				'description' => tr('Incorrect answer to suggest. Several incorrect answers can be suggested (separated by "+")'),
				'since' => '9.0',
				'filter' => 'text',
			],
		],
	];
}

function wikiplugin_exercise($data, $params)
{
	static $nextId = 1;
	$smarty = TikiLib::lib('smarty');
	$smarty->loadPlugin('smarty_modifier_escape');

	$params = new JitFilter($params);
	$answer = $params->answer->text();

	if (isset(TikiLib::lib('parser')->option['indexing']) && TikiLib::lib('parser')->option['indexing']) {
		return "{$params->answer->text()} {$params->incorrect->text()}";
	}

	if ($answer) {
		$escapedAnswer = smarty_modifier_escape($answer);
		$escapedId = smarty_modifier_escape('exercise-' . $nextId++);

		if ($incorrect = $params->incorrect->text()) {
			$exercises = wikiplugin_exercise_parse_argument($incorrect);
			wikiplugin_exercise_process_group($exercises, '#' . $escapedId);
		}

		return <<<HTML
<span id="$escapedId" class="exercise-input" data-answer="$escapedAnswer">___________</span>
HTML;
	} else {
		$exercises = wikiplugin_exercise_parse_data($data);
		wikiplugin_exercise_process_group($exercises);
		return wikiplugin_exercise_finalize();
	}
}

function wikiplugin_exercise_parse_data($data)
{
	$exercises = [];
	$key = -1;

	foreach (explode("\n", $data) as $line) {
		$line = trim($line);

		if (empty($line)) {
			continue;
		}

		if (substr($line, 0, 3) === '---') {
			$key = count($exercises);
			$exercises[] = [];
		} elseif ($key !== -1) {
			$parts = array_map('trim', explode(':', $line, 2));
			$exercises[$key][] = ['option' => array_shift($parts), 'justification' => array_shift($parts)];
		}
	}

	return $exercises;
}

function wikiplugin_exercise_parse_argument($data)
{
	$out = [];
	$answers = explode('+', $data);
	foreach ($answers as $possibility) {
		if (preg_match('/^\s*([^\(]+)(:\s*\(\s*(.*)\s*\))?\s*/', $possibility, $parts)) {
			$out[] = ['option' => $parts[1], 'justification' => isset($parts[2]) ? $parts[2] : false];
		}
	}

	return [$out];
}

function wikiplugin_exercise_process_group($exercises, $scope = '.exercise-input')
{
	$headerlib = TikiLib::lib('header');

	$js = <<<JS
$.exerciseGroup = function (exercises, scope) {
	var shuffle = function(o){
	for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
		return o;
	};
	$.each(exercises, function (k, options) {
		$(scope + ':not(.done)').filter(':first').each(function (k, container) {
			var answer = $(container).data('answer'), input;

			$(container).addClass('done').empty();
			if (options.length > 0) {
				input = $('<select><option/></select>');
				options.push({option: answer, justification: false});
				options = shuffle(options);

				$.each(options, function (k, o) {
					input.append($('<option/>')
						.val(o.option)
						.text(o.option)
						.data('justification', o.justification ? o.justification : ''));
				});
			} else {
				input = $('<input type="text"/>');
				input.attr('size', answer.length);
			}

			input.appendTo(container);
		});
	});
};
JS;
	$headerlib->add_js($js);

	$exercises = json_encode($exercises);
	$headerlib->add_js("$.exerciseGroup($exercises, '$scope');");
}

function wikiplugin_exercise_finalize()
{
	$smarty = TikiLib::lib('smarty');
	$smarty->loadPlugin('smarty_function_icon');

	$checkYourScore = smarty_modifier_escape(tr('Check your score'));
	$yourScoreIs = tr('You scored %0 out of %1', '~SCORE~', '~TOTAL~');
	$checkIcon = smarty_function_icon(['_id' => 'tick', 'title' => tr('Good!')], $smarty->getEmptyInternalTemplate());
	$crossIcon = smarty_function_icon(['_id' => 'cross', 'title' => tr('Oops!')], $smarty->getEmptyInternalTemplate());

	$js = <<<JS
$.exerciseFinalize = function (random) {
	$('.exercise-form').filter(':first').removeClass('exercise-form').each(function (k, form) {
		var label = $('p', form).hide().text(), elements = $('.exercise-input.done:not(.complete)').addClass('complete');
		$(form).submit(function () {
			var score = 0, total = 0;

			elements.find('.mark').remove();

			elements.each(function (k, container) {
				var correct, input, image;
				total += 1;
				correct = $(container).data('answer');
				input = $(':input', container).val();

				image = $('<span class="mark"/>')
					.appendTo(container);

				if (correct.toString() === input) {
					score += 1;
					image.append('$checkIcon');
				} else {
					image.append('$crossIcon');

					var just = $('option:selected', container).data('justification');
					if (just) {
						image.find('img').attr('title', just);
					}
				}
			});

			$('p', form).text(label.replace('~SCORE~', score).replace('~TOTAL~', total)).show();
			return false;
		});
	});
};
JS;
	$headerlib = TikiLib::lib('header');
	$headerlib->add_js($js);

	static $id = 0;
	++$id;
	$headerlib->add_js("$.exerciseFinalize($id);");

	return <<<HTML
<form class="exercise-form" method="get" action="#">
	<p>$yourScoreIs</p>
	<input type="submit" class="btn btn-primary btn-sm" value="$checkYourScore"/>
</form>
HTML;
}
