ONO::FW::Edu::Apps::Modules::MathFillin

package ONO::FW::Edu::Apps::Modules::MathFillin;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2022-2026 Jos Kirps and The Joopita Project [ASBL]
# Copyright (C) 2011-2022 Jos Kirps and Joopita Research ASBL
# Copyright (C) 2006-2011 Jos Kirps and The Joopita Project
# Copyright (C) 2001-2006 Jos Kirps and The WobbleWolf Project
#
# This file, as well as many other parts of the ONO Software Project or
# related elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program 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.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################

#: This module is a front end application module wrapper that has been
#: created for the Morzino Project (morzino.com) and that has been released
#: as Open Source software.
#:
#: Note that this is NOT a self-contained application, it is only a wrapper
#: that makes heavy use of the ONO Perl Framework application engine.

use strict;

use ONO::FW::Edu::Apps::ToolBox;
use ONO::FW::Edu::Apps::Modules::WorksheetMaker::Input;

use ONO::FW::Apps::ToolBox;

use ONO::Lib::PDF::Draw;
use ONO::Lib::PDF::ToolBox;

use ONO::FW::Apps::Core;
use ONO::FW::Apps::Core::Graphics;

use ONO::Lib::Parser;
use ONO::FW::Apps::PDF;
use ONO::IO;

