// Person Constructor
// Переделайте задание createPerson на функцию конструктор Person.
// Для этого методы и свойства заносите не в создаваемый объект, а в this внутри конструктора.
person_constructor: {
  function Person(name, surname) {
    this.name = name;
    this.surname = surname;
    this.getFullName = () => {
      let fullName = this.fatherName ? this.name + ' ' + this.fatherName + ' ' + this.surname
        : this.name + ' ' + this.surname;
      return fullName;
    };
  }
  const a = new Person("Вася", "Пупкин")
  const b = new Person("Анна", "Иванова")
  const c = new Person("Елизавета", "Петрова")

  console.log(a.getFullName()); //Вася Пупкин
  a.fatherName = 'Иванович';    //Вася Иванович Пупкин
  console.log(a.getFullName());
  console.log(b.getFullName()); //Анна Иванова
}

// Person Prototype
// Переделайте предыдущее задание, вынеся методы в прототип. Для этого вместо присвоения в this занесите их в
// объект Person.prototype. После этой переделки все должно работать по старому:
person_prototype: {
  function Person(name, surname) {
    this.name = name;
    this.surname = surname;
    Person.prototype.getFullName = function () {
      let fullName = this.fatherName ? this.name + ' ' + this.fatherName + ' ' + this.surname
        : this.name + ' ' + this.surname;
      return fullName;
    }
  }
  const a = new Person("Вася", "Пупкин");
  const b = new Person("Анна", "Иванова");
  const c = new Person("Елизавета", "Петрова");

  console.log(a.getFullName()); //Вася Пупкин
  a.fatherName = 'Иванович';    //Вася Иванович Пупкин
  console.log(a.getFullName());
  console.log(b.getFullName()); //Анна Иванова
}

// Store
// Переделайте функцию createStore (та, которая хранилище Redux) на конструктор Store. Прототип не используйте;
// оставьте переменные state, cbs и reducer замкнутыми. Соответственно методы subscribe, dispatch и getState
// должны быть объявлены внутри функции-конструктора и не могут быть в прототипе. Проверьте переделанный
// конструктор на вашем ДЗ по ларьку;
store: {
  function reducer(state, { type, name, amount, money }) { //объект action деструктуризируется на три переменных
    if (!state) { //начальная уборка в ларьке:
      return {
        products: {
          пиво: {
            amount: 100,
            price: 30,
          },
          чипсы: {
            amount: 100,
            price: 25,
          },
          сиги: {
            amount: 100,
            price: 35,
          }
        },
        balance: {
          amount: 0
        }
      }
    }
    if (type === 'buy') { //если тип action - КУПИТЬ, то:
      if (amount > state.products[name].amount) {
        alert('You have entered more quantity than is available');
        return {
          ...state,
        }
      }
      if (money < amount * state.products[name].price) {
        alert('You don`t have enought money');
        return {
          ...state,
        }
      }
      if (money > amount * state.products[name].price) {
        alert('You gave more money');
        return {
          ...state,
        }
      }
      let updatedProducts = state.products
      let updatedField = updatedProducts[name];
      updatedField.amount -= amount;
      let updatedBalance = state.balance;
      updatedBalance.amount += +money;
      return {
        ...state, //берем все что было из ассортимента
        products: updatedProducts, //и уменьшаем то, что покупается на количество
        balance: updatedBalance,
      }

    }

    return state; //если мы не поняли, что от нас просят в `action` - оставляем все как есть
  }
  function СreateStore(reducer) {
    let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
    let cbs = [];                     //массив подписчиков

    this.getState = () => state;            //функция, возвращающая переменную из замыкания

    this.subscribe = cb => (cbs.push(cb),   //запоминаем подписчиков в массиве
      () => cbs = cbs.filter(c => c !== cb)); //возвращаем функцию unsubscribe, которая удаляет подписчика из списка

    this.dispatch = action => {
      const newState = reducer(state, action); //пробуем запустить редьюсер
      if (newState !== state) { //проверяем, смог ли редьюсер обработать action
        state = newState; //если смог, то обновляем state 
        for (let cb of cbs) cb(); //и запускаем подписчиков
      }
    }
  }
  const store = new СreateStore(reducer);

  //запомнит функцию во внутреннем массиве cbs. 
  //она будет запущена при любом успешном dispatch 
  const unsubscribe = store.subscribe(() => console.log(store.getState()));

  setTimeout(unsubscribe, 10000); //отпишемся через 10 секунд, например

  const buyAction = (name, amount, money) => ({ type: 'buy', name, amount, money });
  store.dispatch(buyAction('пиво', 1, 30));
}

