# frozen_string_literal: true

#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.

require 'cgi'
module Qti
class CalculatedInteraction < AssessmentItemConverter
  def initialize(opts)
    super(opts)
    @question[:answers] = []
    @question[:variables] = []
    @question[:question_type] = 'calculated_question'
  end

  def parse_question_data
    imported_formula = @doc.at_css('calculated formula')
    @question[:imported_formula] = CGI.unescape(imported_formula.text) if imported_formula
    get_calculated_property('answer_tolerance')
    if @question[:answer_tolerance] && !@question[:answer_tolerance].to_s.match(/[^\d\.]/)
      @question[:answer_tolerance] = @question[:answer_tolerance].to_f
    end

    get_calculated_property('unit_points_percent')
    @question[:unit_points_percent] = @question[:unit_points_percent].to_f if @question[:unit_points_percent]
    get_calculated_property('unit_value')
    get_calculated_property('unit_required', true)
    get_calculated_property('unit_case_sensitive', true)
    get_calculated_property('partial_credit_points_percent')
    @question[:partial_credit_points_percent] = @question[:partial_credit_points_percent].to_f if @question[:partial_credit_points_percent]
    get_calculated_property('partial_credit_tolerance')
    @question[:partial_credit_tolerance] = @question[:partial_credit_tolerance].to_f if @question[:partial_credit_tolerance]

    get_variables()
    get_answer_sets()
    get_feedback()
    get_formulas()

    if !@question[:answer_tolerance] && tolerance = get_node_att(@doc, 'instructureMetadata instructureField[name=formula_tolerance]', 'value')
      @question[:answer_tolerance] = tolerance
    end
    if !@question[:formula_decimal_places] && precision = get_node_att(@doc, 'instructureMetadata instructureField[name=formula_precision]', 'value')
      @question[:formula_decimal_places] = precision.to_i
    end

    apply_d2l_fixes if @flavor == Qti::Flavors::D2L

    if @question[:formulas]&.empty? && @question[:imported_formula]
      @question[:formulas] << {:formula => @question[:imported_formula]}
    end
    @question
  end

  def get_calculated_property(prop_name, is_true_false=false)
    @question[:"#{prop_name}"] = @doc.at_css("calculated #{prop_name}").text if @doc.at_css("calculated #{prop_name}")
    if is_true_false and @question[:"#{prop_name}"]
      @question[:"#{prop_name}"] = @question[:"#{prop_name}"] == 'true' ? true : false
    end
  end

  def get_variables
    @doc.css('calculated vars var').each do |v|
      var = {}
      @question[:variables] << var
      var[:name] = v['name']
      var[:scale] = v['scale'].to_i
      var[:min] = v.at_css('min').text.to_f if v.at_css('min')
      var[:max] = v.at_css('max').text.to_f if v.at_css('max')
    end
  end

  def get_answer_sets
    @doc.css('calculated var_sets var_set').each do |vs|
      set = {:variables=>[], :weight=>100}
      set[:id] = vs['ident'].presence || unique_local_id
      @question[:answers] << set
      set[:answer] = vs.at_css('answer').text.to_f if vs.at_css('answer')

      vs.css('var').each do |v|
        var = {}
        set[:variables] << var
        var[:name] = v['name']
        var[:value] = v.text.to_f
      end
    end
  end

  def get_formulas
    @question[:formulas] = []
    if formulas_node = @doc.at_css('formulas')
      @question[:formula_decimal_places] = formulas_node['decimal_places'].to_i
      formulas_node.css('formula').each do |f_node|
        formula = {}
        formula[:formula] = f_node.text
        @question[:formulas] << formula
      end
    end
    @question[:formulas]
  end

  def apply_d2l_fixes
    @question[:variables].each do |v|
      v_name = v[:name]
      # substitute {var} for [var]
      @question[:question_text].gsub!("{#{v_name}}", "[#{v_name}]") if @question[:question_text]
      # substitute {var} for var
      @question[:imported_formula].gsub!("{#{v_name}}", "#{v_name}") if @question[:imported_formula]
    end
    if @question[:imported_formula]
      method_substitutions = {"sqr" => "sqrt", "Factorial" => "fact", "exp" => "e"}
      method_substitutions.each do |orig_method, new_method|
        @question[:imported_formula].gsub!("#{orig_method}(", "#{new_method}(")
      end
    end
    if @question[:variables].count == 1
      # is this secretly a simple numeric question in disguise
      var = @question[:variables].first
      if (var[:min] == var[:max]) && (@question[:imported_formula] == var[:name]) # yup the formula for the answer is "x" and there's only one possible value
        [:variables, :formulas, :imported_formula, :formula_decimal_places, :answer_tolerance].each{|k| @question.delete(k)}
        @question[:question_type] = 'numerical_question'
        @question[:answers] = [
          {:weight => 100, :id => unique_local_id, :text => 'answer_text',
            :numerical_answer_type => "exact_answer", :exact => var[:min]}
        ]
      end
    end
  end

end
end
