/*makeProfileTimer
Напишите функцию makeProfileTimer, которая служит для замера времени выполнения другого кода и работает следующим образом:*/

/*var timer = makeProfileTimer()
alert('Замеряем время работы этого alert');  //некий код, время выполнения которого мы хотим измерить с высокой точностью
alert(timer()); //alert должен вывести время в микросекундах от выполнения makeProfileTimer до момента вызова timer(), 
                // т. е. измерить время выполнения alert
//Используйте performance.now() */

function makeProfileTimer() {
    const t0 = performance.now()
    return () => {
        const t1 = performance.now()
        return t1 - t0
    }
}

var timer = makeProfileTimer()
alert('Замеряем время работы этого alert')
alert(timer())

/*makeSaver
Напишите функцию makeSaver, которая:*/
{
    function makeSaver (f) {
        const fResult = f()
        function result(){
            return fResult
        }
        return result
    }

    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 был только один раз`)
}
/*
Таким образом makeSaver решает две задачи:
Навсегда сохраняет результат функции. Это актуально, например, для Math.random.
Действует лениво, то есть вызывает Math.random только тогда, когда результат действительно нужен. Если же по каким-то причинам значение не понадобится, то Math.random даже не будет вызван*/


/*myBind
Изучите встроенную функцию bind, и сделайте свою версию, которая позволит определить "значение по умолчанию" не только для первых параметров, но для любых других, например для степени в Math.pow:
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'
Массив, который идет третьим параметром определяет, какие поля должны подменяться значением по умолчанию, а какие - задаваться в последствии (undefined).*/



{
    function sum(a, b, c, d) { return a + b + c + d }

    function myBind(func, context, args) {
        return function funcResult(...lostArgs) {
            let combinedArgs = []
            for (let i of args) {
                if (i === undefined) {
                    combinedArgs.push(lostArgs[0])
                    lostArgs.shift()
                } else {
                    combinedArgs.push(i)
                }
            }
            return func.apply(context, combinedArgs)
        }
    }

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

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

    var chessMin = myBind(Math.min, Math, [, 4, , 5,, 8,, 9])
console.log(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'
console.log(bindedJoiner('a','c','d') === 'abcdef')
console.log(bindedJoiner('1','2','3') === '1b23ef')


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

function checkResult(original, validator){
    

    function wrapper(...params){
         let result = original(...params)
        if (!validator(result)) {
            return wrapper(...params)
        }
        return result
    }
    
    return wrapper
}


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

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

const randomHigh = checkResult(Math.random, x => x >= 0.5 && x <=1 ? true : false )

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

//respectMe. Достает пользователя запуском этой фунцкии, пока какое-то из полей не введено*/
const respectMe = checkResult(person, validatorCredentials)

function person() {

    let trimAndCapitalize = (str) => {
        if (str) {
            return str.trim()[0].toUpperCase() + str.trim().slice(1).toLowerCase()
        }
    }

    let surname = trimAndCapitalize(
        prompt('введите вашу фамилию.\nMожете добавить пару пробелов в конце или начале. Написать с маленькой буквы или даже сделать пару букв в середине фамилии большими, я все поправлю!')
    );

    let name = trimAndCapitalize(
        prompt('введите ваше имя')
    );

    let fatherName = trimAndCapitalize(
        prompt('введите ваше отчество')
    );

    let fullName = surname + " " + name + " " + fatherName;

    return {
        name,
        surname,
        fatherName,
        fullName
    }
}


function validatorCredentials({ name, surname, fatherName }) {
    if (!name || !isNaN(+name)) return false
    if (!surname || !isNaN(+surname)) return false
    if (!fatherName || !isNaN(+fatherName)) return false
    return true
}
}