// Password
// Напишите функцию конструктор Password, которая будет в родительском элементе создавать поле ввода для
// пароля и кнопку/иконку/чекбокс, который будет переключать режим просмотра пароля в поле ввода. (видимый
// пароль или нет, input type='text' или же input type='password')
// Параметры:
// parent - родительский элемент
// open - стартовое состояние
// Методы:
// setValue/getValue - задают/читают значения
// setOpen/getOpen - задают/читают открытость текста в поле ввода
// Колбэки (функции обратного вызова, который возможно, будут заданы снаружи конструктора):
// onChange - запускается по событию oninput в поле ввода, передает текст в колбэк
// onOpenChange - запускается по изменению состояния открытости пароля
password: {
  function Password(parent, open) {
    let passInputEl = document.createElement('input');
    parent.append(passInputEl);
    let passVisibilityCheckboxEl = document.createElement('input');
    passVisibilityCheckboxEl.type = 'checkbox';
    passVisibilityCheckboxEl.checked = open;
    parent.append(passVisibilityCheckboxEl);
    if (open) {
      passInputEl.type = 'text';
    } else {
      passInputEl.type = 'password';
    }
    passVisibilityCheckboxEl.addEventListener('change', (event) => {
      if (event.currentTarget.checked) {
        passInputEl.type = 'text';
      } else {
        passInputEl.type = 'password';
      }
      this.onOpenChange(event.currentTarget.checked);
    });
    passInputEl.addEventListener('input', (event) => {
      this.onChange(event.currentTarget.value);
    });
    this.setValue = (value) => {
      passInputEl.value = value;
    }
    this.getValue = () => {
      return passInputEl.value;
    }
    this.setOpen = (value) => {
      passVisibilityCheckboxEl.checked = value;
    }
    this.getOpen = () => {
      return passVisibilityCheckboxEl.checked;
    }
  }

  let 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());
}

// LoginForm
// С помощью предыдущего кода Password сделайте форму логина, кнопка в которой будет активна только если и
// login и пароль не пусты.
// "С помощью предыдущего кода" значит что в коде формы логина вы используете объект, сконструированный
// конструктором Password - const password = new Password(........)
login_form: {
  let form = document.createElement('form');
  document.body.append(form);
  let loginLabel = document.createElement('label');
  loginLabel.innerText = 'Login: ';
  form.append(loginLabel);
  let login = new Login(form);
  let passwordLabel = document.createElement('label');
  passwordLabel.innerText = 'Password: ';
  form.append(passwordLabel);
  let password = new Password(form, false);
  let submit = document.createElement('button');
  submit.innerText = 'Submit';
  form.append(submit);
  function validateForm() {
    if (login.getValue() == '' || password.getValue() == '') {
      submit.disabled = true;
    } else {
      submit.disabled = false;
    }
  }
  validateForm();
  login.onChange = validateForm;
  password.onChange = validateForm;

  function Login(parent) {
    let loginInputEl = document.createElement('input');
    loginInputEl.type = 'text';
    parent.append(loginInputEl);
    loginInputEl.addEventListener('input', (event) => {
      this.onChange();
    });
    this.setValue = (value) => {
      loginInputEl.value = value;
    }
    this.getValue = () => {
      return loginInputEl.value;
    }
  }
  function Password(parent, open) {
    let passInputEl = document.createElement('input');
    parent.append(passInputEl);
    let passVisibilityCheckboxEl = document.createElement('input');
    passVisibilityCheckboxEl.type = 'checkbox';
    passVisibilityCheckboxEl.checked = open;
    parent.append(passVisibilityCheckboxEl);
    if (open) {
      passInputEl.type = 'text';
    } else {
      passInputEl.type = 'password';
    }
    passVisibilityCheckboxEl.addEventListener('change', (event) => {
      if (event.currentTarget.checked) {
        passInputEl.type = 'text';
      } else {
        passInputEl.type = 'password';
      }
      this.onOpenChange(event.currentTarget.checked);
    });
    passInputEl.addEventListener('input', (event) => {
      this.onChange();
    });
    this.setValue = (value) => {
      passInputEl.value = value;
    }
    this.getValue = () => {
      return passInputEl.value;
    }
    this.setOpen = (value) => {
      passVisibilityCheckboxEl.checked = value;
    }
    this.getOpen = () => {
      return passVisibilityCheckboxEl.checked;
    }
  }

}

