ONO::FW::Edu::Apps::Modules::SearchWords

package ONO::FW::Edu::Apps::Modules::SearchWords;
################################################################################
# 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::Edu::Apps::Variation;

use ONO::Lib::Encoding::HTML;

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::FW::Apps::Core::Lang;

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

use ONO::DB;
use ONO::Lib::SQL::TableCreate;
use ONO::Lib::Basic;
use ONO::Lib::Lang::ToolBox;
use ONO::Lib::Data::Letters;

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 = "searchwords";

my $WEB;

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

#

my @txt = (
"Search words",
);
if ($vars{'app_lang'} eq "de") {
@txt = (
"Suche die Wörter",
);
}

if ($vars{'app_lang'} eq "lu") {
@txt = (
"Wierder sichen",
);
}

if ($vars{'app_lang'} eq "fr") {
@txt = (
"Trouve les mots",
);
}

my $NAME = ucfirst $txt[0];
$vars{'app_input_print_options_title_default'} = $NAME;

#

my $total_pix = ONO::FW::Apps::Core::Lang->total_pix();

my $mz_font_multiplier = 1.8;

if (ONO::IO->devstation) {
$vars{'username'} = "jos";
}
if (ONO::FW::Apps::Core->running(\%vars,$db) && ($vars{'output_web'} || $vars{'output_paper'})) {
$vars{'wordlist_force_loading'} = 1;
}

if ($vars{'app_input_display_images'}) {
$vars{'app_input_langs_lang_2'} = "img";
}

my (
$RETURN,$WORDLIST_ref,$TRANS_ref,$IMGS_ref,
$USE_GROUPS,$USE_GROUPS_ALT,$USE_GROUPS_LU,$USE_GROUPS_CUSTOM_ref,
$CACHE_WORDS,$CACHE_TRANS,$CACHE_IMGS,
$use_language1,$use_language2,
$use_images,$use_images_reverse,$use_images_forced,$use_custom_groups,$use_lu,
$get_words,$words_found,$multi_frame_custom_width,$hide_flags,
) = ONO::FW::Apps::Core::Lang->lang_load_wordlist($db,$community,\%vars);

# $WEB .= $RETURN;
my @WORDLIST = @$WORDLIST_ref;
my @TRANS = @$TRANS_ref;
my @IMGS = @$IMGS_ref,
my @USE_GROUPS_CUSTOM = @$USE_GROUPS_CUSTOM_ref;

$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) && $words_found) {

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

if ($vars{'output_worksheet_maker'}) {

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

}

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

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

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

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

if ($vars{'app_input_number_of_questions'}) {
$vars{'app_input_number_of_questions'} = $question_number;
}

if ($vars{'app_input_number_of_questions'} > $words_found) {
$vars{'app_input_number_of_questions'} = $words_found;
}

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

my $questions_per_page = 20;

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);


}

@letters_uc = ONO::Lib::Data::Letters->get_uc();

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

my @corr_cache;

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 ($copy > 1) {
@WORDLIST = ONO::Lib::Basic->randomize_array(@WORDLIST);
}

my ($max_x,$max_y) = (24,18);
my $max_words = 20;
my $depth = 20;

&search_words_board_layout("",$canvas);
my ($CODE,$display_words_ref,$board_ref) = &search_words_board("",\@WORDLIST,$max_x,$max_y,$max_words,$depth);

my @display_words = @$display_words_ref;
my %board = %$board_ref;

my ($pdf_x,$pdf_y) = (0,12);

foreach my $res (ONO::Lib::Basic->randomize_array(@display_words)) {
$pdf_x = $pdf_x + 28;
if ($pdf_x > 150) {
$pdf_x = 28;
$pdf_y = $pdf_y + 10;
}
my @rp = split(/:/,$res);
@corr_cache = (@corr_cache,"W:$pdf_x:$pdf_y:$rp[1]:");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x,$pdf_y,$rp[1],"c");
}

