"use strict";

import {Utils} from "../../Utils/Utils.ecma.js";
import {Check} from "../../Utils/Check.ecma.js";
import {FormatModule} from "../FormatModule.ecma.js";

/**
 * @typedef {import("../FormatModule.ecma.js").FormatModule} FormatModule
 */

/**
 * @class RandomFormat
 * @constructor
 * @param {!FormatModule} format 
 * @returns {void}
 * @access public
 * @static
 */
export const RandomFormat = (function(){

    /**
     * @constructs RandomFormat
    * @param {!FormatModule} format 
     * @returns {void}
     * @access private
     * @static
     */
    const RandomFormat = function(format){

        /** @type {RandomFormat} */
        const self = this;

        /**
         * @returns {void}
         * @access private
         */
        const constructor = () => {

            format.modes.rand = format.modes.random = self.get;
            format.checks.rand = format.checks.random = self.check;

            format.modes.Rand = format.modes.Random = ([i, j], inputs, shared, fragments) => self.get([i, j], inputs, {
                ...shared, capitalized : true
            }, fragments);
            format.checks.Rand = format.checks.Random = ([i, j], string, inputs, shared, fragments) => self.check([i, j], string, inputs, {
                ...shared, capitalized : true
            }, fragments);

        };

        /**
         * @param {[number, number]} coordenates 
         * @param {!(string|Array.<any|null>)} inputs 
         * @param {!Object.<string, any|null>} [shared = {}] 
         * @param {!Array.<string>} [fragments = []] 
         * @returns {string}
         * @access public
         */
        this.get = ([i, j], inputs, shared = {}, fragments = []) => {
            
            /** @type {Array.<string>} */
            const items = (
                      Check.is_string(inputs) ? inputs.split("|") : 
                      Check.is_array(inputs[0]) ? inputs[0] : 
                  []), 
                  /** @type {boolean} */
                  has_uniques = shared && shared.uniques_cache;
            /** @type {string} */
            let results;

            for(let _ = 0; _ < 100; _ ++){
                results = format.execute([i, j], Utils.get_random(format.get_list([i, j], items)), shared, fragments);
                if(!has_uniques || !shared.uniques_cache.includes(results))
                    break;
            };
            has_uniques && shared.uniques_cache.push(results);

            return FormatModule.process_item(results, shared);
        };

        /**
         * @param {[number, number]} coordenates 
         * @param {!string} string 
         * @param {!(string|Array.<string>)} inputs 
         * @param {!Object.<string, any|null>} [shared = {}] 
         * @param {!Array.<string>} [fragments = []] 
         * @returns {number}
         * @access public
         */
        this.check = ([i, j], string, inputs, shared = {}, fragments = []) => {

            /** @type {boolean} */
            let has_empty = false;

            for(const option of format.get_list([i, j], (
                Check.is_string(inputs) ? inputs.split("|") : 
                Check.is_array(inputs[0]) ? inputs[0] : 
            []))){
                if(!option){
                    has_empty = true;
                    continue;
                };
                if(/\{[^\{\}]+\}/.test(format.set_fragments_level(option, fragments)))
                    return format.get_check_length([i, j], string, [option], shared, fragments, false);
                
                /** @type {number} */
                let length = FormatModule.prepare_result(string, option, shared);

                if(length != -1)
                    return length;

            };
            return has_empty ? 0 : -1;
        };

        constructor();

    };

    return RandomFormat;
})();