import { bindable, bindingMode, computedFrom, customElement, TaskQueue } from "aurelia-framework";
import { UIInternal } from './../../utils/ui-internal';

@customElement("time-page")
export class TimePage {
  @bindable({ defaultBindingMode: bindingMode.twoWay }) public time: Date = new Date();
  @bindable() public startHour: number = 0;
  @bindable() public endHour: number = 23;
  @bindable() public hourInterval: number = 1;
  @bindable() public startMinute: number = 0;
  @bindable() public endMinute: number = 59;
  @bindable() public minuteInterval: number = 5;

  protected hours: Array<string> = [];
  protected currHour = 0;
  protected minutes: Array<string> = [];
  protected currMinute = 0;

  constructor(private element: Element, private taskQueue: TaskQueue) { }

  bind() {
    this.setHours();
    this.setMinutes();

    if (this.time != null) {
      this.setCurrHour();
      this.setCurrMinute();
    }
  }

  attached() {
    const hour = this.element.children[0].children[1];
    hour?.addEventListener('wheel', (ev) => this.wheelHour(ev));
    const minute = this.element.children[2].children[1];
    minute?.addEventListener('wheel', (ev) => this.wheelMinute(ev));

    UIInternal.subscribe("openTimePage", () => {
      this.taskQueue.queueTask(() => this.scrollMinuteAndHour());
    });
  }

