import { AfterViewChecked, AfterViewInit, Component, Directive, ElementRef, EventEmitter, Inject, Input, OnInit, Output, ViewChild, OnChanges, SimpleChanges, OnDestroy, Optional, Host, SkipSelf, HostBinding, ViewContainerRef } from '@angular/core';

import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/empty';
import 'rxjs/add/observable/interval';
import { of } from 'rxjs/observable/of';
import { take, delay } from 'rxjs/operators';

import { MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatExpansionPanel, MatGridTile } from '@angular/material';

import { AbstractControl } from '@angular/forms';

import { AccountService, IKnokOngoingData, IOngoingKnok, KnokService } from '../../shared';
import { getKnokserRank, formatTimeLeft } from '../../shared/helpers/converters';

import { Chart } from 'chart.js';
import { KnokLinearScale, KnokLinearPlugin } from '../../shared/libs/chart_js';
import 'chartjs-plugin-annotation';

import { ActivatedRoute, Router, UrlTree } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Subject } from 'rxjs/Subject';

import { AppComponent } from '../../../app/app.component';
import { applyMixins, Log } from '../../shared/helpers';

import { trigger, style, transition, animate, keyframes, query, stagger, state } from '@angular/animations';
import { AnimationTriggerMetadata } from '@angular/animations/src/animation_metadata';
import 'web-animations-js';
import { IKnoker, IKnokser } from '../../shared/knok';
import { KnkFeedbackKnokComponent } from '../knk-feedback-knok';
import { has as _has } from 'lodash/object';

function fade(): AnimationTriggerMetadata {
  return trigger('fade', [
    state('false', style({opacity: 0.0})),
    state('true', style({opacity: 1.0})),
    transition('void => true, * => true, false => true', [
      style({opacity: 0.0}),
      animate('250ms ease-in-out', style({opacity: 1.0}))
    ]),
    transition('void => false, * => false, true => false, * => void', [
      style({opacity: 1.0}),
      animate('250ms ease-in-out', style({opacity: 0.0}))
    ])
  ]);
}

@Component({
  selector: 'knk-ongoing-item',
  styleUrls: ['./ongoingitem.component.scss'],
  templateUrl: './ongoingitem.component.pug',
  animations: [fade()]
})
export class OngoingItemComponent implements OnInit, OnDestroy {
  lang: string;

  @HostBinding('class')
  get componentCssClass(): string {
    return this.parent.componentCssClass;
  }

  @Input('isbutton') isbutton: boolean = false;
  @Input('isUser') isUser: boolean = false;
  @Input('item') item: IKnokOngoingData;
  @Input() userIsKnokser: boolean = false;
  @Output() changedItem: EventEmitter<void> = new EventEmitter<void>();

  timeLeft: any;
  // timeLeftOnFeed: any;
  observer: MutationObserver;
  showProgress: boolean = true;

  maxVal: number = 24;
  minVal: number = 0;

  startHeight: number;
  isCollapsed: boolean = true;
  expandChart: boolean = false;

  public _selectedItems: Array<{ label: string, value: string }> = [];
  public watchedItems: Array<{ label: string, value: string }>;

  get feedbackData(): any {
    let knokId: number = -1;
    let markFeedback: Function = () => undefined;
    if (_has(this.item, ['knok']) && _has(this.item.knok, ['knokId'])) {
      knokId = +this.item.knok.knokId;
    }
    if (_has(this.item, ['knok', 'feedbackExists'])) {
      markFeedback = () => this.item.knok.feedbackExists = true;
      markFeedback = markFeedback.bind(this);
    }
    return {knokId, markFeedback};
  }

  /* Mixins */
  public log: (...args: any[]) => void;

  private _items: Array<{ label: string, value: string }>;
  private ngUnsubscribe: Subject<void> = new Subject<void>();

  constructor(public dialog: MatDialog,
              @Optional() @Inject(AppComponent)
              private parent: AppComponent,
              private viewContainerRef: ViewContainerRef,
              private router: Router,
              private route: ActivatedRoute,
              private translateService: TranslateService) {
  }

