// =================================
// КРОССВОРД (zak, add 07/2024)
// =================================
function CrosswordZak(words, clues, params){
	
    // шаблон
    var tpl = $(`
        <div class="task_crossword_wr" data-type="${params.clue_type}">                
            <div class="task_crossword"></div>
            <div class="task_crossword_error"></div>
            <div class="task_crossword_legend">
                <div><b>По горизонтали</b><ul class="task_crossword_legend_across"></ul></div>
                <div><b>По вертикали</b><ul class="task_crossword_legend_down"></ul></div>
            </div>
        </div>
    `);

    // при фокусе сохраняем сюда текущее значение инпута
    // а при blur - сверяем - изменилось ли оно?
    var input_value = '';

    // json сетки кроссворда (для хранения в БД, чтобы не генерировать каждый раз на лету)
    this.grid = params.grid || null;
    this.legend = params.legend || null;
    
    // тип определений для слов (text или image)
    this.clue_type = params.clue_type || 'text';

    // массив слов и массив подсказок к ним
    this.words = words;
    this.clues = clues;

    

    // jquery-элемент куда будет append() кроссворд
    this.container = params.container;    

    this.correct_words = 0;

    // кол-во попыток генерации сетки
    this.tries = params.tries || 99;

    // показывть placeholder ответов
    this.show_answers = params.show_answers || false;

    // показывть кнопку "рефреша сетки"
    this.show_refresh_button = params.show_refresh_button || false;

    // callback при завершении набора слова
    this.after_finish_word = params.after_finish_word;

    // callback после рефреша сетки
    this.after_grid_refresh = params.after_grid_refresh;
    
    // текущее направление набора и текущее слово
    this.crossword_input_direction = '';
    this.crossword_input_word = '';




    // FUNC: генерируем сетку
    this.generateGrid = function(){        

        var t = this;
  
        var cw = new Crossword(t.words, t.clues);
        var grid = cw.getAllWordGrid(t.tries);

        // возникли проблемы, сетка не сгенерирована
        if(grid == null){

            var bad_words = cw.getBadWords();
            var str = [];
            for(var i = 0; i < bad_words.length; i++){
                str.push(bad_words[i].word);
            }
            var err_text = "Проблема! Кроссворд не может быть создан с этими словами: " + str.join(", ");
            
            $('.task_crossword_error', tpl).html(err_text);
            t.container.append(tpl);
            return;
        }

        // сохраняем сгенерированную сетку в глобальную переменную
        t.grid = grid;

        // получаем легенду
        t.legend = cw.getLegend(grid);  

        return grid;

    }


    // инициализация
    this.init = function(){
        
        var t = this;

        if(t.grid==null){

            // генерируем сетку
            var grid = t.generateGrid();
        
        // берем уже сохраненную сетку
        }else{
            var grid = t.grid;
        }

        var crossword_html = CrosswordUtils.toHtml(grid, t.show_answers);

        $('.task_crossword', tpl).html(crossword_html);

        // добавляем кнопку "рефреш сетки" (если она еще не добавлена)
        if(t.show_refresh_button && tpl.find('.task_crossword_refresh').length==0){
            tpl.append('<button class="task_crossword_refresh" title="Перестроить сетку"><i class="fa fa-refresh"></i></button>');
        }

        // собираем легенду
        if(grid != null){

            legend = t.legend;

            for(var k in legend){
                var html = [];
                for(var i = 0; i < legend[k].length; i++){
                    html.push("<li class='task_crossword_legend_item' data-direction='"+k+"' data-word='"+legend[k][i]['word']+"'><b>" + legend[k][i]['position'] + ".</b> " + legend[k][i]['clue'] + "</li>");
                }

                $('.task_crossword_legend_'+k, tpl).html(html);
            }
        }

        t.container.append(tpl);
        
        
        // EVENTS

        // ------------------
        // EVENT: перестроить сетку   
        // ------------------
        $(tpl).on('click', '.task_crossword_refresh', function(e){

            t.grid = null;
            t.init();

            // калбек после рефреша
            if(typeof t.after_grid_refresh === 'function'){
                t.after_grid_refresh();
            }

        });

        // ------------------
        // EVENT: клик по строке в легенде     
        // ------------------
        $(tpl).on('click', '.task_crossword_legend_item', function(e){

            e.preventDefault();

            // убираем выделение легенды
            t.remove_highlight_legend();

            // выделяем текущий пункт легенды
            $(this).addClass('is_active');

            var direction = e.target.dataset.direction;
            var word = e.target.dataset.word;

            // принудительно устанавливаем напраление
            t.set_direction(direction,word);

            // показываем копию легенды в самом кроссворде
            t.show_inline_legend($(this).html(), word);

            //$('.inline_legend_item', tpl).remove();
            //$('.task_crossword', tpl).append('<div class="inline_legend_item" data-word="'+word+'">'+$(this).html()+'</div>');

            // выделяем все ячейки слова
            $('.crossword_input[data-word-'+direction+'="'+word+'"]', tpl).addClass('is_active');            

            var min_x = 9999999;
            var min_y = 9999999;
            var min_cell;

            // находим "первую" ячейку слова
            $('.crossword_input[data-word-'+direction+'="'+word+'"]', tpl).each(function(){

                var x = $(this).data('x');
                var y = $(this).data('y');

                if(direction=='across'){
                    
                    if(x < min_x){
                        min_cell = $(this);
                        min_x = x;
                    }
                    
                }else if(direction=='down'){

                    if(y < min_y){
                        min_cell = $(this);
                        min_y = y;
                    }

                }                

            });

            // ставим курсор в нее
            if(min_cell){
                min_cell.focus();
            }

        });


        // ------------------
        // EVENT: blur 
        // ------------------    
        $(tpl).on('blur', '.crossword_input', function(e){

            if(e.target.value!=input_value){
                //console.log(`было "${input_value}" стало "${e.target.value}"`);
            }            

            // убираем выделение легенды
            t.remove_highlight_legend();

            // удаляем inline-легенду
            //$('.inline_legend_item', tpl).remove();

        });


        // ------------------
        // EVENT: click     
        // ------------------
        $(tpl).on('click', '.crossword_input', function(e){

            $(this).select();

        });

        // ------------------
        // EVENT: focus     
        // ------------------
        $(tpl).on('focus', '.crossword_input', function(e){

            var word_across     = e.target.dataset.wordAcross;
            var word_down       = e.target.dataset.wordDown;
            var start_word      = e.target.dataset.startWord;
            var val             = e.target.value;

            // сохраняем значение инпута
            input_value = val;

            // получаем текущее направление
            var direction = t.current_direction(word_across, word_down, start_word);

            // уже есть направление (начали набирать слово..) - придерживаемся его
            if(t.crossword_input_direction!=''){

               // направление "битое"? указывает в ту сторону где нет слова
                var tmp_word = t.crossword_input_direction=='across' ? word_across : word_down;
                if(tmp_word){
                    direction = t.crossword_input_direction;                
                }else{
                    t.reset_direction();
                }
            }

            var word = direction=='across' ? word_across : word_down;            

            // подсвечивем слово в легенде
            t.highlight_legend_item(direction, word);            

        });

            
        // ------------------
        // EVENT: ввод буквы 
        // ------------------       
        $(tpl).on('input', '.crossword_input', function(e){             

            // удалили букву из ячейки - ничего не делаем..
            if(e.target.value==''){return false;}

            var x               = +e.target.dataset.x;
            var y               = +e.target.dataset.y;
            var word_across     = e.target.dataset.wordAcross;
            var word_down       = e.target.dataset.wordDown;
            var start_word      = e.target.dataset.startWord;

            // получаем текущее направление
            var direction = t.current_direction(word_across, word_down, start_word);            

            var word = direction=='across' ? word_across : word_down;

            if(t.crossword_input_direction==''){
                t.crossword_input_direction = direction;                
                t.crossword_input_word = word;
            }

            var next_input = t.next_input(t.crossword_input_direction, t.crossword_input_word, x+1, y+1);

            if(next_input.length > 0){

                // если попали на уже решенную ячейку - перепрыгиваем дальше
                if(next_input.hasClass('is_correct')){
                    next_input = t.next_input(t.crossword_input_direction, t.crossword_input_word, x+2, y+2);
                }

                
                // если попали на пробел - перепрыгиваем дальше
                if( next_input.data('letter')==' '){
                    next_input = t.next_input(t.crossword_input_direction, t.crossword_input_word, x+2, y+2);
                }

                // ставим фокус
                if(next_input.length > 0){      
                    next_input.focus().select();  
                }
                
            }

            // проверка
            t.check_word(t.crossword_input_direction, t.crossword_input_word); 
         
        });


        // ------------------
        // EVENT: keydown
        // ------------------
        $(tpl).on('keydown', '.crossword_input', function(e){

            // запрет нажатия "tab"
            if(e.keyCode==9){ 
                e.preventDefault();
                return false;
            }

        });


        // ------------------
        // EVENT: keypress
        // ------------------
        $(tpl).on('keypress', '.crossword_input', function(e){

            var x               = +e.target.dataset.x;
            var y               = +e.target.dataset.y;
            var word_across     = e.target.dataset.wordAcross;
            var word_down       = e.target.dataset.wordDown;
            var start_word      = e.target.dataset.startWord;

            // получаем текущее направление
            var direction = t.current_direction(word_across, word_down, start_word); 
            var word = direction=='across' ? word_across : word_down;

            if(t.crossword_input_direction==''){
                t.crossword_input_direction = direction;                
                t.crossword_input_word = word;
            }

            // предыдущая ячейка                
            var prev_input = t.next_input(t.crossword_input_direction, t.crossword_input_word, x-1, y-1);

            // нажали пробел
            if(e.keyCode==32){
                
                // если в предыдущей ячейке тоже был "пробел" - ничего не делаем
                if(prev_input && prev_input.data('letter')==' '){
                    e.preventDefault();
                    e.stopPropagation();
                    return false;
                }

            }else{

                // если предыдущая ячейка была решенной и сейчас набираем такую же букву - ничего не делаем
                if(prev_input && prev_input.hasClass('is_correct')){
                    if(prev_input.data('letter')==String.fromCharCode(e.keyCode)){
                        e.preventDefault();
                        e.stopPropagation();
                        return false;
                    }
                }
            }

        });


        // ------------------
        // EVENT: перемещение стрелочками, бекспейс
        // ------------------
        $(tpl).on('keyup', '.crossword_input', function(e){

            // обрабатываем только стрелки и бекспейс
            if([8,37,38,39,40].indexOf(+e.keyCode) == -1){
                e.preventDefault();
                return false;
            }
       
            var x               = +e.target.dataset.x;
            var y               = +e.target.dataset.y;
            var word_across     = e.target.dataset.wordAcross;
            var word_down       = e.target.dataset.wordDown;       

            // backspace
            if(e.keyCode==8){
                
                // слева есть ячейки?
                var left_input = $('.crossword_input[data-word-across="'+word_across+'"][data-x="'+(x-1)+'"]', tpl);
                if(left_input.hasClass('is_correct')){
                    left_input = $('.crossword_input[data-word-across="'+word_across+'"][data-x="'+(x-2)+'"]', tpl);
                }

                if(left_input.length > 0){
                    left_input.focus().select();
                } 
                
             }

            // есть слово по горизонтали
            if(word_across){

                // вправо
                if(e.keyCode==39){

                    // справа есть ячейки?
                    var right_input = $('.crossword_input[data-word-across="'+word_across+'"][data-x="'+(x+1)+'"]', tpl);
                    if(right_input.hasClass('is_correct')){
                        right_input = $('.crossword_input[data-word-across="'+word_across+'"][data-x="'+(x+2)+'"]', tpl);
                    }
                    if(right_input.length > 0){
                        right_input.focus().select();

                        // сбрасываем текущее направление
                        t.reset_direction();   
                    }                  

                // влево
                }else if(e.keyCode==37){

                    // слева есть ячейки?
                    var left_input = $('.crossword_input[data-word-across="'+word_across+'"][data-x="'+(x-1)+'"]', tpl);
                    if(left_input.hasClass('is_correct')){
                        left_input = $('.crossword_input[data-word-across="'+word_across+'"][data-x="'+(x-2)+'"]', tpl);
                    }
                    if(left_input.length > 0){
                        left_input.focus().select();

                        // сбрасываем текущее направление
                        t.reset_direction();   
                    }  
                }

            }

            // есть слово по вертикали
            if(word_down){

                // вверх
                if(e.keyCode==38){                    

                    // сверху есть ячейки?
                    var top_input = $('.crossword_input[data-word-down="'+word_down+'"][data-y="'+(y-1)+'"]', tpl);
                    if(top_input.hasClass('is_correct')){
                        top_input = $('.crossword_input[data-word-down="'+word_down+'"][data-y="'+(y-2)+'"]', tpl);
                    }
                    if(top_input.length > 0){
                        top_input.focus().select();

                        // сбрасываем текущее направление
                        t.reset_direction();   

                    }  
                
                // вниз
                } else if(e.keyCode==40){

                    // ниже есть ячейки?
                    var bottom_input = $('.crossword_input[data-word-down="'+word_down+'"][data-y="'+(y+1)+'"]', tpl);
                    if(bottom_input.hasClass('is_correct')){
                        bottom_input = $('.crossword_input[data-word-down="'+word_down+'"][data-y="'+(y+2)+'"]', tpl);
                    }
                    if(bottom_input.length > 0){
                        bottom_input.focus().select();

                        // сбрасываем текущее направление
                        t.reset_direction();   
                    }  
                }

            }

        });

    }



     // ------------------
    // EVENT: клик по inline-легенде
    // ------------------
    $(tpl).on('click', '.inline_legend_item', function(e){
        $(this).remove();
    });


    // FUNCTIONS

    // -----------
    // сбрасываем текущее направление
    // -----------
    this.reset_direction = function(){
        this.crossword_input_direction = '';
        this.crossword_input_word = '';
    }

    // -----------
    // принудительно устанавливем "текущее направление"
    // -----------
    this.set_direction = function(direction, word){
        this.crossword_input_direction = direction;
        this.crossword_input_word = word;
    }

    // -----------
    // FUNC: получаем текущее направление для заполнения, подсветки
    // -----------
    this.current_direction = function(word_across, word_down, start_word){

        var direction = '';

        // только горизонтальное слово
        if(word_across && !word_down){

            direction = 'across';

        // только вертикальное слово
        }else if(!word_across && word_down){

            direction = 'down';

        // в этой ячейке начинается какое-то слово:
        }else if(start_word){                

            // вправо..
            if(start_word==word_across){
                direction = 'across';                
            // вниз..
            }else{
                direction = 'down';             
            }

        // ячейка на перекрестке двух слов
        }else if(word_across && word_down){
           
            direction = 'across';                  
        }

        return direction;

    }


    // -----------
    // FUNC: показываем копию легенды в самом кроссворде
    // -----------
    this.show_inline_legend = function(html, word){
        
        // может она уже добавлена?
        var curr_inline_legend_word = $('.inline_legend_item', tpl).data('word');
        if(curr_inline_legend_word==word){return;}

        // показываем копию легенды в самом кроссворде       
        $('.inline_legend_item', tpl).remove();
        $('.task_crossword', tpl).append('<div class="inline_legend_item" data-word="'+word+'">'+html+'</div>');

    }


    // -----------
    // FUNC: убираем подсветку легенды, ячеек
    // -----------
    this.remove_highlight_legend = function(){

        // легенда
        $('.task_crossword_legend_item', tpl).removeClass('is_active');

        // ячейки
        $('.crossword_input', tpl).removeClass('is_active');

    }


    // -----------
    // FUNC: подсветка легенды, ячеек
    // -----------
    this.highlight_legend_item = function(direction, word){
        
        // убираем выделение легенды
        this.remove_highlight_legend();

        $('.crossword_input[data-word-'+direction+'="'+word+'"]', tpl).addClass('is_active');

        // добавляем выделение нужной строке легенды
        var legend_item = $('.task_crossword_legend_item[data-direction="'+direction+'"][data-word="'+word+'"]', tpl);
        legend_item.addClass('is_active');

        // показываем копию легенды в самом кроссворде        
        this.show_inline_legend(legend_item.html(), word);
        
    }
    

    // -----------
    // FUNC: поиск следующей ячейки
    // -----------
    this.next_input = function(direction, word, x, y){

        var t = this;
        var next_input;

        // правее
        if(direction=='across'){

            next_input = $('.crossword_input[data-word-across="'+word+'"][data-x="'+x+'"]', tpl);

        // ниже
        }else if(direction=='down'){

            next_input = $('.crossword_input[data-word-down="'+word+'"][data-y="'+y+'"]', tpl);
        }

        return next_input;
    }


    // -----------
    // FUNC: проверка слова
    // -----------
    this.check_word = function(direction, word){

        var t = this;
        var user_word = '';
        var empty_cell = 0;
        var status = '';
            
        // получаем все буквы слова
        $('.crossword_input[data-word-'+direction+'="'+word+'"]', tpl).each(function(){
            var val = $(this).val();
            var letter = $(this).data('letter');

            // в текущей ячейке пробел, а юзер ничего в нее не ввел
            // [это нормальная ситуация, пробел не учитываем в "ошибки"]
            if(val=='' && letter!=' '){
                empty_cell++;
            }
            user_word += $(this).val();        
        });

        // слово полностью введено
        if(empty_cell==0){
            
            // сбрасываем текущее направление
            t.reset_direction();    
            
            // проверяем правильность введенного слова
            // (вырезаем все пробелы, чтобы корректно сравнивть слов содержащие пробел)
            if(user_word==word.replace(/\s/g, '')){

                $('.crossword_input', tpl).blur();
                $('.crossword_input[data-word-'+direction+'="'+word+'"]', tpl).addClass('is_correct');
                $('.task_crossword_legend_item[data-direction="'+direction+'"][data-word="'+word+'"]', tpl).addClass('is_correct');
                
                // удаляем inline-легенду
                $('.inline_legend_item', tpl).remove();

                status = 'correct';   

                // увеличиваем счетчик правильно решенных слов
                t.correct_words++;
                
                // убираем выделение легенды
                t.remove_highlight_legend();

            // слово ошибочно
            }else{
                status = 'error';
            }        

            // вызываем калбек
            t.after_finish_word(t, user_word, status);

        }

    }

}