for (my $y = 0; $y < $max_y; $y++) {
for (my $x = 0; $x < $max_x; $x++) {
if (!$board{"$x,$y"} || $board{"$x,$y"} eq "/") {
my $random = $letters_uc[int(rand(25))];
ONO::Lib::PDF::Draw->text($canvas,$x*7+4.5,65+$y*7,$random,"c");
} else {
my $letter = $board{"$x,$y"};
ONO::Lib::PDF::Draw->text($canvas,$x*7+4.5,65+$y*7,$letter,"c");
@corr_cache = (@corr_cache,"L:$x:$y:$letter:");
}
}
}

}

if ($vars{'output_web'}) {

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

$WEB .= qq~<$form>
<input type="hidden" name="output_web_cache_words" value="$CACHE_WORDS">
<input type="hidden" name="output_web_cache_trans" value="$CACHE_TRANS">
<input type="hidden" name="output_web_cache_imgs" value="$CACHE_IMGS">
~;

if (ONO::FW::Apps::Core->question_progress_done($vars{'app_input_number_of_questions'},$question_number) || $vars{'output_web_finish'}) {

$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->question_progress($vars{'app_input_number_of_questions'},$question_number);

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

# define board size and other settings

my ($max_x,$max_y) = (36,12);
my $max_words = 20;
my $depth = 20;

if (ONO::IO->devstation) {
$max_words = 5;
}

my ($CODE,$display_words_ref,$board_ref) = &search_words_board("",\@WORDLIST,$max_x,$max_y,$max_words,$depth);

$WEB .= $CODE;
my @display_words = @$display_words_ref;
my %board = %$board_ref;

# finally we can draw the board

my $res_count;

$WEB .= qq~<div class="mt10">
<div class="inline auto">
~;

foreach my $res (ONO::Lib::Basic->randomize_array(@display_words)) {
$res_count++;
my @rp = split(/:/,$res);
$WEB .= qq~ <div id="word_display_$rp[0]" class="box_fabric pad10_2 fl ml5 mr5 mb5">$rp[1]</div>~;
}

$stat_todo_total = $res_count;

$WEB .= qq~ </div>
</div>
<div class="inline auto">
<table class="default_table bo mt10">
~;

for (my $y = 0; $y < $max_y; $y++) {
$WEB .= qq~ <tr class="bt bb">~;
for (my $x = 0; $x < $max_x; $x++) {
if (!$board{"$x,$y"} || $board{"$x,$y"} eq "/") {
my $random = $letters_uc[int(rand(25))];
$WEB .= qq~ <td class="p0 bl br center"><div id="board_${x}_${y}" class="block24 cursorlink" onclick="check_board_field($x,$y,0);check_board_mistake();">$random</div></td>~;
} else {
$WEB .= qq~ <td class="p0 bl br center"><div id="board_${x}_${y}" class="block24 cursorlink" onclick="check_board_field($x,$y,1);">$board{"$x,$y"}</div></td>~;
}
}
$WEB .= qq~ </tr>~;
}

$WEB .= qq~ </table>
</div>
<input type="hidden" name="output_web_finish" value="1">
<script>
function check_board_mistake() {
onojs_sethtml('output_web_total_mistakes',Number(onojs_gethtml('output_web_total_mistakes'))+1);

}
function check_board_field(x,y,status) {
if (status == 1) {
onojs_class('board_'+x+'_'+y,'block24 bg_green');

// first, check the main word char

var word_id = onojs_getvalue('board_word_'+x+'_'+y+'_id');
var word_count = onojs_getvalue('board_word_'+word_id+'_chars_id');
word_count = word_count-1;
onojs_setvalue('board_word_'+word_id+'_chars_id',word_count);
onojs_setvalue('board_word_'+x+'_'+y+'_id','0');
onojs_class('word_display_'+word_id,'box_yellow pad5_2 fl ml5 mr5 mb5');
if (word_count < 1) {
onojs_class('word_display_'+word_id,'box_green pad5_2 fl ml5 mr5 mb5');
onojs_sethtml('total_correct',Number(onojs_gethtml('total_correct'))+1);
onojs_sethtml('total_todo',Number(onojs_gethtml('total_todo'))-1);
}

// second, check the alt char option

var word_id = onojs_getvalue('board_word_'+x+'_'+y+'_alt_id');
var word_count = onojs_getvalue('board_word_'+word_id+'_chars_id');
word_count = word_count-1;
onojs_setvalue('board_word_'+word_id+'_chars_id',word_count);
onojs_setvalue('board_word_'+x+'_'+y+'_alt_id','0');
onojs_class('word_display_'+word_id,'box_yellow pad5_2 fl ml5 mr5 mb5');
if (word_count < 1) {
onojs_class('word_display_'+word_id,'box_green pad5_2 fl ml5 mr5 mb5');
onojs_sethtml('total_correct',Number(onojs_gethtml('total_correct'))+1);
onojs_sethtml('total_todo',Number(onojs_gethtml('total_todo'))-1);
}

if (Number(onojs_gethtml('total_todo')) < 1) {
onojs_submit();
}

} else {
onojs_class('board_'+x+'_'+y,'block24 bg_red');
}
}
</script>
~;

# foreach my $word (@sorted) {
# $WEB .= qq~$word<br>~;
# }

}

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

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

}