sub learn {

my (
$self,
$option,
$vars_ref,
$HTTP,
$BASE,
) = @_;

################################################################
# INIT - BEGIN
################################################################

my (
$db,
$community,
$action_url,
$form,
$format,
$canvas,
$pdfkey,
$sec,$min,$hour,
$mday,$mon,$year,
$wday,$yday,$timestamp,
$lang,
$BLK_ref,
$vars_ref,
) = ONO::FW::Apps::Core->init($vars_ref);

my %vars = %$vars_ref;
my %BLK = %$BLK_ref;

my $app = "math_fillin";

ONO::FW::Apps::Core->now("",$app,$vars{'app_lang'});

my ($num_comma,$num_delimiter) = ONO::FW::Apps::Core->local_comma_delimiter(ONO::FW::Apps::Core->detect_local($vars{'app_lang'}),$vars_ref);

my $WEB;

my $AppName = "Fill in numbers";

if ($vars{'app_lang'} eq "de") {
$AppName = "Zahlen einfüllen";
}
if ($vars{'app_lang'} eq "fr") {
$AppName = "Remplir des nombres";

}
if ($vars{'app_lang'} eq "lu") {
$AppName = "Zuelen afëllen";
}

$vars{'app_input_print_options_title_default'} = $AppName;

$WEB .= ONO::FW::Apps::Core->output_paper_save($pdfkey,$BLK_ref,\%vars,$HTTP,$BASE,"$year$mon$mday");

if (ONO::FW::Apps::Core->running(\%vars,$db)) {

$WEB .= ONO::FW::Apps::Core->blockbackbutton($BASE,$vars_ref);

(
$vars{'app_input_range_from'},
$vars{'app_input_range_to'},

) = ONO::FW::Apps::Core->range_check(
100,
100,
$vars{'app_input_range_from'},
$vars{'app_input_range_to'},
$vars{'app_input_negative'},
$vars{'app_input_numsys_status'},
);

#############################################################
# WORKSHEET MAKER
#############################################################

if ($vars{'output_worksheet_maker'}) {

$vars_ref = ONO::FW::Edu::Apps::Modules::WorksheetMaker::Input->vars(\%vars);
%vars = %$vars_ref;

}

#############################################################
# ONLINE TRAINING
#############################################################

if ($vars{'output_web'} || $vars{'output_paper'}) {

my $question_number = ONO::FW::Apps::Core->question_number($vars{'output_web_question_number'});

my (
@num1,
$focus_on,
$page,$pages,%cache,
$whilecounter,$stats_switches,
$PDF_OPTS,
$stat_correct,$stat_wrong,$stat_todo,$stat_todo_total,
);

my $questions_per_page = 1;

if ($vars{'output_paper'}) {

$PDF_OPTS = ONO::FW::Apps::Core->headers_options($BLK_ref,$vars_ref);

ONO::FW::Apps::PDF->new("$BASE/pdf/pdf-$pdfkey.pdf","",$format);

$vars{'app_input_multiple_questions_per_page'}++;

}

foreach my $copy (ONO::FW::Apps::Core->unique_copies($vars{'app_input_print_options_unique_copies'})) {

if ($vars{'output_paper'}) {

if ($copy > 1) {
ONO::FW::Apps::PDF->page("",$format);
}
ONO::FW::Apps::Core->headers(\%vars,$format);
ONO::FW::Apps::PDF->title($vars{'app_input_print_options_title'},$PDF_OPTS,$vars{'app_input_print_options_code'},$format);
ONO::FW::Apps::Core->unique_copies_info($canvas,$copy,$vars_ref);

# ONO::Lib::PDF::ToolBox->canvas_test($canvas);

}


if ($vars{'output_web'}) {

$WEB .= qq~<$form>~;

# flush the cashes if continue...

if ($vars{'output_web_continue_key'}) {
foreach my $key (keys %vars) {
if ($key =~ /^output_web_(start|status|result|answer)/) {
$vars{$key} = "";
}
}
}

$WEB .= ONO::FW::Apps::Core->question_progress($vars{'app_input_number_of_questions'},$question_number);

unless ($vars{'output_web_question_number'} > $vars{'app_input_number_of_questions'}) {

if ($vars{'app_input_rows'} > 6) {
$stats_switches .= "aLPtvV";
} else {
$stats_switches .= "Bc";
}

$WEB .= ONO::FW::Apps::Core->stats("",$BLK_ref,"m$stats_switches");

}


}

my $used_numbers = ":";

my ($counter,@trans_vars,@trans_start,@trans_x,@trans_y,@trans_status);

if ($vars{'output_web'} && $vars{'output_web_question_number'} > $vars{'app_input_number_of_questions'}) {

$vars{'output_progress_done'}++;

$WEB .= ONO::FW::Apps::Core->question_progress_done_msg($app,$db,$community,$form,$action_url,$vars{'app_input_number_of_questions'},$vars{'output_web_mistakes'},$lang,$BLK_ref,$vars_ref);

} else {

$WEB .= ONO::FW::Apps::Core->timer($vars{'output_web'},$vars{'output_web_timer_start'});

for (my $i = 0; $i < $questions_per_page; $i++) {

# output paper

if ($vars{'output_paper'}) {

my (@number,@sign);

my %tvars = %vars;
$tvars{'app_input_rows'} = $vars{'app_input_rows'};
$trans_vars[$counter] = \%tvars;

my ($y,$whilecounter,$item_number);

my $max_y = 190;
if ($vars{'output_worksheet_maker'}) {
$max_y = 45;
}

while (($y+$tvars{'app_input_rows'}*7.5) < $max_y && $whilecounter < 256) {
$whilecounter++;

$tvars{'app_input_rows'} = 1+int(rand($vars{'app_input_rows'}));

# worksheet_maker - step 1 of 2

if ($vars{'output_worksheet_maker'}) {

if ($vars{'app_input_cols_max'}) {
$vars{'app_input_cols'} = 3+int(rand($vars{'app_input_cols_max'}-3));
}

$tvars{'app_input_rows'} = $vars{'app_input_rows'};
$tvars{'app_input_cols'} = $vars{'app_input_cols'};
}

my $x;

while (($x+$tvars{'app_input_cols'}*7.5) < 150 && $whilecounter < 256) {
$whilecounter++;

$tvars{'app_input_cols'} = 3+int(rand($vars{'app_input_cols'}-3));

$counter++;

if ($counter > 2 && $vars{'app_input_rows'} > 3 && $y > 120) {
$vars{'app_input_rows'} = 5;
}
if ($counter > 2 && $vars{'app_input_rows'} > 6 && $y > 100) {
$vars{'app_input_rows'} = 3;
}
if ($vars{'app_input_rows'} > 3 && $y > 150) {
$vars{'app_input_rows'} = 3;
}
if ($vars{'app_input_rows'} > 2 && $y > 175) {
$vars{'app_input_rows'} = 2;
}

# worksheet_maker - step 2 of 2 (same as 1 of 2)

if ($vars{'output_worksheet_maker'}) {
$tvars{'app_input_rows'} = $vars{'app_input_rows'};
$tvars{'app_input_cols'} = $vars{'app_input_cols'};
}

my (
$DEBUG,
$GRID,
$INPUT,
$questions,
$correct,
$wrong,
$start,
$STATUS_ref,
$tvars_ref,
) = &gen("",$canvas,"pdf",$x+5,$y+1,"Q","",\%tvars);

$trans_status[$counter] = $STATUS_ref;
$trans_vars[$counter] = $tvars_ref;
$trans_start[$counter] = $start;

$trans_x[$counter] = $x;
$trans_y[$counter] = $y;

$x = $x+$tvars{'app_input_cols'}*7.5+7.5;

}

$item_number++;
ONO::FW::Apps::Core::Graphics->pdf_draw_item_number($canvas,-2,$y,$item_number,"VW",$vars_ref);

$y = $y+$tvars{'app_input_rows'}*7.5+7.5;

}

}

if ($vars{'output_web'}) {

$WEB .= qq~<input type="hidden" name="output_web_cache_running" value="1">~;

my (
$DEBUG,
$GRID,
$INPUT,
$questions,
$correct,
$wrong,
) = &gen("",$canvas,"web",0,0,"","",\%vars);

$stat_correct = $correct;
$stat_wrong = $wrong;
$stat_todo_total = $questions - $correct - $wrong;

if (!$stat_todo_total && !$wrong) {

$vars{'output_web_answer_status'} = 1;

}

if ($wrong) {

$vars{'output_web_answer_status'} = 2;
$vars{'output_web_mistakes'} = $vars{'output_web_mistakes'} + $wrong;

}

my $BUTTON = ONO::FW::Apps::Core->check_single(
"",
"",
0,
2,
$BLK_ref,
\%vars,
"fCIMPR",
);

$WEB .= qq~$INPUT
<table class="wide_table">
<tr class="vtop">
<td class="w25 p10"></td>
<td class="w50 p5">
<table class="default_table auto" style="margin-bottom:10px">
$GRID
</table>
$BUTTON
</td>
<td class="w25 p10"></td>
</tr>
</table>
~;

if (ONO::IO->devstation) {
$WEB .= $DEBUG;
}

}

}

if ($vars{'output_web'}) {

$WEB .= ONO::FW::Apps::Core->multi_frame_end($vars{'output_web_finish'},$vars{'app_input_multiple_questions_per_page'});

$WEB .= ONO::FW::Apps::Core->input_vars(\%vars);

$WEB .= qq~</form>~;

}

}

if (ONO::FW::Apps::Core->print_corrections("$BASE/pdf/pdf-$pdfkey-corr.pdf","",$format,$vars_ref)) {

ONO::FW::Apps::Core->headers(\%vars,$format,"c");
ONO::FW::Apps::PDF->title($BLK{'Corrections'},$PDF_OPTS,$vars{'app_input_print_options_code'},$format);
ONO::FW::Apps::Core->unique_copies_info($canvas,$copy,$vars_ref);

# ONO::Lib::PDF::ToolBox->canvas_test($canvas);

my ($item_number,$last_y);

for (my $i = 1; $i < $counter+1; $i++) {

my $tvars_ref = $trans_vars[$i];
my %tvars = %$tvars_ref;
$tvars{'running'} = "REUSE";
$tvars{'output_web_start'} = $trans_start[$i];

my ($DEBUG) = &gen("",$canvas,"pdf",$trans_x[$i]+5,$trans_y[$i]+1,"A",$trans_status[$i],\%tvars);

if ($trans_y[$i] != $last_y || $last_y eq "") {

$last_y = $trans_y[$i];
if ($last_y < 1) {
$last_y = 0;
}
$item_number++;
ONO::FW::Apps::Core::Graphics->pdf_draw_item_number($canvas,-2,$trans_y[$i],$item_number,"CVW",$vars_ref);

}

}

}

}

if ($vars{'output_web'}) {

$WEB = ONO::FW::Apps::Core->stats_replace($WEB,$stat_correct,$stat_wrong,$stat_todo_total,$vars{'output_web_mistakes'},$page,$pages,$vars{'output_web_completed'});

}

if ($vars{'output_paper'}) {

ONO::FW::Apps::PDF->end;

$WEB .= ONO::FW::Apps::Core->output_paper($pdfkey,\%BLK,\%vars,$HTTP,$BASE);

}

}

if ($vars{'output_web'}) {
ONO::FW::Apps::Core->varstore($vars{'username'},$vars{'virclass'},$vars{'anonymous'},\%vars);
}

} else {

#############################################################
# EXERCISE SETUP
#############################################################

if (!$vars{'app_input_number_of_questions'}) {
$vars{'app_input_number_of_questions'} = 20;
}

$WEB .= ONO::FW::Apps::Core->infopics(
"sheet:168:190:110:15:math_fillin_sheet_1.jpg",
"screen:320:50:30::math_fillin_screenshot_1.jpg",
"sheet:168:40:140:-10:math_fillin_sheet_2.jpg",
);

$vars{'app_input_number_defaults'} = "10;20;30;40;50;60;70;80;90;100";

my $CONF .= ONO::FW::Apps::ToolBox->conf_input("number", $BLK_ref,\%vars,"",$BLK{'up_to'},"range_to",10,100,100);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("number", $BLK_ref,\%vars,"O",$BLK{'columns'},"cols",3,10,6,"max.");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("number", $BLK_ref,\%vars,"O",$BLK{'rows'},"rows",1,10,3,"max.");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("level", $BLK_ref,\%vars,"",1,10,$vars{'app_input_level_status'});

$WEB .= ONO::FW::Apps::Core->conf_box(
$db,
$community,
$app,
$form,
"",
$action_url,
$CONF,
$lang,
$BLK_ref,
\%vars,
"op-ncdi-CDI-lsu-N",
);
if (!$vars{'exercise'}) {
$WEB .= ONO::FW::Apps::Core->info_display(
ONO::FW::Apps::Core->info("info_online_print",$vars{'app_lang'}),
ONO::FW::Apps::Core->info("info_indiv_diff", $vars{'app_lang'}),
);
}

}

my ($status,$WEB) = ONO::Lib::Parser->parse($WEB,$vars_ref);

$WEB .= ONO::FW::Apps::Core->vardump($vars_ref);

$WEB = ONO::FW::Edu::Apps::ToolBox->apps_gate($WEB,$vars_ref);

return $WEB;

}

