import {
  OnDestroy,
  OnInit,
  OnChanges,
  EventEmitter,
  ElementRef,
  Input,
  Output,
  NgModule,
  SimpleChanges,
  Directive
} from '@angular/core';

import { Chart } from 'chart.js';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

var dataSetConstruct:boolean = false;

/* tslint:disable-next-line */
@Directive({selector: 'canvas[bbChart]', exportAs: 'bb-chart'})
export class BBChartDirective implements OnDestroy, OnChanges, OnInit {
  public static defaultColors:Array<number[]> = [
    [255, 0, 0],
    [0, 75, 0],
    [33, 109, 255],  //non-receipt
    [255, 8, 98],
    [167, 148, 224],
    [167, 0, 224],
    [72, 0, 224],
    [255, 0, 224],
    [38, 255, 224],
    [38, 0, 55],
    [148, 159, 177],
    [255, 255, 148]
  ];
  

  // @Input() public data:number[] | any[];
  // @Input() public datasets:any[];
  @Input() public data:number[] | any;
  @Input() public datasets:any;
  @Input() public labels:Array<any> = [];
  @Input() public options:any = {};
  @Input() public chartType:string | any;
  @Input() public colors:Array<any> | any;
  @Input() public legend:boolean | any;
  
  //input for pie chart (added)
  @Input() customLabels:boolean | any;
  @Input() customLegend:boolean | any;
  @Input() public plugins:any = {};
  @Input() displayText:string | any;
  @Input() width:any;
  @Input() height:any;
  @Input() chartBrdrColor:any;
  @Input() chartBrdrWidth:any;
  @Input() cutOutPercentage:any;
  @Input() animateScale:boolean | any
  @Input() animateRotate:boolean | any
  @Input() displayTopBar:boolean | any;
  //property TODO
  @Input() rotation: any
  @Input() circumference: any;
  @Input() displayTextValue: any;
  @Input() opacity: any;
    
  @Output() public chartClick:EventEmitter<any> = new EventEmitter();
  @Output() public chartHover:EventEmitter<any> = new EventEmitter();
  @Output() public chartLoaded:EventEmitter<any> = new EventEmitter();

  public ctx:any;
  public chart:any;
  private cvs:any;
  private initFlag:boolean = false;
  private element:ElementRef;

  public constructor(element:ElementRef , private _sanitizer:DomSanitizer) {
    this.element = element;
  }

  public ngOnInit():any {
      this.opacity=parseFloat(this.opacity);
    console.log("opacityFinal",this.opacity," data :: = >" , this.data , "labels :: == > " , this.labels , 
            " chartType :: ==   > " , this.chartType ,"datasets ::==> " , this.datasets ,
            "customLabels == > "  , this.customLabels  ,"customLegends ==> " , this.customLegend , "clors " , this.chartBrdrColor , this.chartBrdrWidth , "displayTextValue ==>" , this.displayTextValue, "opasity ==>",parseFloat(this.opacity)) ;
    this.ctx = this.element.nativeElement.getContext('2d');
    this.cvs = this.element.nativeElement;
    if(this.chartType =='horizontalBar'){
    //To set Canvas Height and Width dynamically
   
        this.cvs.height = this.height;
        this.cvs.width = this.width;
        
    }
    
    this.initFlag = true;
    if (this.data || this.datasets) {
      this.refresh();
    }
  }

  public ngOnChanges(changes: SimpleChanges): void {
    if (this.initFlag) {
      // Check if the changes are in the data or datasets
        if (changes.hasOwnProperty('data') || changes.hasOwnProperty('datasets')) {
        if (changes['data']) {
          this.updateChartData(changes['data'].currentValue);
        } else {
          this.updateChartData(changes['datasets'].currentValue);
        }
        this.chart.update();
      } else {
      // otherwise rebuild the chart
        this.refresh();
      }
    }
  }

  public ngOnDestroy():any {
    if (this.chart) {
      this.chart.destroy();
      this.chart = void 0;
    }
  }