if ($vars{'output_paper'}) {

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

ONO::FW::Apps::Core->headers($vars_ref,$format,"c");
ONO::FW::Apps::PDF->title($BLK{'Corrections'},$PDF_OPTS,$vars{'app_input_print_options_code'},$format);
# ONO::Lib::PDF::ToolBox->canvas_test($canvas);

foreach my $cache (@corr_cache) {
my @cp = split(/:/,$cache);
if ($cp[0] eq "L") {
ONO::Lib::PDF::Draw->rect($canvas,$cp[1]*7+1,60+$cp[2]*7,$cp[1]*7+8,67+$cp[2]*7,1,"0:150:200:150","0:150:200:150");
ONO::Lib::PDF::Draw->text($canvas,$cp[1]*7+4.5,65+$cp[2]*7,$cp[3],"c");
}
}

&search_words_board_layout("",$canvas);

foreach my $cache (@corr_cache) {
my @cp = split(/:/,$cache);
if ($cp[0] eq "W") {
ONO::Lib::PDF::Draw->text($canvas,$cp[1],$cp[2],$cp[3],"c");
}
}

}

}

}

if ($vars{'output_web'} && !$vars{'output_web_finish'}) {

# ($stat_correct,$stat_todo_total) = ONO::FW::Apps::Core->stats_multi_pages($vars{'app_input_number_of_questions'},$stat_correct,$stat_wrong,$page,$pages,$questions_per_page);

$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);
}

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

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

} else {

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

$WEB .= ONO::FW::Apps::Core->infopics(
"screen:320:50:50::searchwords_screenshot_1.jpg",
"sheet:168:180:110:15:searchwords_sheet_1.jpg",
"sheet:168:40:140:-10:searchwords_sheet_2.jpg",
);

if (!$vars{'app_input_number_of_questions'}) {
$vars{'app_input_number_of_questions'} = 20;
if (ONO::IO->device eq "phone") {
$vars{'app_input_number_of_questions'} = 10;
}
}

my (
$default_ref,$default_lessons,$all_lessons,$custom_lessons,$LU,
) = ONO::FW::Apps::Core::Lang->lang_default_setup_config(\%vars);
my @default = @$default_ref;

$vars{'software_base'} = $BASE;

my $CONF .= ONO::FW::Apps::ToolBox->conf_input("lang", $BLK_ref,\%vars,"ips","$default[0];en:de:fr$LU");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("selections", $BLK_ref,\%vars,"Nl",$BLK{'lists'},$default_lessons,$all_lessons,"$BLK{'Add_a_list'}::$custom_lessons");

# $CONF .= ONO::FW::Apps::ToolBox->conf_input("check", $BLK_ref,\%vars,"S$img_switches","","display_images",$vars{'app_input_display_images'},"$txt[0] ($if_possible)");

$CONF .= ONO::FW::Apps::ToolBox->conf_input("difficulty", $BLK_ref,\%vars,"i",5,3);

$WEB .= ONO::FW::Apps::Core->conf_box(
$db,
$community,
$app,
$form,
"",
$action_url,
$CONF,
$lang,
$BLK_ref,
\%vars,
"op-cd-CD-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'}),
);
}

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

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

}



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