###################################################################
# SUBS
###################################################################

sub gen {

my (
$self,
$canvas,
$mode,
$x,
$y,
$sheet,
$STATUS_ref,
$vars_ref,
) = @_;

my %STATUS;
if ($STATUS_ref) {
%STATUS = %$STATUS_ref;
}
my %vars = %$vars_ref;

# start pos: row shifter

my $start_rows;

if ($vars{'app_input_range_to'} > $vars{'app_input_rows'}*10) {

my $shifter = int($vars{'app_input_range_to'}/10)-$vars{'app_input_rows'};
$start_rows = int(rand($shifter+1))*10;

}

# start pos: col shifter

my $start = 1+int(rand(10-$vars{'app_input_cols'}))+$start_rows;

if ($vars{'output_web_start'}) {
$start = $vars{'output_web_start'};
}

my $INPUT = qq~<input type="hidden" name="output_web_start" value="$start">~;

my ($GRID,$hint,$fields);

my $DEBUG = "<br><br>DEBUGGER<br><br>start: $start<br>running: $vars{'running'}<br>";

if ($vars{'output_web_cache_running'} && !$vars{'output_web_continue_key'}) {

foreach my $key (keys %vars) {
if ($key =~ /^output_web_status_(.*?)$/) {
$STATUS{$1} = $vars{$key};
}
}

} else {

for (my $r = 0; $r < $vars{'app_input_rows'}; $r++) {

for (my $c = 0; $c < $vars{'app_input_cols'}; $c++) {

if (rand(100) > 66-$vars{'app_input_level_status'}*3) {
$fields++;
$STATUS{"${r}_${c}"} = "F";
}
if (rand(100) > 66-$vars{'app_input_level_status'}*5 && !$STATUS{"${r}_${c}"}) {
$STATUS{"${r}_${c}"} = "B";
}

if (!$STATUS{"${r}_${c}"}) {
$hint++;
}
}
}

# insert a hint if there's none

if (!$hint) {

my $r = int($vars{'app_input_rows'})-1;
my $c = int($vars{'app_input_cols'})-1;

$STATUS{"${r}_${c}"} = "H";

}

my $whilecounter;
while (!$fields && $whilecounter < 256) {
$whilecounter++;

my $r = int($vars{'app_input_rows'})-1;
my $c = int($vars{'app_input_cols'})-1;

if (!$STATUS{"${r}_${c}"} || $STATUS{"${r}_${c}"} eq "B") {
$fields++;
$STATUS{"${r}_${c}"} = "F";
}

}

}

my $questions = 0;
my $correct = 0;
my $wrong = 0;
my $focus;

my ($off_x,$off_y);

if ($vars{'output_worksheet_maker'}) {
($off_x,$off_y) = (0,2);
}

if ($mode eq "pdf" && $sheet ne "A") {
my ($c1,$c2,$c3) = (235+int(rand(15)),235+int(rand(15)),235+int(rand(15)));
ONO::Lib::PDF::Draw->rect($canvas, $x, $y+$off_y, $x+$vars{'app_input_cols'}*7.5, $y+$vars{'app_input_rows'}*7.5+$off_y,0.1,"","0:$c1:$c2:$c3");
}

for (my $r = 0; $r < $vars{'app_input_rows'}; $r++) {

$GRID .= qq~ <tr>~;

for (my $c = 0; $c < $vars{'app_input_cols'}; $c++) {

$vars{"output_web_answer_${r}_${c}"} =~ s~[^0-9]~~g;

my $BORDER = qq~border:1px solid #999999~;

my $number = $start+($r*10)+$c;

my $NUMBER = $number;
if ($STATUS{"${r}_${c}"} eq "F") {
$questions++;
my $maxlength = 2;
if ($vars{"output_web_result_${r}_${c}"} > 99 || (!$vars{"output_web_result_${r}_${c}"} && $number > 99)) {
$maxlength = 3;
}
$NUMBER = qq~<input type="text" id="output_web_answer_${r}_${c}_id" name="output_web_answer_${r}_${c}" value="$vars{"output_web_answer_${r}_${c}"}"
maxlength="$maxlength" class="auto center" style="width:20px" placeholder="?">
~;

}
if ($STATUS{"${r}_${c}"} eq "B") {
$NUMBER = "";
}

$INPUT .= qq~<input type="hidden" name="output_web_result_${r}_${c}" value="$number">
<input type="hidden" name="output_web_status_${r}_${c}" value="$STATUS{"${r}_${c}"}">
~;
my $BG;

if ($vars{"output_web_answer_${r}_${c}"} ne "") {
$wrong++;
$BG = qq~bg_red~;
}
if ($vars{"output_web_answer_${r}_${c}"} ne "" && $vars{"output_web_answer_${r}_${c}"} eq $vars{"output_web_result_${r}_${c}"}) {
$correct++;
$wrong--;
$BG = qq~bg_green~;
$NUMBER = qq~<span class="bold" style="color:#006600">$number</span>~;
$INPUT .= qq~<input type="hidden" name="output_web_answer_${r}_${c}" value="$number">~;
}
if ($STATUS{"${r}_${c}"} eq "B") {
$BG = qq~bg_title~;
}

if (!$focus && $NUMBER =~ /input type="text"/) {
$focus++;
$NUMBER .= qq~<script>onojs_focus('output_web_answer_${r}_${c}_id');</script>~;
}

$GRID .= qq~ <td class="center $BG" style="width:40px;height:32px;$BORDER">$NUMBER</td>~;

if ($mode eq "pdf") {

if ($STATUS{"${r}_${c}"} eq "B") {

my $rx = 3*($x+$c*7.5);
my $ry = 3*($y+$r*7.5);

if ($c < $vars{'app_input_cols'}-1) {
ONO::Lib::PDF::Draw->rect($canvas, $x+($c+0)*7.5+$off_x, $y+($r+0)*7.5+$off_y, $x+($c+1)*7.5+$off_x, $y+($r+1)*7.5+$off_y,0.1,"","0:0:0:0");
}

}

if ($c < $vars{'app_input_cols'}-1) {
ONO::Lib::PDF::Draw->rect($canvas, $x+($c-0)*7.5+$off_x, $y+($r+0)*7.5+$off_y, $x+($c+1)*7.5+7.5+$off_x, $y+($r+0)*7.5+7.5+$off_y,0.5);
}

if ($STATUS{"${r}_${c}"} ne "B" && $STATUS{"${r}_${c}"} ne "F") {
ONO::Lib::PDF::Draw->text($canvas,($x+($c+0)*7.5)+3.75+$off_x, ($y+($r+1)*7.5)-2.25+$off_y, $number, "c");
} elsif ($sheet eq "A") {
ONO::Lib::PDF::Draw->font('HB');
ONO::Lib::PDF::Draw->text($canvas,($x+($c+0)*7.5)+3.75+$off_x, ($y+($r+1)*7.5)-2.25+$off_y, $number, "c");
ONO::Lib::PDF::Draw->font('H');
}

}

}

$GRID .= qq~ </tr>~;

}

return (
$DEBUG,
$GRID,
$INPUT,
$questions,
$correct,
$wrong,
$start,
\%STATUS,
\%vars,
)

}

###################################################################
#### THAT'S IT :-D
###################################################################

1;

__END__