  public getChartBuilder(ctx:any/*, data:Array<any>, options:any*/):any {
    let datasets:any = this.getDatasets();    
    console.log("dataset:: = >", datasets);
    let options:any = Object.assign({}, this.options);
    if (this.legend === false) {
      options.legend = {display: false};
    }
    //for cutOut %  adde by chitranga
    options.cutoutPercentage = this.cutOutPercentage,
    options.animation =  { animateScale: this.animateScale , animateRotate : this.animateRotate },
    
    // hock for onHover and onClick events
    options.hover = options.hover || {};
    if (!options.hover.onHover) {
      options.hover.onHover = (active:Array<any>) => {
        if (active && !active.length) {
          return;
        }
        this.chartHover.emit({active});
      };
    }

    if (!options.onClick) {
      options.onClick = (event:any, active:Array<any>) => {
        this.chartClick.emit({event, active});
      };
    }
    //added for customlegend and customlabel
    if(this.customLabels && this.customLegend){
        
        options.legendCallback = this.getLegands,
        options.tooltips = {
            callbacks: {
               // label: this.getValue
            }
        }
    }

    let plugins:any = Object.assign([], this.plugins);
        if(this.drawTextOrders){
            if (this.displayText && ( this.chartType == 'pie'  || this.chartType == 'doughnut')) {
                plugins= [{
                    afterDraw: this.drawTextOrders.bind(this)
                }]
            }
            console.log("displayText ==> " , this.displayText , "plugins ==>", plugins);
        }
       /* if(this.drawTextCalls)
        {
            if (this.displayText && ( this.chartType == 'pie'  || this.chartType == 'doughnut')) {
                plugins= [{
                    afterDraw: this.drawTextCalls.bind(this)
                }]
            }
        }*/
        
    if (this.displayTopBar && ( this.chartType == 'bar'  || this.chartType == 'horizontalBar')) {  // at center of pie or doughnut chart
            console.log(" ===   chart type is ");
            plugins= [{
                afterDatasetsDraw: this.drawTextAfterDatasets.bind(this)
            }]
        }
    //console.log("displayText ==> " , this.displayText , "plugins ==>", plugins , "displayTopBar  == " ,this.displayTopBar);
    
    let opts = {
      type: this.chartType,
      data: {
        labels: this.labels,
        datasets: datasets
      },
      options: options,
      plugins: plugins
    };
    console.log("opts ::==>>>" , opts);
    return new Chart(ctx, opts);
  }

  private updateChartData(newDataValues: number[] | any[]): void {
    if (Array.isArray(newDataValues[0].data)) {
      this.chart.data.datasets.forEach((dataset: any, i: number) => {
        dataset.data = newDataValues[i].data;

        if (newDataValues[i].label) {
          dataset.label = newDataValues[i].label;
        }
      });
    } else {
      this.chart.data.datasets[0].data = newDataValues;
    }
  }
  
  private getDatasets():any {
    let datasetExist = true ;
    let datasets:any = void 0;
    console.log("this.datasets ::" ,this.datasets);
    // in case if datasets is not provided, but data is present
    if (!this.datasets || !this.datasets.length && (this.data && this.data.length)) {
      if (Array.isArray(this.data[0])) {
        datasets = (this.data as Array<number[]>).map((data:number[], index:number) => {
          return {data, label: this.labels[index] || `Label ${index}` };
        });
      } else {
        datasets = [{data: this.data, label: this.labels}];
        dataSetConstruct = true;
      }
    }
    
    if ((this.datasets && this.datasets.length) ||
            (datasets && datasets.length)) {
             datasets = (this.datasets || datasets)
              .map((elm:number, index:number) => {
                let newElm:any = Object.assign({}, elm);
              
                if(this.datasets){
                    if(this.datasets[index].backgroundColor){
                        //backgroundColor
                        Object.assign(newElm, this.datasets[index].backgroundColor[index]);
                    }
                    else
                    {
                        //default
                        Object.assign(newElm, getColors(this.chartType, index, newElm.data.length,this.opacity));
                    }
                }
                else
                { 
                    if (this.colors && this.colors.length ) {
                      //Object.assign(newElm, this.colors[index]);
                      console.log('Custom Colors Exist',this.colors);
                      Object.assign(newElm, formatPieColors(this.colors,this.opacity ));
                      
                    } else {
                      Object.assign(newElm, getColors(this.chartType, index, newElm.data.length,this.opacity));
                    }
                }
                return newElm;
              });
          } 
          
          if (!datasets) {
            throw new Error(`ng-charts configuration error,
            data or datasets field are required to render char ${this.chartType}`);
          }
          console.log("dataset which is going to be return " , datasets);
          
          return datasets ;
  }