  ngOnInit(): void {
    this.lang = this.translateService.currentLang || this.translateService.defaultLang;
    Observable.timer(10, 30000).takeUntil(this.ngUnsubscribe).subscribe((): void => {
      this.timeLeft = this.getTimeLeft();
      // this.timeLeftOnFeed = this.getTimeLeftOnFeed();
    });
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  // calcTimeLeft(): any {
  //   const dayTime: number = (24 * 60 * 60 * 1000);
  //   const createTime: number = this.item.knok.createDate.getTime();
  //   const lifeTime: number = createTime + (dayTime * this.item.knok.duration);

  //   let d1: any = new Date(lifeTime);
  //   let d2: any = new Date();
  //   let diff: number = d2 - d1,
  //     sign: number = diff < 0 ? -1 : 1,
  //     milliseconds: number,
  //     seconds: number,
  //     minutes: number,
  //     hours: number,
  //     days: number;

  //   diff /= sign; // or diff = Math.abs(diff);
  //   diff = (diff - (milliseconds = diff % 1000)) / 1000;
  //   diff = (diff - (seconds = diff % 60)) / 60;
  //   diff = (diff - (minutes = diff % 60)) / 60;
  //   days = (diff - (hours = diff % 24)) / 24;

  //   // console.warn(`sign: ${sign},\ndays: ${days},\nhours: ${hours},\nminutes: ${minutes}`);
  //   return {
  //     sign,
  //     milliseconds,
  //     seconds,
  //     minutes,
  //     hours,
  //     days
  //   };
  // }

  getTimeLeft(): any {
    if (this.userIsKnokser) {
      const minuteTime: number = (60 * 1000);
      const currDate: Date = new Date();
      return formatTimeLeft(new Date(currDate.getTime() + (this.item.knok.minutesLeftOnFeed * minuteTime)), false);
    } else {
      const dayTime: number = (24 * 60 * 60 * 1000);
      return formatTimeLeft(new Date(this.item.knok.createDate.getTime() + (this.item.knok.duration * dayTime)), false);
    }
    
    // let { sign, milliseconds, seconds, minutes, hours, days } = this.calcTimeLeft();
    // let value: number = 0,
    //   label: string = 'minutes';
    // if (sign === 1) {
    //   return {
    //     value,
    //     label
    //   };
    // } else {
    //   switch (true) {
    //     case (days > 0):
    //       value = days;
    //       label = 'days';
    //       break;
    //     case (hours > 0):
    //       value = hours;
    //       label = 'hours';
    //       break;
    //     case (minutes > 0):
    //       value = minutes;
    //       label = 'minutes';
    //       break;
    //     case (milliseconds > 0):
    //       value = 1;
    //       label = 'minutes';
    //       break;
    //     default:
    //   }
    //   return {
    //     value,
    //     label
    //   };
    // }
  }

  goToKnok(): void {
    const knokId: number = this.item.knok.knokId;
    let url: UrlTree = this.router.createUrlTree(['.', {outlets: {'knok': [knokId]}}], {relativeTo: this.route.parent});
    this.router.navigateByUrl(url);
  }

  giveFeedback(): void {
    let dialogRef: MatDialogRef<KnkFeedbackKnokComponent, any> = this.dialog.open(KnkFeedbackKnokComponent, {
        viewContainerRef: this.viewContainerRef,
        panelClass: 'knk-overlay-pane',
        backdropClass: 'knk-overlay-backdrop',
        width: '860px',
        data: this.feedbackData
      });
  }

  get feedbackExists(): boolean {
    if (_has(this.item, ['knok', 'feedbackExists'])) {
      return Boolean(this.item.knok.feedbackExists);
    }
    return false;
  }

  get avatarHref(): string {
    return `${SETTINGS.API_URL}` + this.item.knokser.href;
  }

  get userFirstName(): string {
    let knokser: IKnokser = this.item.knokser;
    let userName: string = knokser.userName;
    let userNameAsArray: string[] = userName.split(/\s+/);
    return (userNameAsArray.length > 0) ? userNameAsArray[0] : knokser.firstName;
  }

  get userLastName(): string {
    let knokser: IKnokser = this.item.knokser;
    let userName: string = knokser.userName;
    let userNameAsArray: string[] = userName.split(/\s+/);
    return (userNameAsArray.length > 1) ? userNameAsArray[1] : knokser.lastName;
  }

  get percentShiftCaption(): number {
    return 50 + 290; // * (this.daysLeft / this.item.knok.duration);
  }

  get rankValue(): string {
    return getKnokserRank(this.item.knokser.rank);
  }

  get knokPriceFormat(): string {
    let knok: IOngoingKnok | undefined = this.item.knok;
    let precision: number | undefined = (typeof knok === 'object') ? +knok.precision : undefined;
    if (typeof precision === 'number' && !Number.isNaN(precision)) {
      return `1.${precision}-${precision}`; // (precision === 8 ? '1' : '') + '.' + precision.toString();
    } else {
      return '1.8-8';
    }
  }

  private addPadding(val: number): string {
    let res: string = val.toString();
    while (res.length < 2) {
      res = '0' + res;
    }
    return res;
  }

  private floorFigure(figure: number, decimals: number): number {
    if (!decimals) {
      decimals = 0;
    }
    let d: number = Math.pow(10, decimals);
    d = (parseInt((figure * d).toString(), null) / d);
    return parseInt(d.toFixed(decimals), null);
  }

  private truncateToInt(n: number): number {
    return parseInt(n.toString().match(/^-?\d+(?:\.\d{0})?/)[0], 10);
  }
}

Chart.scaleService.registerScaleType('knok-linear', KnokLinearScale, Chart.scaleService.getScaleDefaults('linear'));
Chart.plugins.register(KnokLinearPlugin);

@Component({
  selector: 'knk-ongoing-item-chart',
  template: '<canvas #canvas></canvas>',
  styles: [`
    :host {
      display: block;
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
  `]
})
export class OngoingItemChartComponent implements OnInit, OnChanges {
  @ViewChild('canvas') canvas;
  @Input('item') item: IKnokOngoingData;
  @Input('chartData') chartData: Array<{x: Date, y: number}>;
  chart: Chart;

