//1. Store Class
// Переделайте задание Store на синтаксис ES6-классов:
// {
//
// }class Store {
//     #reducer;
//     #state;
//     #cbs = []
//
//     constructor(reducer) {
//         this.#reducer = reducer
//         this.#state = (undefined, {} )
//     }
//     getState () {
//         return this.#state
//     }
//
//     subscribe (cb) {
//         this.#cbs.push(cb)
//         return (cb) => {
//             this.#cbs = this.#cbs.filter(c => c !== cb)
//         }
//     }
//
//     dispatch (action) {
//         const newState = this.#reducer(this.#state, action)
//         if (newState !== this.#state) {
//             this.#state = newState
//             for (let cb of this.#cbs)
//                 cb()
//         }
//     }
//
//     get state () {
//         return this.#state
//     }
// }


//2. Password Class
// По аналогии, переделайте код задания Password в синтаксис классов ES6. Спрячьте все что можно в #приватные свойства
// объектов класса. Проверьте на форме логина - ведь она использует Password
// {
//     class Password {
//         #parent
//         #open
//         #inputPassword = document.createElement('input')
//         #btnSee = document.createElement('input')
//         #onChange
//         #onOpenChange
//
//         onchange = () => this.setOpen(this.#btnSee.checked)
//
//         constructor(parent, open) {
//             this.#parent = parent
//             this.#open = open
//
//
//             this.#parent.append(this.#inputPassword)
//             this.#btnSee.type = 'checkbox'
//             this.#parent.append(this.#btnSee)
//             this.#btnSee.onchange = this.onchange
//         }
//
//         set onChange(value) {
//             this.#onChange = value
//         }
//
//         set onOpenChange(value) {
//             this.#onOpenChange = value
//         }
//
//         setValue(value) {
//             this.#inputPassword.value = value
//         }
//
//         setOpen(open) {
//             this.#open = open
//             this.#inputPassword.type = this.#open ? 'text' : 'password'
//         }
//
//         getValue() {
//             return this.#inputPassword.value
//         }
//
//         getOpen() {
//             return this.#inputPassword.type
//         }
//     }
//
//     const p = new Password(document.body, true)
//     p.onChange = data => console.log(data)
//     p.onOpenChange = open => console.log(open)
//
//     p.setValue('qwerty')
//     console.log(p.getValue())
//
//     p.setOpen(false)
//     console.log(p.getOpen())
// }

// StoreThunk Class
// Унаследуйте класс Store в новом классе StoreThunk. Новый класс должен перекрывать метод dispatch, проверять тип
// переданного экшона и если это функция, запускать её, передав у неё this.dispatch и this.getState. Данное условие
// написано тут. Учтите, что в thunk передаются функции dispatch и getState без объекта до точечки, а эти методы в
// классе Store являются обычными функциями, склонными к потере this. Для прибития this намертво к функции используйте
// метод bind. Посмотреть можно тут и тут Проверьте на модульном проекте
{
    class Store {
        #reducer;
        #state;
        #cbs = []

        constructor(reducer){
            this.#reducer = reducer
            this.#state = reducer(undefined, {})
        }

        getState(){
            return this.#state
        }

        subscribe(cb){
            (this.#cbs.push(cb), () => this.#cbs = this.#cbs.filter(c => c !== cb))
        }

        dispatch(action){
            let newState = this.#reducer (this.#state,action)
            if (newState !== this.#state) {
                this.#state = newState
                for ( let cb of this.#cbs) {
                    cb ()
                }
            }
        }

        get state () {
            return this.#state
        }
    }

    class StoreThunk  extends Store {
        dispatch(action) {
            //если это функция,
            if (typeof action === 'function') {
                //запускать её, передав у неё this.dispatch и this.getState; используйте метод bind
                return action(this.dispatch.bind(this), this.getState.bind(this))
            } else {
                super.dispatch(action)
            }
        }
    }
}

//RGB Class
// Напишите класс RGB, приватными свойствами которого являются три числа #r, #g, #b. Класс должен обладать следующими
// геттерами и сеттерами:

class RGB {
    #r;
    #g;
    #b;

    set r (newR) {
        if (typeof newR !="number" || !(newR>= 0 && newR<=255)) {
            throw new RangeError('Неправильный формат или диапазон')
        } else {
            this.#r = newR
        }
    }

    set g (newG) {
        if (typeof newG !="number" || !(newG>= 0 && newG<=255)) {
            throw new RangeError('Ошибка в типе значений')
        } else {
            this.#g = newG
        }
    }

    set b (newB) {
        if (typeof newB != 'number' || !(newB>= 0 && newB<=255)) {
            throw new RangeError('Ошибка в типе значений')
        } else {
            this.#b = newB
        }
    }

    set rgb (newRgb) {
        let matchColors = /rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)/;
        let match = matchColors.exec(newRgb)
        if (match != null) {
            this.#r = parseInt(match[1])
            this.#g = parseInt(match[2])
            this.#b = parseInt(match[3])
            return
        } else {
            throw new SyntaxError('Вы пытаетесь интерпретировать синтаксически неправильный код')
        }
    }

    set hex (newHex){
        let matchColors = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/
        let match = matchColors.exec(newHex)
        if (match !== null) {
            this.#r = parseInt(match[1], 16)
            this.#g = parseInt(match[2], 16)
            this.#b = parseInt(match[3], 16)
            return
        } else {
            throw new SyntaxError('Вы пытаетесь интерпретировать синтаксически неправильный код')
        }
    }

    ////////

    get r (){
        return this.#r
    }

    get g (){
        return this.#g
    }

    get b (){
        return this.#b
    }

    get rgb (){
        return `rgb(${this.#r},${this.#g},${this.#b})`
    }

    get hex (){
        return `#${this.componentToHex(this.#r)}${this.componentToHex(this.#g)}${this.componentToHex(this.#b)}`
    }

    componentToHex(color) {
        let hex = color.toString(16)
        return hex.length == 1 ? "0" + hex : hex
    }
}

const rgb = new RGB
rgb.r = 15
rgb.g = 128
rgb.b = 192
console.log(rgb.hex) //#0F80C0
console.log(rgb.rgb) //rgb(15,128,192)
rgb.hex = '#203040'
console.log(rgb.rgb) //rgb(32, 48, 64)
rgb.rgb = 'rgb(100, 90, 50)'
console.log(rgb.r, rgb.g, rgb.b) //100, 90, 50

rgb.hex = 'дичь' //SyntaxError
rgb.b   = 1000   //RangeError