  private refresh():any {
    // if (this.options && this.options.responsive) {
    //   setTimeout(() => this.refresh(), 50);
    // }

    // todo: remove this line, it is producing flickering
    this.ngOnDestroy();
    this.chart = this.getChartBuilder(this.ctx/*, data, this.options*/);
    this.chart.canvas.parentNode.style.height = this.height + "px";
    this.chart.canvas.parentNode.style.width = this.width + "px";
  //this.chart.canvas.parentNode.style.width = this.elWidth - 40 + "px";

    console.log("height and width set =====  >>for chart" , this.chart.canvas.parentNode ,this.chart.canvas , this.height , this.width);
    
    //this.drawText(this.chart);
    if(this.customLabels && this.customLegend)
    {
        this.chartLoaded.emit(this.chart);
    }
    console.log(this.chart, "data :: = >" , this.data , "labels :: == > " , this.labels , " chartType :: ==   > " , this.chartType ,"datasets ::==> " , this.datasets);
  }
  
  getChart()
  {
      return this.chart;
  }
  
 /* getValue(tooltipItem, data){
      var dataset = data.datasets[tooltipItem.datasetIndex];
      var index = tooltipItem.index;
      return dataset.labels[index] + ': ' + dataset.data[index];
  } */

  getLegands(chart: any) 
  {
      var text: any = [];
      var helper: any  = {};
      helper['onClick'] = function(dsIdx: any, dataIndex: any) {
            var meta = chart.getDatasetMeta(dsIdx);
              if( chart.config.type == "pie" ||  chart.config.type == "doughnut") 
              {     
                  meta.data[dataIndex].hidden = meta.data[dataIndex].hidden === false ? true : false;
              }
              if( chart.config.type == "bar" ||  chart.config.type == "horizontalBar")
              {
                  meta.hidden = meta.hidden === null? !chart.data.datasets[dataIndex].hidden : null;
              }
              chart.update();
      } ;
      (window as any)['customLegendHelper'] = helper;
     if( chart.config.type == "pie" ||  chart.config.type == "doughnut") 
     {         
              var dsi = 0;
              for(var dataSet of chart.data.datasets){
                  var customLegands: any = [];
                  var i = 0;
                  for(var data of dataSet.data ){
                         var customLegand = { label: dataSet.label[i] , color: dataSet.backgroundColor[i]};
                          if(!containsObject (customLegand,customLegands) )
                          {
                              customLegands.push(customLegand);
                          }
                          i++;
                  }
                  
                  text.push(`<ul class="custom-legends  ${chart.id}-legend">`);
                  for( var k =0; k < customLegands.length; k++)
                  {
                      if(customLegands.length==1){
                          text.push(`<li style = "padding-left: 137px; ">
                                  <span  class="legend" 
                                          onclick="customLegendHelper.onClick(${dsi}, ${k});" 
                                          style="background-color: ${ customLegands[k].color}">
                                  </span>
                                  <span style = "padding-left: 12px; ">${customLegands[k].label}</span>
                                  </li>`);                         
                      }
                      else{
                          text.push(`<li style = "">
                                  <span  class="legend" 
                                          onclick="customLegendHelper.onClick(${dsi}, ${k});" 
                                          style="background-color: ${ customLegands[k].color}">
                                  </span>
                                  <span style = "padding-left: 12px; ">${customLegands[k].label}</span>
                                  </li>`);  
                      }  
                  }    
                  text.push(`</ul>`);
                  dsi++;
              }
              return text.join(""); 
         }
        if( chart.config.type == "bar" ||  chart.config.type == "horizontalBar") {
            var t = 0;
            for(var dataSet of chart.data.datasets)
            {
                var customLegands: any = [];
                var customLegand = { label: dataSet.label , color: dataSet.backgroundColor};
            
                if(!containsObject (customLegand,customLegands) )
                {
                    customLegands.push(customLegand);
                }   
                 text.push(``);
                for( var k =0; k < customLegands.length; k++)
                {
                    text.push(`  
                    <span  class="legend-color" 
                            onclick="customLegendHelper.onClick(${t}, ${k});" 
                            style="background-color: ${ customLegands[k].color}">
                    </span>
                    <span class = "legend-text">${customLegands[k].label}</span>
                  `);
                }    
                text.push(``);
                t++;
            }
            return text.join(""); 
         }
    }
  