my $VARIATION = ONO::FW::Edu::Apps::Variation->variation();
if ($community eq "school") {
$WEB = ONO::Lib::Encoding::HTML->encode($WEB);
}

return $WEB;

}

sub search_words_board_layout {
ONO::Lib::PDF::Draw->rect($_[1],0,0,170,50,1,"","0:250:230:175",5);
ONO::Lib::PDF::Draw->grid($_[1],1,60,169,186,7,1,1);
}

sub search_words_board {

my (
$self,
$WORDLIST_ref,
$max_x,
$max_y,
$max_words,
$depth,
) = @_;

my @WORDLIST = @$WORDLIST_ref;

# first we'll create a list of words, removing articles

my ($CODE,$DEBUG,@display_words,$words_found,@sorted,%board,%board_occupied,$processed,$placed_total);

for (my $i = 0; $i < 64; $i++) {
my ($img,$word) = split(/\^/,$WORDLIST[$i]);
if ($word && $words_found < $max_words && (length $word < $max_x || length $word < $max_y)) {
$word = ONO::Lib::Lang::ToolBox->remove_article($word);
if ($word !~ m~ ~) {
$words_found++;
@sorted = (@sorted,$word);
}
}
}

# then we will put the words on the board

foreach my $word (ONO::Lib::Basic->array_sortbylength(@sorted)) {

$depth++;
if ($depth > 128) {
$depth = 128;
}

my $word_length = ONO::Lib::Basic->umlaut_string_length($word);
my @letters = ONO::Lib::Basic->umlaut_string_to_array(ONO::Lib::Basic->umlaut_make_uppercase($word));

my ($try_x,$try_y,$vertical,$placed,$whilecounter);

while ($whilecounter < $depth && !$placed) {
$whilecounter++;

# select a random position, horiontal chance is 2:1

my $vertical = 0;
if (($depth+1)%3 == 0) {
$vertical = 1;
}
if ($vertical) {
$try_x = int(rand($max_x));
$try_y = int(rand($max_y-$word_length));
} else {
$try_x = int(rand($max_x-$word_length));
$try_y = int(rand($max_y));
}

# now see if this might fit

if (!$board{"$try_x,$try_y"}) {

my ($ori_x,$ori_y) = ($try_x,$try_y);

my $fail;
foreach my $letter (@letters,"/") {
if (!$fail) {
if ($vertical) {
$try_y++;
} else {
$try_x++;
}
if ($board{"$try_x,$try_y"} && $board{"$try_x,$try_y"} ne $letter) {
$fail++;
}
}
}

# if it fits, then we'll insert the letters

if (!$fail) {
$placed = 1;
$placed_total++;
my $char_count;

$board{"$ori_x,$ori_y"} = "/";
foreach my $letter (@letters) {
$char_count++;
if ($vertical) {
$ori_y++;
} else {
$ori_x++;
}
$board{"$ori_x,$ori_y"} = $letter;
if ($board_occupied{"$ori_x,$ori_y"}) {
$CODE .= qq~<input type="hidden" id="board_word_${ori_x}_${ori_y}_alt_id" name="board_word_${ori_x}_${ori_y}_alt" value="$placed_total">~;
} else {
$CODE .= qq~<input type="hidden" id="board_word_${ori_x}_${ori_y}_id" name="board_word_${ori_x}_${ori_y}" value="$placed_total">~;
}
$board_occupied{"$ori_x,$ori_y"}++
}
if ($vertical) {
$ori_y++;
} else {
$ori_x++;
}
$board{"$ori_x,$ori_y"} = "/";
@display_words = (@display_words,"$placed_total:$word");
$CODE .= qq~<input type="hidden" id="board_word_${placed_total}_chars_id" name="board_word_word_${placed_total}_chars" value="$char_count">~;
}

}
}
}

return ($CODE,\@display_words,\%board);

}

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

1;

__END__