// LoginForm Constructor
// оформите предыдущую задачу как функцию-конструктор. Продумайте и предусмотрите геттеры, сеттеры и колбэки.
login_form_constructor: {
  function LoginForm() {
    let form = document.createElement('form');
    document.body.append(form);
    let loginLabel = document.createElement('label');
    loginLabel.innerText = 'Login: ';
    form.append(loginLabel);
    let login = new Login(form);
    let passwordLabel = document.createElement('label');
    passwordLabel.innerText = 'Password: ';
    form.append(passwordLabel);
    let password = new Password(form, false);
    let submit = document.createElement('button');
    submit.innerText = 'Submit';
    form.append(submit);
    this.validateForm = () => {
      if (login.getValue() == '' || password.getValue() == '') {
        submit.disabled = true;
      } else {
        submit.disabled = false;
        this.onValidForm();
      }
    }
    submit.addEventListener('click', (e) => {
      e.preventDefault();
      this.onSubmitForm();
    });
    this.getLoginValue = () => {
      return login.getValue();
    }
    this.setLoginValue = (value) => {
      login.setValue(value);
    }
    this.getPasswordValue = () => {
      return password.getValue();
    }
    this.setPasswordValue = (value) => {
      password.setValue(value);
    }
    this.validateForm();
    login.onChange = this.validateForm;
    password.onChange = this.validateForm;
  }

  let loginForm = new LoginForm();
  loginForm.onValidForm = () => console.log('Form is valid');
  loginForm.onSubmitForm = () => console.log('Form is submitted');


  function Login(parent) {
    let loginInputEl = document.createElement('input');
    loginInputEl.type = 'text';
    parent.append(loginInputEl);
    loginInputEl.addEventListener('input', (event) => {
      this.onChange();
    });
    this.setValue = (value) => {
      loginInputEl.value = value;
    }
    this.getValue = () => {
      return loginInputEl.value;
    }
  }
  function Password(parent, open) {
    let passInputEl = document.createElement('input');
    parent.append(passInputEl);
    let passVisibilityCheckboxEl = document.createElement('input');
    passVisibilityCheckboxEl.type = 'checkbox';
    passVisibilityCheckboxEl.checked = open;
    parent.append(passVisibilityCheckboxEl);
    if (open) {
      passInputEl.type = 'text';
    } else {
      passInputEl.type = 'password';
    }
    passVisibilityCheckboxEl.addEventListener('change', (event) => {
      if (event.currentTarget.checked) {
        passInputEl.type = 'text';
      } else {
        passInputEl.type = 'password';
      }
      this.onOpenChange(event.currentTarget.checked);
    });
    passInputEl.addEventListener('input', (event) => {
      this.onChange();
    });
    this.setValue = (value) => {
      passInputEl.value = value;
    }
    this.getValue = () => {
      return passInputEl.value;
    }
    this.setOpen = (value) => {
      passVisibilityCheckboxEl.checked = value;
    }
    this.getOpen = () => {
      return passVisibilityCheckboxEl.checked;
    }
  }
}

// Password Verify
// С помощью Password сделайте пару инпутов, которые проверяют введеный пароль (в двух полях ввода) на
// совпадение. Подсвечивайте поля красным цветом/бордером когда пароли не совпадают При открытом пароле в
// первом поле ввода (которое реализуется с помощью объекта класса Password второе поле вводы должно пропадать
// с экрана Таким образом:
// Когда Password в скрытом режиме - появляется второй инпут (<input type='password'>) с паролем в скрытом режиме
// Когда Password в открытом режиме - второй инпут пропадает
password_verify: {
  function PasswordVerify() {
    let passVerifyTask = document.createElement('div');
    document.body.append(passVerifyTask);
    let password = new Password(passVerifyTask, false);
    let passwordCheck = new Password(passVerifyTask, false);
    password.onChange = validatePasswords;
    passwordCheck.onChange = validatePasswords;
    function validatePasswords(value) {
      const isInvalid = password.getIsTouched() && passwordCheck.getIsTouched() && password.getValue() !== passwordCheck.getValue();
      password.setIsValid(!isInvalid);
      passwordCheck.setIsValid(!isInvalid);
    }
    password.onOpenChange = (value) => {
      passwordCheck.setIsHidden(value);
    }

  }

  let passVerify = new PasswordVerify();

  function Password(parent, open) {
    let passInputEl = document.createElement('input');
    parent.append(passInputEl);
    let passVisibilityCheckboxEl = document.createElement('input');
    passVisibilityCheckboxEl.type = 'checkbox';
    passVisibilityCheckboxEl.checked = open;
    parent.append(passVisibilityCheckboxEl);
    let isValid = true;
    let isTouched = false;
    let isHidden = false;
    if (open) {
      passInputEl.type = 'text';
    } else {
      passInputEl.type = 'password';
    }
    passVisibilityCheckboxEl.addEventListener('change', (event) => {
      if (event.currentTarget.checked) {
        passInputEl.type = 'text';
      } else {
        passInputEl.type = 'password';
      }
      this.onOpenChange(event.currentTarget.checked);
    });
    passInputEl.addEventListener('input', (event) => {
      this.setIsTouched(true);
      this.onChange(event.currentTarget.value);
    });
    this.getIsHidden = () => {
      return isHidden;
    }
    this.setIsHidden = (value) => {
      isHidden = value;
      isHidden ? passInputEl.classList.add('hidden') : passInputEl.classList.remove('hidden');
      isHidden ? passVisibilityCheckboxEl.classList.add('hidden') : passVisibilityCheckboxEl.classList.remove('hidden');
    }
    this.getIsTouched = () => {
      return isTouched;
    }
    this.setIsTouched = (value) => {
      isTouched = value;
      isTouched ? passInputEl.classList.add('touched') : passInputEl.classList.remove('touched');
    }
    this.setValue = (value) => {
      passInputEl.value = value;
    }
    this.getValue = () => {
      return passInputEl.value;
    }
    this.setOpen = (value) => {
      passVisibilityCheckboxEl.checked = value;
    }
    this.getOpen = () => {
      return passVisibilityCheckboxEl.checked;
    }
    this.getIsValid = () => {
      return isValid;
    }
    this.setIsValid = (value) => {
      isValid = value;
      isValid ? passInputEl.classList.remove('invalid') : passInputEl.classList.add('invalid');
    }
  }
}