  ngOnChanges(changes: SimpleChanges): void {
    if (this.chart && changes.chartData) {
      let update: boolean = false;
      let index: number = 0;
      changes.chartData.currentValue.forEach((e: {x: Date, y: number})  => {
        let el: Array<{x: Date, y: number}> = this.chart.data.datasets[0].data.filter(e1 => this.getDateOnly(e1.x).getTime() === this.getDateOnly(e.x).getTime());
        // if (el.length > 0) {
          // if (el[0].x.getTime() !== e.x.getTime() || el[0].y !== e.y) {
            // el[0].x = e.x;
            // el[0].y = e.y;
            // let a: {xMin: Date; xMax: Date; yMin: number; yMax: number} = this.chart.options.annotation.annotations[2];
            // a.xMin = e.x;
            // a.xMax = new Date(e.x.getTime() + (1000 * 60)),
            // a.yMin = e.y + ((this.item.knok.exitPriceTo - this.item.knok.stopLoss) / 4),
            // a.yMax =  e.y - ((this.item.knok.exitPriceTo - this.item.knok.stopLoss) / 4),
            // update = true;
          // }
        // } else {
        let chartData: Array<{x: Date, y: number}> = this.chart.data.datasets[0].data;
        if (chartData[chartData.length - 1].x.getTime() < e.x.getTime()) {
          this.chart.data.datasets[0].data.push(e);
          // this.chart.data.datasets[0].data.splice(index, 0, e);
          update = true;
        }
        index++;
      });
      if (update) {
        this.chart.update();
      }
    }
  }

  ngOnInit(): void {
    this.initChart();
  }