  drawTextOrders(chart: any) {
      
      var width = chart.width,
          height = chart.height,
          ctx = chart.ctx;
          ctx.restore();
          ctx.font = "60px sans-serif";
         //ctx.textBaseline = "middle";
          ctx.fillStyle = 'black';
          ctx.textAlign = 'center';
        
          var text = (this.displayText || '0'),
            //  text2 = 'Orders',
              text2 =this.displayTextValue,
             //textX = Math.round((width - ctx.measureText(text).width) / 2),
          textX = width /2, 
          textY = height /2;
        
          ctx.fillText(text, textX, textY);
          ctx.fillStyle = 'gray';
          ctx.textAlign = 'center';
          ctx.font = "20px sans-serif";
          ctx.fillText(text2, textX, textY+20);
          
          ctx.save();
    }
  
 /* drawTextCalls(chart) {
      
      var width = chart.width,
          height = chart.height,
          ctx = chart.ctx;
          ctx.restore();
          ctx.font = "60px sans-serif";
         //ctx.textBaseline = "middle";
          ctx.fillStyle = 'black';
          ctx.textAlign = 'center';
        
          var text = (this.displayText || '0'),
              text2 = 'Calls',
             //textX = Math.round((width - ctx.measureText(text).width) / 2),
          textX = width /2, 
          textY = height /2;
        
          ctx.fillText(text, textX, textY);
          ctx.fillStyle = 'gray';
          ctx.textAlign = 'center';
          ctx.font = "20px sans-serif";
          ctx.fillText(text2, textX, textY+20);
          
          ctx.save();
    } */
  
  drawTextAfterDatasets(chart: any, easing: any) {
      // To only draw at the end of animation, check for easing === 1
      var ctx: any = chart.ctx;

      chart.data.datasets.forEach(function (dataset: any, i: any) {
          var meta = chart.getDatasetMeta(i);
          if (!meta.hidden) {
              meta.data.forEach(function(element: any, index: any) {
                  // Draw the text in black, with the specified font
                  ctx.fillStyle = 'rgb(0, 0, 0)';

                  var fontSize = 16;
                  var fontStyle = 'normal';
                  var fontFamily = 'Helvetica Neue';
                  // ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
                  ctx.font = (Chart as any).helpers.fontString(fontSize, fontStyle, fontFamily);
                  // this.newMethod(ctx, fontSize, fontStyle, fontFamily);

                  // Just naively convert to string for now
                  var dataString = dataset.data[index].toString();

                  // Make sure alignment settings are correct
                  ctx.textAlign = 'center';
                  ctx.textBaseline = 'top';

                  var padding =15;
                  var position = element.tooltipPosition();
                  ctx.fillText(dataString, position.x  + (fontSize / 2) + padding, position.y - (fontSize / 2) );
              });
          }
      });
  }
 /* newMethod(ctx: any, fontSize: any, fontStyle: string, fontFamily: string) 
  {
      throw new Error('Method not implemented.');
  }*/
}


// private helper functions
export interface Color {
  backgroundColor?:string | string[];
  borderWidth?:number | number[];
  borderColor?:string | string[];
  borderCapStyle?:string;
  borderDash?:number[];
  borderDashOffset?:number;
  borderJoinStyle?:string;

  pointBorderColor?:string | string[];
  pointBackgroundColor?:string | string[];
  pointBorderWidth?:number | number[];

  pointRadius?:number | number[];
  pointHoverRadius?:number | number[];
  pointHitRadius?:number | number[];

  pointHoverBackgroundColor?:string | string[];
  pointHoverBorderColor?:string | string[];
  pointHoverBorderWidth?:number | number[];
  pointStyle?:string | string[];

