// Материал
// Как и всегда, почитайте мануал, позапускайте примерчики, найдите ошибки, ощутите множественность Scope.
{
    // done
}


// Предыдущие ДЗ
// Изучите предыдущие несколько ДЗ и найдите где там применяются замыкания.Особенно это касается задач, связанных с DOM, да createPersonClosure не просто так имеет в названии слово "Closure".
{
    // done
}


// makeProfileTimer
// Напишите функцию makeProfileTimer, которая служит для замера времени выполнения другого кода и работает следующим образом:
// Используйте performance.now()
{
    function makeProfileTimer() {
        const start = performance.now()

        function endTime() {
            return performance.now() - start
        }

        return endTime
    }

    // проверка
    var timer = makeProfileTimer()
    alert('Замеряем время работы этого alert')
    alert(`Время выполнения кода: ${Math.round(timer())} мс`)
}


// makeSaver
// Напишите функцию makeSaver, которая:
// Таким образом makeSaver решает две задачи:
//      Навсегда сохраняет результат функции.Это актуально, например, для Math.random.
//      Действует лениво, то есть вызывает Math.random только тогда, когда результат действительно нужен.Если же по каким - то причинам значение не понадобится, то Math.random даже не будет вызван
{
    function makeSaver(func) {
        const start = func()

        function saver() {
            return start
        }

        return saver
    }

    // Проверка раз
    var saver = makeSaver(Math.random) //создает функцию-хранилище результата переданной в качестве параметра функции (Math.random в примере). На этом этапе Math.random НЕ вызывается
    var value1 = saver()              //saver вызывает переданную в makeSaver функцию, запоминает результат и возвращает его
    var value2 = saver()              //saver в дальнейшем просто хранит результат функции, и более НЕ вызывает переданную в makeSaver функцию;
    value1 === value2                 // всегда true

    var saver2 = makeSaver(() => console.log('saved function called') || [null, undefined, false, '', 0, Math.random()][Math.ceil(Math.random() * 6)])
    var value3 = saver2()
    var value4 = saver2()
    value3 === value4 // тоже должно быть true

    // Проверка два
    let namePrompt = prompt.bind(window, 'Как тебя зовут?')
    let nameSaver = makeSaver(namePrompt)
    alert(`Привет! Prompt еще не было!`)
    alert(`Привет ${nameSaver()}. Только что запустился prompt, первый и последний раз`)
    alert(`Слушай, ${nameSaver()}, го пить пиво. Ведь prompt был только один раз`)
}


// myBind
// Изучите встроенную функцию bind, и сделайте свою версию, которая позволит определить "значение по умолчанию" не только для первых параметров, но для любых других, например для степени в Math.pow:
// Массив, который идет третьим параметром определяет, какие поля должны подменяться значением по умолчанию, а какие - задаваться в последствии(undefined).
{
    function myBind(func, zis, params) {
        function bind(...addParams) {
            let newParams = [...params]

            for (let i = 0; i < params.length; i++) {
                if (params[i] === undefined) {
                    newParams[i] = addParams.shift()
                }
            }

            return func.bind(zis)(...newParams)
        }

        return bind
    }

    // Проверка
    var pow5 = myBind(Math.pow, Math, [, 5]) // первый параметр - функция для биндинга значений по умолчанию, 
    // второй - this для этой функции, третий - массив, в котором undefined означает параметры, которые должны передаваться при вызове,
    // а другие значения являются значениями по умолчанию:
    var cube = myBind(Math.pow, Math, [, 3]) // cube возводит число в куб

    pow5(2) // => 32, вызывает Math.pow(2,5), соотнесите с [undefined, 5]
    cube(3) // => 27


    var chessMin = myBind(Math.min, Math, [, 4, , 5, , 8, , 9])
    chessMin(-1, -5, 3, 15) // вызывает Math.min(-1, 4, -5, 5, 3, 8, 15, 9), результат -5



    var zeroPrompt = myBind(prompt, window, [undefined, "0"]) // аналогично, только теперь задается "0" как текст по умолчанию в prompt, 
    // а текст приглашения пользователя задается при вызове zeroPrompt
    var someNumber = zeroPrompt("Введите число")              // вызывает prompt("Введите число","0")

    const bindedJoiner = myBind((...params) => params.join(''), null, [, 'b', , , 'e', 'f'])//('a','c','d') === 'abcdef'
    bindedJoiner('a', 'c', 'd') === 'abcdef'
    bindedJoiner('1', '2', '3') === '1b23ef'
}