  initChart(): void {
    const oneDayTiks: number = (24 * 60 * 60 * 1000);
    let pointRadiusData: Array<number> = [];
    const endDate: Date = new Date(this.item.knok.createDate.getTime() + (this.item.knok.duration * oneDayTiks));

    for (let i: number = 0; i < this.item.chartData.length; i++) {
      if ( i === this.item.chartData.length - 1) {
        pointRadiusData.push(6);
      } else {
        pointRadiusData.push(0);
      }
    }

    if (this.item.chartData.length > 1 && !this.item.chartData[0].y) {
      this.item.chartData[0].y = this.item.chartData[1].y;
    }
    this.item.chartData.push({x: endDate, y: null});

    setTimeout(() => {
    const canvas: HTMLCanvasElement | undefined = (this.canvas && this.canvas.nativeElement) || undefined;
    if (canvas) {
      const ctx: CanvasRenderingContext2D = canvas.getContext('2d');
      this.chart = new Chart(ctx, {
        type: 'line',
        data: {
          datasets: [
            {
              showLine: true,
              fill: true,
              backgroundColor: this.getCanvasLinearGradient(ctx, [[0, 'rgba(0, 186, 255, 0.15)'], [1, 'rgba(255, 255, 255, 0.15)']], {y1: ctx.canvas.height}),
              borderColor: 'rgba(0, 186, 255, 1)',
              borderWidth: 2,
              pointBackgroundColor: 'rgba(0, 186, 255, 1)',
              pointBorderColor: 'rgba(255, 255, 255, 1)',
              pointBorderWidth: 1,
              pointRadius: pointRadiusData,
              data: this.item.chartData,
              yAxisID: 'A'
            }
          ]
        },
        options: {
          layout: {
            padding: {
                left: -90,
                right: 0,
                top: 10,
                bottom: 10
            }
          },
          listItemMode: true,
          responsive: true,
          maintainAspectRatio: false,
          easing: 'easeInOutBack',
          elements: {
            line: {
              tension: 0.5
            }
          },
          plugins: ['knok-linear'],
          scales: {
            xAxes: [
              {
                id: 'D',
                type: 'time',
                display: false,
                time: {
                  unit: 'minute',
                  displayFormats: {
                    day: 'MMM yyyy'
                  }
                },
                ticks: {
                  display: false,
                  beginAtZero: false,
                  min: this.item.knok.createDate,
                  max: endDate
                },
                gridLines: {
                  display: false,
                  drawBorder: false
                }
              }
            ],
            yAxes: [
              {
                id: 'A',
                type: 'knok-linear',
                position: 'left',
                display: true,
                listItemMode: true,
                ticks: {
                  display: true,
                  beginAtZero: false,
                  fontFamily: 'Roboto',
                  fontSize: 10,
                  exitPriceFrom: this.item.knok.exitPriceFrom,
                  exitPriceTo: this.item.knok.exitPriceTo,
                  entryPriceFrom: this.item.knok.entryPriceFrom,
                  entryPriceTo: this.item.knok.entryPriceTo,
                  stopLoss: this.item.knok.stopLoss
                },
                gridLines: {
                  display: true,
                  drawBorder: false,
                  zeroLineColor: 'transparent'
                }
              }
            ]
          },
          legend: {
            display: false
          },
          title: {
            display: false
          },
          tooltips: {
            enabled: false
          }
        }
      });
    }
  }, 10);
  }

  private getCanvasLinearGradient(ctx: CanvasRenderingContext2D, colors: [number, string][], options?: { [key: string]: any }): CanvasGradient | null {
    if (ctx && ctx.canvas) {
      const y0: number = options.y0 || 0;
      const y1: number = options.y1 || 100;
      const gradient: CanvasGradient = ctx.createLinearGradient(0, y0, 0, y1);
      colors.forEach((value: [number, string]): void => {
        const index: number = value[0];
        const color: string = value[1];
        gradient.addColorStop(index, color);
      });
      return gradient;
    } else {
      return null;
    }
  }

  private getDateOnly(d: Date): Date {
    if (d) {
      if (this.item.knok.duration > 1) {
        return new Date(d.getFullYear(), d.getMonth(), d.getDate());
      } else {
        // new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
        return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours());
      }
    }
    return d;
  }
}

applyMixins(OngoingItemComponent, [Log]);