  private timeIsChanging = false;
  timeChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal && !this.timeIsChanging) {
      this.timeIsChanging = true;
      if (this.setCurrHour()) {
        this.time.setHours(this.currHour);
        this.time = new Date(this.time);
      }

      if (this.setCurrMinute()) {
        this.time.setMinutes(this.currMinute);
        this.time = new Date(this.time);
      }

      this.scrollMinuteAndHour();
      this.taskQueue.queueTask(() => {
        this.timeIsChanging = false;
      });
    }
  }

  private setCurrHour() {
    this.currHour = this.time.getHours();
    if (!this.hours.some(x => x == this.currHour.toString())) {
      const closest = this.hours.reduce((a, b) => {
        return Math.abs(parseInt(b) - this.currHour) < Math.abs(parseInt(a) - this.currHour) ? b : a;
      });
      this.currHour = parseInt(closest);
      return true;
    }
    return false;
  }

  private setCurrMinute() {
    this.currMinute = this.time.getMinutes();
    if (!this.minutes.some(x => x == this.currMinute.toString())) {
      const closest = this.minutes.reduce((a, b) => {
        return Math.abs(parseInt(b) - this.currMinute) < Math.abs(parseInt(a) - this.currMinute) ? b : a;
      });
      this.currMinute = parseInt(closest);
      return true;
    }
    return false;
  }

  startHourChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal) {
      this.setHours();
    }
  }
  endHourChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal) {
      this.setHours();
    }
  }
  hourIntervalChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal) {
      this.setHours();
    }
  }
  startMinuteChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal) {
      this.setMinutes();
    }
  }
  endMinuteChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal) {
      this.setMinutes();
    }
  }
  minuteIntervalChanged(newVal, oldVal) {
    if (newVal != null && newVal != oldVal) {
      this.setMinutes();
    }
  }

  setHours() {
    this.hours.splice(0);
    for (let i = this.startHour; i <= this.endHour; i = i + this.hourInterval) {
      this.hours.push(i.toLocaleString('fr-BE', { minimumIntegerDigits: 2, useGrouping: false }));
    }
  }

  setMinutes() {
    this.minutes.splice(0);
    for (let i = this.startMinute; i <= this.endMinute; i = i + this.minuteInterval) {
      this.minutes.push(i.toLocaleString('fr-BE', { minimumIntegerDigits: 2, useGrouping: false }));
    }
  }

  wheelHour(event) {
    var delta = 0;
    if (event.wheelDelta) { (delta = event.wheelDelta / 120); }
    else if (event.detail) { (delta = -event.detail / 3); }

    var divHour = this.element.children[0].children[1];
    var iCurrent = Array.from(divHour.children).findIndex(x => x.classList.contains("currentHour"));
    var toShow;
    if (delta < 0) {
      toShow = divHour.children.item(iCurrent + 1);
    }
    else {
      toShow = divHour.children.item(iCurrent - 1);
    }

    this.wheel(toShow, true);
  }

  wheelMinute(event) {
    var delta = 0;
    if (event.wheelDelta) { (delta = event.wheelDelta / 120); }
    else if (event.detail) { (delta = -event.detail / 3); }

    var divMinute = this.element.children[2].children[1];
    var iCurrentMinute = Array.from(divMinute.children).findIndex(x => x.classList.contains("currentMinute"));
    var toShow;
    if (delta < 0) {
      toShow = divMinute.children.item(iCurrentMinute + 1);
    }
    else {
      toShow = divMinute.children.item(iCurrentMinute - 1);
    }

    this.wheel(toShow, false);
  }

  wheel(toShow: Element, isHours: boolean) {
    if (toShow == null) return;

    const toShowInt = parseInt(toShow.innerHTML);
    if (!isNaN(toShowInt)) {
      let newTime = null;
      if (this.time == null) {
        newTime = new Date();
      }
      else {
        newTime = new Date(this.time);
      }

      if (isHours) {
        this.currHour = toShowInt;
        newTime.setHours(toShowInt);
      } else {
        this.currMinute = toShowInt;
        newTime.setMinutes(toShowInt);
      }
      this.time = newTime;
    }
  }

  private selectHour(hour: number) {
    var divHour = this.element.children[0].children[1];
    var iCurrent = Array.from(divHour.children).findIndex(x => x.classList.contains("currentHour"));
    var toShow;
    if (hour < 0) {
      toShow = divHour.children.item(iCurrent - 1);
    }
    else {
      toShow = divHour.children.item(iCurrent + 1);
    }

    this.wheel(toShow, true);
  }

  private selectMinute(minute: number) {
    var divMinute = this.element.children[2].children[1];
    var iCurrentMinute = Array.from(divMinute.children).findIndex(x => x.classList.contains("currentMinute"));
    var toShow;
    if (minute < 0) {
      toShow = divMinute.children.item(iCurrentMinute - 1);
    }
    else {
      toShow = divMinute.children.item(iCurrentMinute + 1);
    }

    this.wheel(toShow, false);
  }

  private scrollMinuteAndHour() {
    const divHour = this.element.children[0].children[1];
    const iCurrentHour = Array.from(divHour.children).findIndex(x => x.classList.contains("currentHour"));
    if (iCurrentHour != -1) {
      let topHour = divHour.children.item(iCurrentHour).offsetTop - divHour.children.item(iCurrentHour).getBoundingClientRect().height -
        (divHour.getBoundingClientRect().height - divHour.children.item(iCurrentHour).getBoundingClientRect().height) / 2;
      divHour.scrollTo({ behavior: 'smooth', top: topHour });
    }

    const divMinute = this.element.children[2].children[1];
    const iCurrentMinute = Array.from(divMinute.children).findIndex(x => x.classList.contains("currentMinute"));
    if (iCurrentMinute != -1) {
      let topMinute = divMinute.children.item(iCurrentMinute).offsetTop - divMinute.children.item(iCurrentMinute).getBoundingClientRect().height -
        (divMinute.getBoundingClientRect().height - divMinute.children.item(iCurrentMinute).getBoundingClientRect().height) / 2;
      divMinute.scrollTo({ behavior: 'smooth', top: topMinute });
    }
  }

  @computedFrom("currHour", "startHour")
  private get currHourPrevDisabled(): boolean {
    return this.currHour == this.startHour;
  }

  @computedFrom("currHour", "hours.length")
  private get currHourNextDisabled(): boolean {
    return this.currHour == parseInt(this.hours[this.hours.length - 1]);
  }

  @computedFrom("currMinute", "startMinute")
  private get currMinutePrevDisabled(): boolean {
    return this.currMinute == this.startMinute;
  }

  @computedFrom("currMinute", "minutes.length")
  private get currMinuteNextDisabled(): boolean {
    return this.currMinute == parseInt(this.minutes[this.minutes.length - 1]);
  }
}
