<?php


/**
 * @file imagetarget.class.inc
 * Question type, enabling the creation of imagetarget questions
 */

/**
 * Extension of QuizQuestion.
 */
class ImagetargetQuestion extends QuizQuestion {

  /**
   * Run check_markup() on the field of the specified choice alternative
   * @param int $alternativeIndex
   *   The index of the alternative in the alternatives array.
   * @param string $field
   *   The name of the field we want to check markup on
   * @param boolean $check_user_access
   *   Whether or not to check for user access to the filter we're trying to apply
   * @return string
   *   HTML markup
   */
  private function checkMarkup($alternativeIndex, $field, $check_user_access = TRUE) {
    $alternative = $this->node->alternatives[$alternativeIndex];
    return check_markup($alternative[$field], $alternative[$field .'_format'], $check_user_access);
  }

  /**
   * Implementation of save
   * Stores the question in the database.
   *
   * @param boolean $is_new
   *   TRUE if - if the node is a new node
   *
   * @see sites/all/modules/quiz-HEAD/question_types/quiz_question/QuizQuestion#save()
   */
  public function saveNodeProperties($is_new = FALSE) {
    $is_new = $is_new || $this->node->revision == 1;

    $file = imagetarget_save_image('alternatives');

    if ($file) {
      $this->node->fid = $file->fid;
      $this->node->width = $file->width;
      $this->node->height = $file->height;
    }

    if (!isset($this->node->feedback_format)) {
      $this->node->feedback_format = FILTER_FORMAT_DEFAULT;
    }

    if ($is_new) {

      if (!isset($this->node->fid)) {
        watchdog('imagetarget', 'Could not find any image attached to the node on save %nid', array('%nid' => $this->node->nid), WATCHDOG_ERROR);
        $this->node->fid = 0;
      }

      $sql = 'INSERT INTO {quiz_imagetarget_properties}
      (nid, vid, fid, width, height, feedback, feedback_format)
      VALUES (%d, %d, %d, %d, %d, \'%s\', %d)';
      db_query($sql, $this->node->nid, $this->node->vid, $this->node->fid, $this->node->width, $this->node->height, $this->node->feedback, $this->node->feedback_format);
      for ($i = 1; isset($this->node->alternatives['targets'][$i]); $i++) {
        if (drupal_strlen($this->node->alternatives['targets'][$i]['targettext']) > 0) {
          $this->insertAlternative($i);
        }
      }
    }
    else {

      $sql = 'UPDATE {quiz_imagetarget_properties}
        SET fid = %d, width = %d, height = %d, feedback = \'%s\', feedback_format = %d
        WHERE nid = %d AND vid = %d';
      db_query($sql, $this->node->fid, $this->node->width, $this->node->height, $this->node->feedback, $this->node->feedback_format, $this->node->nid, $this->node->vid);

      // We fetch ids for the existing answers belonging to this question
      // We need to figure out if an existing alternative has been changed or deleted.
      $sql = 'SELECT id FROM {quiz_imagetarget_target}
              WHERE question_nid = %d AND question_vid = %d';
      $res = db_query($sql, $this->node->nid, $this->node->vid);

      // We start by assuming that all existing alternatives needs to be deleted
      $ids_to_delete = array();
      while ($res_o = db_fetch_object($res)) {
        $ids_to_delete[] = $res_o->id;
      }

      for ($i = 1; isset($this->node->alternatives['targets'][$i]); $i++) {
        $short = $this->node->alternatives['targets'][$i];
        if (drupal_strlen($this->node->alternatives['targets'][$i]['targettext']) > 0) {
          // If new alternative
          if (!is_numeric($short['id'])) $this->insertAlternative($i);
          // If existing alternative
          else {
            $this->updateAlternative($i);
            // Make sure this alternative isn't deleted
            $key = array_search($short['id'], $ids_to_delete);
            $ids_to_delete[$key] = FALSE;
          }
        }
      }
      foreach ($ids_to_delete as $id_to_delete) {
        if ($id_to_delete) {
          db_query('DELETE FROM {quiz_imagetarget_target} WHERE id = %d', $id_to_delete);
        }
      }
    }
  }

  /**
   * Helper function. Saves new alternatives
   *
   * @param int $i
   *  The alternative index
   */
  private function insertAlternative($i) {
    $sql = 'INSERT INTO {quiz_imagetarget_target}
            (targettext, top, leftpx, width, height, score_if_correct,
            score_if_not_correct, question_nid, question_vid)
            VALUES(\'%s\', %d, %d, %d, %d, %d, %d, %d, %d)';
    $short = $this->node->alternatives['targets'][$i];
    $position = explode(',', $short['region']);
    db_query($sql, $short['targettext'], $position[0], $position[1], $position[2], $position[3], $short['score_if_correct'], $short['score_if_not_correct'], $this->node->nid, $this->node->vid);
  }

  /**
   * Helper function. Updates existing alternatives
   *
   * @param int $i
   *  The alternative index
   */
  private function updateAlternative($i) {
    $sql = 'UPDATE {quiz_imagetarget_target}
            SET targettext = \'%s\', top = %d, leftpx = %d, width = %d, height = %d,
            score_if_correct = %d, score_if_not_correct = %d
            WHERE id = %d AND question_nid = %d AND question_vid = %d';
    $short = $this->node->alternatives['targets'][$i];
    $position = explode(',', $short['region']);
    db_query($sql, $short['targettext'], $position[0], $position[1], $position[2], $position[3], $short['score_if_correct'], $short['score_if_not_correct'], $short['id'], $this->node->nid, $this->node->vid);
  }

  /**
   * Implementation of validate
   * @param form $form
   */
  public function validateNode(array &$form) {
    if ($this->node->revision == 1) {
      // TODO: check a file was uploaded if new
    }


    for ($i = 1; isset($this->node->alternatives['targets'][$i]); $i++) {
      $short = $this->node->alternatives['targets'][$i];

      // TODO: check the positional elements are correct and sane
    }
  }

  /**
   * Implementation of delete
   * @see QuizQuestion#delete()
   */
  public function delete($only_this_version = FALSE) {
    $delete_properties = 'DELETE FROM {quiz_imagetarget_properties} WHERE nid = %d';
    $delete_targets = 'DELETE FROM {quiz_imagetarget_target} WHERE question_nid = %d';
    $delete_multi_results = '
      DELETE FROM {quiz_imagetarget_user_answer_targets}
      USING {quiz_imagetarget_user_answer_targets}
      INNER JOIN {quiz_imagetarget_user_answers}
      ON {quiz_imagetarget_user_answer_targets}.user_answer_id = {quiz_imagetarget_user_answers}.id
      WHERE {quiz_imagetarget_user_answers}.question_nid = %d'. ($only_this_version ? ' AND question_vid = %d' : '');
    $delete_results = 'DELETE FROM {quiz_imagetarget_user_answers} WHERE question_nid = %d';
    if ($only_this_version) {
      $delete_properties .= ' AND vid = %d';
      $delete_targets .= ' AND question_vid = %d';
      $delete_results .= ' AND question_vid = %d';
    }
    db_query($delete_properties, $this->node->nid, $this->node->vid);
    db_query($delete_targets, $this->node->nid, $this->node->vid);
    db_query($delete_multi_results, $this->node->nid, $this->node->vid);
    db_query($delete_results, $this->node->nid, $this->node->vid);

    parent::delete($only_this_version);
  }

  /**
   * Implementation of getNodeProperties
   * @see QuizQuestion#getNodeProperties()
   */
  public function getNodeProperties() {
    if (isset($this->nodeProperties)) return $this->nodeProperties;
    $props = parent::getNodeProperties();

    // Load the properties
    $sql = 'SELECT * FROM {quiz_imagetarget_properties} WHERE nid = %d AND vid = %d';
    $res = db_query($sql, $this->node->nid, $this->node->vid);
    $res_a = db_fetch_array($res);
    if (is_array($res_a)) {
      $props = array_merge($props, $res_a);
    }

    // Load the targets
    $sql = 'SELECT *
      FROM {quiz_imagetarget_target}
      WHERE question_nid = %d AND question_vid = %d
      ORDER BY id';
    $res = db_query($sql, $this->node->nid, $this->node->vid);
    $props['alternatives']['targets'] = array(); // init array so it can be iterated even if empty

    $i = 1;
    while ($res_arr = db_fetch_array($res)) {
      $props['alternatives']['targets'][$i] = $res_arr;
      $i++;
    }
    $this->nodeProperties = $props;
    return $props;
  }

  /**
   * Implementation of getNodeView
   * @see QuizQuestion#getNodeView()
   */
  public function getNodeView() {
    $content = parent::getNodeView();

    if (user_access('edit any quiz') && !isset($this->node->alternatives['targets'])) {
      drupal_set_message(t('There are no targets set on this image'));
      $content['answers'] = array(
        '#value' => '',
        '#weight' => 2,
      );
    }
    else {
      $content['answers'] = array(
        '#value' => theme('imagetarget_answer_node_view', $this->node->alternatives['targets'], $this->viewCanRevealCorrect(), $this->node->fid),
        '#weight' => 2,
      );
    }

    return $content;
  }

  /**
   * Generates the question form.
   *
   * This is called whenever a question is rendered, either
   * to an administrator or to a quiz taker.
   */
  public function getAnsweringForm(array $form_state = NULL, $rid) {
    $form = parent::getAnsweringForm($form_state, $rid);

    if (user_access('edit any quiz') && empty($this->node->alternatives['targets'])) {
      drupal_set_message(t('You can now set some targets on this image'));
      drupal_goto('node/' . $this->node->nid . '/edit');
    }

    $form['#theme'] = 'imagetarget_answering_form';

    $form['imagetarget_image'] = $this->_generateImageMarkupFormElement();

    /* We use an array looking key to be able to store multiple answers in tries.
     * At the moment all user answers have to be stored in tries. This is something we plan
     * to fix in quiz 5.x.
     */
    $res = '';
    if (isset($rid)) {
      // This question has already been answered. We load the answer.
      $response = new ImagetargetResponse($rid, $this->node);
      $response = $response->getResponse();
      $res = array();
      foreach ($response AS $id => $r) {
        $res[] = $r['top'] .','. $r['leftpx'];
      }
      $res = implode(':', $res);
    }


    $form['tries[answer]'] = array(
        '#type' => user_access('access devel information') ? 'hidden' : 'hidden',
        '#default_value' => isset($res) ? $res : 'here',
        '#size' => 10,
        '#attributes' => array('class' => 'imagetarget-target'),
      );

    $form['targets'] = array(
      '#type' => 'fieldset',
      '#title' => t('Answer'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
      '#weight' => -4,
      '#tree' => TRUE,
    );

    for ($i = 1; isset($this->node->alternatives['targets'][$i]); $i++) {
      $short = $this->node->alternatives['targets'][$i];
      if (!isset($response)) {
        //$form['tries[answer]']['#default_value'] .= ':NULL';
      }

      $form['targets'][$i]['id'] = array(
        '#type' => 'value',
        '#value' => $short['id'],
      );

      $form['targets'][$i]['targettext'] = array(
        '#type' => 'markup',
        '#value' => check_plain($short['targettext']),
      );

      $form['targets'][$i]['selected_target'] = array(
        '#type' => 'button',
        '#value' => t('Set target !i', array('!i' => $i)),
        '#attributes' => array('class' => 'imagetarget-select-region'),
        );
    }
    if (!isset($response)) {
      //$form['tries[answer]']['#default_value'] = substr( $form['tries[answer]']['#default_value'] , 1);
    }


    return $form;
  }

  /**
   * Implementation of getCreationForm
   * @see QuizQuestion#getCreationForm()
   */
  public function getCreationForm(array $form_state = NULL) {

    // ajax may have added some image data
    if ( isset($form_state['values']['fid']) ) {
      $this->node->fid = $form_state['values']['fid'];
      $this->node->width = $form_state['values']['width'];
      $this->node->height = $form_state['values']['height'];
    }

    $form = array();
    $form['#attributes'] = array('enctype' => "multipart/form-data");

    $type = node_get_types('type', $this->node);
    // We add #action to the form because of the use of ajax
    $options = array();
    $get = $_GET;
    unset($get['q']);
    if (!empty($get)) {
      $options['query'] = $get;
    }

    $action = url('node/add/'. $type->type, $options);
    if (isset($this->node->nid)) {
      $action = url('node/' . $this->node->nid . '/edit', $options);
    }
    $form['#action'] = $action;
  
    if (isset($this->node->fid)) {

      $form['fid'] = array(
        '#type' => 'value',
        '#value' => $this->node->fid,
      );

      $form['width'] = array(
        '#type' => 'value',
        '#value' => $this->node->width,
      );

      $form['height'] = array(
        '#type' => 'value',
        '#value' => $this->node->height,
      );
    }

    $form['alternatives'] = array(
      '#type' => 'fieldset',
      '#title' => t('Answer'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
      '#weight' => -4,
      '#tree' => TRUE,
      '#prefix' => '<div id="imagetarget_alternatives">',
      '#suffix' => '</div>',
    );

    $form['alternatives']['upload'] = array(
      '#type' => 'file',
      '#title' => t('Attach a new image file'),
      '#description' => t('After adding the image, save the question then come back to set the targets.'),
    );

    // Ajax button to upload the image
    $form['alternatives']['upload_now'] = array(
      '#type' => 'submit',
      '#value' => 'Upload',
      '#submit' => array('imagetarget_upload_image'), // If no javascript action.
      '#ahah' => array(
        'path' => 'node/add/imagetarget/upload_image_ahah',
        'wrapper' => 'imagetarget_alternatives',
        'effect' => 'fade',
        'method' => 'replace',
        'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
      ),
    );

    $form['alternatives']['imagetarget_image'] = $this->_generateImageMarkupFormElement();

    $form['alternatives']['#theme'][] = 'imagetarget_creation_form';

    // choice_count might be stored in the form_state after an ajax callback
    if (isset($form_state['choice_count'])) {
      $choice_count = $form_state['choice_count'];
    }
    else {
      $choice_count = max(variable_get('imagetarget_def_num_of_alts', 10), isset($this->node->alternatives['targets']) ? count($this->node->alternatives['targets']) : 0);
    }

    // We only add the targets if there is an image set
    if (isset($this->node->fid)) {

      for ($i = 1; $i <= $choice_count; $i++) {
        $short = isset($this->node->alternatives['targets'][$i]) ? $this->node->alternatives['targets'][$i] : NULL;

        // We add id to be able to update the correct alternatives if the node is updated, without destroying
        // existing answer reports
        $form['alternatives']['targets'][$i]['id'] = array(
          '#type' => 'value',
          '#value' => $short['id'],
        );

        $form['alternatives']['targets'][$i]["targettext"] = array(
          '#type' => 'textfield',
          '#default_value' => $short['targettext'],
        );

        $form['alternatives']['targets'][$i]['selected_region'] = array(
          '#type' => 'button',
          '#value' => t('Set region !i', array('!i' => $i)),
          '#attributes' => array('class' => 'imagetarget-select-region'),
        );

        $form['alternatives']['targets'][$i]['region'] = array(
          '#type' => user_access('access devel information') ? 'hidden' : 'hidden',
          '#default_value' => ( isset($short) ? "{$short['top']},{$short['leftpx']},{$short['width']},{$short['height']}" : ''),
          '#size' => 10,
          '#attributes' => array('class' => 'imagetarget-region'),
        );

        $form['alternatives']['targets'][$i]['score_if_correct'] = array(
          '#type' => 'textfield',
          '#size' => 2,
          '#maxlength' => 2,
          '#default_value' => isset($short['score_if_correct']) ? $short['score_if_correct'] : 1,
        );

        $form['alternatives']['targets'][$i]['score_if_not_correct'] = array(
          '#type' => 'textfield',
          '#size' => 4,
          '#maxlength' => 4,
          '#default_value' => isset($short['score_if_not_correct']) ? $short['score_if_not_correct'] : 0,
        );
      }
    }

    // TODO : Add ahah based button to add more regions
    // We can't send the get values to the ahah callback the normal way, so we do it like this.
    /*
    $form['get'] = array (
      '#type' => 'value',
      '#value' => $get,
    );

    $form['alternatives']['imagetarget_add_alternative'] = array(
      '#type' => 'submit',
      '#value' => t('Add more alternatives'),
      '#submit' => array('imagetarget_more_choices_submit'), // If no javascript action.
      '#ahah' => array(
        'path' => 'node/add/imagetarget/add_alternative_ahah',
        'wrapper' => 'imagetargettable',
        'effect' => 'slide',
        'method' => 'append',
      ),
    );
     */

    $form['feedback'] = array(
      '#type' => 'textarea',
      '#title' => t('Feedback'),
      '#description' => t('Enter the feedback the users will see on the report at the end of the quiz'),
      '#default_value' => $this->node->feedback,
    );

    $form['feedback_format'] = filter_form($this->node->feedback_format, NULL, array('feedback_format'));

    return $form;
  }

  /**
   * Helper function to generate the image in the form
   */
  private function _generateImageMarkupFormElement() {
    $default_settings = $this->getDefaultAltSettings();

    $fileimg = '';
    $height = '';
    if ($default_settings['fid']) {
      $file = imagetarget_file_load($default_settings['fid']);
      if ($file) {
        $imgurl = url($file->filepath);
        if (imagetarget_get_imagecache_preset()) {
          $imgurl = imagecache_create_url(imagetarget_get_imagecache_preset()->presetname, $file->filepath);
        }
        $fileimg = '<img src="' . $imgurl . '" style="position: absolute;" />';
        $height = ' height: ' . (intval($default_settings['height']) + 25) . 'px;';
      }
    }

    return array(
      '#type' => 'markup',
      '#value' => "<div id=\"imagetarget_image\" style=\"position: relative;{$height}\">{$fileimg}</div>",
    );
  }

  /**
   * Helper function provding the default settings for the creation form.
   *
   * @return array
   *  Array with the default settings
   */
  private function getDefaultAltSettings() {
    return array(
      'fid' => isset($this->node->fid) ? $this->node->fid : 0,
      'width' => isset($this->node->width) ? $this->node->width : 0,
      'height' => isset($this->node->fid) ? imagetarget_get_image_height($this->node->fid) : 0,
      'feedback' => isset($this->node->feedback) ? $this->node->feedback : '',
      'feedback_format' => isset($this->node->feedback_format) ? $this->node->feedback_format : FILTER_FORMAT_DEFAULT,
    );
  }

  /**
   * Implementation of getMaximumScore.
   * Calculate the maximum score for this question
   * @return int
   *   The maximum score possible on this question
   * @see QuizQuestion#getMaximumScore()
   */
  public function getMaximumScore() {
    $max = 0;
    for ($i = 1; isset($this->node->alternatives['targets'][$i]); $i++) {
      if (drupal_strlen($this->node->alternatives['targets'][$i]['targettext']) > 0 ) {
        $short = $this->node->alternatives['targets'][$i];
        $max += max($short['score_if_correct'], $short['score_if_not_correct']);
      }
    }
    return max($max, 1);
  }

}

/**
 * Extension of QuizQuestionResponse
 */
class ImagetargetResponse extends QuizQuestionResponse {
  /**
   * ID of the answers.
   */
  protected $user_answers;

  /**
   * Constructor
   */
  public function __construct($result_id, stdClass $question_node, $tries = NULL) {
    parent::__construct($result_id, $question_node, $tries);
    $this->user_answers = array();
    //return true;
    // tries is the tries part of the post data
    if (is_array($tries)) {
      $answer = is_array($tries['answer']) ? $tries['answer']['answer'] : $tries['answer'];
      $targets = explode(":", $answer);
      $i=1;
      foreach ($targets as $target) {
        $coords = explode(',', $target);
        $this->user_answers[$i] = array('top' => $coords[0], 'leftpx' => $coords[1], 'target_id' => $question_node->alternatives['targets'][$i]['id'] );
        $i++;
      }
    }
    // We load the answer from the database
    else {
      $sql = 'SELECT target_id,top,leftpx
        FROM {quiz_imagetarget_user_answers} ua
        LEFT OUTER JOIN {quiz_imagetarget_user_answer_targets} uam ON (uam.user_answer_id = ua.id)
        WHERE ua.result_id = %d AND ua.question_nid = %d AND ua.question_vid = %d';
      $res = db_query($sql, $result_id, $this->question->nid, $this->question->vid);
      $i = 1;
      while ($res_o = db_fetch_array($res)) {
        $this->user_answers[$i] = $res_o;
        $i++;
      }
    }
  }

  /**
   * Implementation of isValid
   *
   * @see QuizQuestionResponse#isValid()
   */
  public function isValid() {
    if (empty($this->user_answers)) return t('You place every target');
    foreach ($this->user_answers as $coords) {
      if (count($coords)!=2 || !is_numeric($coords[0]) || !is_numeric($coords[1])) {
        //return t('You must place every target!');
      }
    }
    return TRUE;
  }

  /**
   * Implementation of save
   *
   * @see QuizQuestionResponse#save()
   */
  public function save() {
    $sql = "INSERT INTO {quiz_imagetarget_user_answers}
    (result_id, question_vid, question_nid)
    VALUES (%d, %d, %d)";
    db_query($sql, $this->rid, $this->question->vid, $this->question->nid);
    $user_answer_id = db_last_insert_id('{quiz_imagetarget_user_answers}', 'id');
    for ($i = 1; $i <= count($this->user_answers); $i++) {
      $sql = 'INSERT INTO {quiz_imagetarget_user_answer_targets}
      (user_answer_id, target_id, top, leftpx)
      VALUES(%d, %d, %d, %d)';
      db_query($sql, $user_answer_id, $this->user_answers[$i]['target_id'], $this->user_answers[$i]['top'], $this->user_answers[$i]['leftpx']);
    }
  }

  /**
   * Implementation of delete
   *
   * @see QuizQuestionResponse#delete()
   */
  public function delete() {
    $sql = 'DELETE FROM {quiz_imagetarget_user_answer_targets}
    USING {quiz_imagetarget_user_answer_targets}
    INNER JOIN {quiz_imagetarget_user_answers}
    ON {quiz_imagetarget_user_answer_targets}.user_answer_id = {quiz_imagetarget_user_answers}.id
    WHERE {quiz_imagetarget_user_answers}.question_nid = %d
    AND {quiz_imagetarget_user_answers}.question_vid = %d
    AND {quiz_imagetarget_user_answers}.result_id = %d';
    db_query($sql, $this->question->nid, $this->question->vid, $this->rid);
    $sql = 'DELETE FROM {quiz_imagetarget_user_answers}
    WHERE result_id = %d AND question_nid = %d AND question_vid = %d';
    db_query($sql, $this->rid, $this->question->nid, $this->question->vid);
  }

  /**
   * Implementation of score
   *
   * @return uint
   *
   * @see QuizQuestionResponse#score()
   */
  public function score() {
    $score = 0;
    foreach ($this->question->alternatives['targets'] as $key => $region) {
      if ($this->checkTargetInRegion($this->user_answers[$key], $region)) {
        $score += $region['score_if_correct'];
      }
      else {
        $score += $region['score_if_not_correct'];
      }
    }
    return $score;
  }

  /**
   * Given a target and region, checks that the target is in the region
   * @param array[string => int] $target
   *   ARRAY with the following keys. Values are in px ['top', 'leftpx']
   * @param array[string => int] $region
   *   ARRAY with the following keys. Values in px ['top', 'leftpx', 'width', 'height']
   * @return boolean
   *   TRUE if target is inside the region, FALSE if outside
   */
  public function checkTargetInRegion($target, $region) {
    return  ($target['leftpx'] >= $region['leftpx'] && $target['leftpx'] <= ($region['leftpx'] + $region['width'])
      && $target['top'] >= $region['top'] && $target['top'] <= ($region['top'] + $region['height']));
  }

  /**
   * Implementation of getResponse
   *
   * @return answer
   *
   * @see QuizQuestionResponse#getResponse()
   */
  public function getResponse() {
    return $this->user_answers;
  }

  /**
   * Implementation of getReportFormResponse
   *
   * @see getReportFormResponse($showpoints, $showfeedback, $allow_scoring)
   */
  public function getReportFormResponse($showcorrect = TRUE, $showfeedback = TRUE, $allow_scoring = FALSE) {
    // Fetch all data for the report
    $feedback = $this->checkMarkup($this->question->feedback, $this->question->feedback_format);

    $answers = array();
    foreach ($this->question->alternatives['targets'] as $i => $short) {
      if (drupal_strlen(check_plain($short['targettext'])) > 0) {
        $answers[$i]['targettext'] = $short['targettext'];
        $answers[$i]['score'] = $this->checkTargetInRegion($this->user_answers[$i], $short) ? $short['score_if_correct'] : $short['score_if_not_correct'];
        $answers[$i]['correct'] = $answers[$i]['score'] > 0;
        $answers[$i]['region'] = $short;
        $answers[$i]['target'] = $this->user_answers[$i];
      }
    }

    // Return themed report
    return array(
      '#type' => 'markup',
      '#value' => theme('imagetarget_response', $this->question->fid, $answers, $feedback, $showcorrect, $showfeedback),
    );
  }

  /**
   * Run check_markup() on the field of the specified choice alternative
   *
   * @param string $alternative
   *   text to be checked
   * @param int $format
   *   The input format to be used
   * @param boolean $check_user_access
   *   Whether or not we are to check the users access to the chosen format
   * @return HTML markup
   */
  private function checkMarkup($alternative, $format, $check_user_access = FALSE) {
    // If the string is empty we don't run it through input filters(They might add empty tags).
    if (drupal_strlen($alternative) == 0) return '';
    return check_markup($alternative, $format, $check_user_access);
  }
}