// checkResult
// Напишите декоратор checkResult, который:
//      принимает функцию для запуска и проверки результата(оригинал)
//      принимает функцию для проверки результата(валидатор)
//      возвращает обертку, которая запускает оригинал до тех пор, пока оригинал не вернет значение, удовлетворяющее функции - валидатору.В валидатор передается результат оригинальной функции.Если валидатор возвращает true, то обертка возвращает результат оригинальной функции.Если валидатор возвращает что - то другое, то оригинал запускается еще, пока валидатор не вернет true.

// Используя checkResult сделайте функции, которые:
//      randomHigh.Возвращает случайное число в диапазоне от 0.5 до 1
//      alwaysSayYes.Достает пользователя окном confirm пока он не согласится(не нажмет OK)
//      respectMe.Достает пользователя запуском этой фунцкии, пока какое - то из полей не введено
{
    function checkResult(original, validator) {
        function wrapper(...params) {
            let res = original(...params)
            do {
                res = original.apply(this, params)
            } while (!(validator(res)))

            return res
        }

        return wrapper
    }

    // Проверка
    //numberPrompt - это функция, которая будет запускать prompt до тех пор, пока пользователь не введет число
    //параметры передаются насквозь в оригинал. Не забудьте передать this, используя call или apply
    const numberPrompt = checkResult(prompt, x => !isNaN(+x))
    let number = +numberPrompt("Введите число", "0")

    //gamePrompt - это функция, которая будет запускать prompt до тех пор, пока пользователь не введет одно из слов 'камень', 'ножницы', 'бумага'
    const gamePrompt = checkResult(prompt, x => ['камень', 'ножницы', 'бумага'].includes(x.toLowerCase()))
    const turn = gamePrompt("Введите что то из списка: 'камень', 'ножницы', 'бумага'")

    // randomHigh.Возвращает случайное число в диапазоне от 0.5 до 1
    const randomHigh = checkResult(Math.random, x => x >= 0.5 && x <= 1 ? true : false)
    console.log(randomHigh())

    // alwaysSayYes.Достает пользователя окном confirm пока он не согласится(не нажмет OK)
    const alwaysSayYes = checkResult(confirm, x => x)
    alwaysSayYes()

    // respectMe.Достает пользователя запуском этой фунцкии, пока какое - то из полей не введено
    // НО ПРОБЕМА - из-за коряво написанной функции enterPersonData невозможно понять, работает ли проверка, из-за того, что в enterPersonData невозможно нажать отправить пустую строку или отказаться от ввода данных - сразу ломается trim
    const enterPersonData = () => {
        const arr = {}

        const name = (prompt('Ввеедиет Ваше Имя в поле ниже')).trim()
        arr.name = `${name[0].toUpperCase()}${name.slice(1).toLowerCase()}`

        const surname = (prompt('Ввеедиет Вашу Фамилию в поле ниже')).trim()
        arr.surname = `${surname[0].toUpperCase()}${surname.slice(1).toLowerCase()}`

        const fatherName = (prompt('Ввеедиет Ваше Отчество в поле ниже')).trim()
        arr.fatherName = `${fatherName[0].toUpperCase()}${fatherName.slice(1).toLowerCase()}`

        const fullName = `${name[0].toUpperCase()}${name.slice(1).toLowerCase()} ${surname[0].toUpperCase()}${surname.slice(1).toLowerCase()} ${fatherName[0].toUpperCase()}${fatherName.slice(1).toLowerCase()}`
        arr.fullName = fullName

        return arr
    }

    function verification(arr) {
        console.log(arr.name)
        if (arr.name) return true
        if (arr.surname) return false
        if (arr.fatherName) return false

        return false
    }

    const respectMe = checkResult(enterPersonData, verification)
    respectMe()
}