  hoverBackgroundColor?:string | string[];
  hoverBorderColor?:string | string[];
  hoverBorderWidth?:number;
}

// pie | doughnut
export interface Colors extends Color {
  data?:number[];
  label?:string;
}

function rgba(colour:Array<number>, alpha:number):string {
  return 'rgba(' + colour.concat(alpha).join(',') + ')';
}

function getRandomInt(min:number, max:number):number {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function formatLineColor(colors:Array<number>):Color {
  return {
    backgroundColor: rgba(colors, 0.4),
    borderColor: rgba(colors, 1),
    pointBackgroundColor: rgba(colors, 1),
    pointBorderColor: '#fff',
    pointHoverBackgroundColor: '#fff',
    pointHoverBorderColor: rgba(colors, 0.8)
  };
}

function formatBarColor(colors:Array<number>,opacityFinal: any):Color {
    console.log('formatBarColor opacity',opacityFinal)
  return {
    backgroundColor: rgba(colors,opacityFinal),
    borderColor: rgba(colors, 1),
    hoverBackgroundColor: rgba(colors, 0.8),
    hoverBorderColor: rgba(colors, 1)
  };
}

function formatPieColors(colors:Array<number[]>,opacityFinal: any):Colors {
   console.log("formatPieColors ::" , colors,opacityFinal);
  return {
    backgroundColor: colors.map((color:number[]) => rgba(color, opacityFinal)),
    borderColor: colors.map(() => '#fff'),
    pointBackgroundColor: colors.map((color:number[]) => rgba(color, 1)),
    pointBorderColor: colors.map(() => '#fff'),
    pointHoverBackgroundColor: colors.map((color:number[]) => rgba(color, 1)),
    pointHoverBorderColor: colors.map((color:number[]) => rgba(color, 1))
  };
}

function formatPolarAreaColors(colors:Array<number[]>,opacityFinal: any):Color {
    console.log("formatPolarAreaColors ::" , opacityFinal);
  return {
    backgroundColor: colors.map((color:number[]) => rgba(color, opacityFinal)),
    borderColor: colors.map((color:number[]) => rgba(color, 1)),
    hoverBackgroundColor: colors.map((color:number[]) => rgba(color, 0.8)),
    hoverBorderColor: colors.map((color:number[]) => rgba(color, 1))
  };
}

function getRandomColor():number[] {
  return [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];
}

/**
 * Generate colors for line|bar charts
 * @param index
 * @returns {number[]|Color}
 */
function generateColor(index:number):number[] {
  return BBChartDirective.defaultColors[index] || getRandomColor();
}

/**
 * Generate colors for pie|doughnut charts
 * @param count
 * @returns {Colors}
 */
function generateColors(count:number):Array<number[]> {
  let colorsArr:Array<number[]> = new Array(count);
  for (let i = 0; i < count; i++) {
    colorsArr[i] = BBChartDirective.defaultColors[i] || getRandomColor();
  }
  return colorsArr;
}

/**
 * Generate colors by chart type
 * @param chartType
 * @param index
 * @param count
 * @returns {Color} 
 */
function getColors(this: any, chartType:string, index:number, count:number,opacity:number):any {
   console.log("chartType "  , chartType );
  if (chartType === 'pie' || chartType === 'doughnut') {
    return formatPieColors(generateColors(count),opacity);
  }

  if (chartType === 'polarArea') {
    return formatPolarAreaColors(generateColors(count),this.opacity);
  }

  if (chartType === 'line' || chartType === 'radar') {
    return formatLineColor(generateColor(index));
  }

  if (chartType === 'bar' || chartType === 'horizontalBar') {
    return formatBarColor(generateColor(index),this.opacity);
  }
  return generateColor(index);
}

function containsObject(obj: any, list: any) {
    var i;
    if(list){
        for (i = 0; i < list.length; i++) {
        if (list[i].label == obj.label ) {
                return true;
            }
        }
        return false;
    }
}

@NgModule({
  declarations: [
    BBChartDirective
  ],
  exports: [
    BBChartDirective
  ],
  imports: []
})
export class BBChartsModule {
}