import Helpers, { IHelpers } from '../Helpers';
import { IPageContentCell, IPageContentLink } from '../Page/Content';
import Emitter, { IEmitter } from '../Emitter';
import Popper from 'popper.js';
import { IActionsButton } from '../Actions/Buttons';
import { IActionsStyleCondition, IActionsStyleConditionStyle } from '../Actions/Styles';
import { IGridPageContentCell } from './Grid';
import GridContent from './GridContent';

const helpers: IHelpers = new Helpers();

export interface IGridCellParamsMergedCells {
  cells: number[];
  last: number[];
}

export interface IGridCellParams {
  content: IGridPageContentCell;
  rowIndex: number;
  colIndex: number;
  row: any;
  mergeEl: HTMLDivElement;
  x: number;
  y: number;
  zIndexRow: number;
  zIndexCol: number;
  total: number;
  // hidden: boolean;
  mergedCells: IGridCellParamsMergedCells;
  // button: IActionsButton | null;
  // style: IActionsStyleConditionStyle | null;
}

export interface IGridCellDataset {
  x: number;
  y: number;
  width: number;
  height: number;
  overflow: number;
  row: number;
  col: number;
  [key: string]: any;
}

export interface IGridContentCellPosition {
  row: number;
  col: number;
  [key: string]: number;
}

export interface IGridCellDetails {
  content: IPageContentCell;
  row: number;
  col: number;
  position: IGridContentCellPosition;
  index: IGridContentCellPosition;
  dataset: IGridCellDataset;
  reference: string;
  button: IActionsButton | null;
  link: IPageContentLink | undefined;
  history: boolean;
  checked: boolean;
}

export interface IGridCell extends IEmitter {
  VALUE: string;
  DONE: string;
  CANCEL: string;
  STOP: string;
  CHECKED: string;
  init: boolean;
  type: string;
  row: number;
  col: number;
  button: IActionsButton | null;
  style: IActionsStyleConditionStyle | null;
  cell: HTMLDivElement;
  cellImage: HTMLImageElement;
  cellBorders: HTMLDivElement;
  cellPopper: HTMLDivElement;
  dataset: IGridCellDataset;
  freeze: string;
  permission: string;
  params: IGridCellParams;
  position: IGridContentCellPosition;
  index: IGridContentCellPosition;
  reference: string;
  hasHistory: boolean;
  isButton: boolean;
  hasButtonOptions: boolean;
  canEditButton: boolean;
  isImage: boolean;
  hidden: boolean;
  input: HTMLInputElement;
  checked: boolean;
  renderer: any;
  // oldValue: string;
  create(): void;
  update(content: IPageContentCell): void;
  edit(value?: string, touch?: boolean): void;
  cancel(clear?: boolean): void;
  setFreeze(which: string, cell?: IGridCell, cell2?: IGridCell): void;
  selected(n: boolean, sides?: string[]): void;
  highlight(): void;
  history(n: boolean, user?: number): void;
  updateSoftMerge(hiddenColumns?: number[]): void;
  onButton(): boolean;
  setSize(which: string, size: number): void;
  setButton(button: IActionsButton | null): void;
  setStyle(style: IActionsStyleConditionStyle | null): void;
  setPermission(permission: string): void;
  setOffset(size: number, which?: string): void;
  setXPosition(pos: number, move?: boolean): void;
  setYPosition(pos: number, move?: boolean): void;
  setSorting(direction: string): void;
  setRowIndex(row: number, y: number): void;
  toggleCheckbox(): void;

  getDetails(): IGridCellDetails;
  getFlattenStyles(only?: string[]): string;
  getValue(): any;
  getRawValue(): any;

  setFound(n: boolean): void;
  openOptions(): void;
  selectOption(direction: string): boolean;
}

export class GridCell extends Emitter implements IGridCell {
  public get VALUE(): string {
    return 'value';
  }
  public get DONE(): string {
    return 'done';
  }
  public get CANCEL(): string {
    return 'cancel';
  }
  public get STOP(): string {
    return 'stop';
  }
  public get CHECKED(): string {
    return 'checked';
  }

  private borders: any = {
    widths: {
      none: 0,
      thin: 1,
      medium: 2,
      thick: 3
    },
    styles: {
      none: 'none',
      solid: 'solid',
      double: 'double',
      dash: 'dashed'
    },
    names: {
      t: 'top',
      r: 'right',
      b: 'bottom',
      l: 'left'
    }
  };

  private inputStyles: string[] = ['color', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-align'];

  private textStyles: string[] = [
    // 'background-color',
    'color',
    'font-family',
    'font-size',
    'font-style',
    'font-weight',
    // 'height',
    // 'number-format',
    'text-align',
    'justify-content',
    'align-items',
    'text-wrap'
    // 'width',
    // 'vertical-align',
  ];

  private cellStyles: string[] = [
    'background-color',
    'color',
    'font-family',
    'font-size',
    'font-style',
    'font-weight',
    'height',
    'text-align',
    'text-wrap',
    'width',
    'vertical-align'
  ];

  public init: boolean = false;
  public type: string = 'cell';
  public cell: HTMLDivElement;
  public cellImage!: HTMLImageElement;
  // public cellOffset: HTMLDivElement;
  // public cellOffsetBackground: HTMLDivElement;
  public cellBackground!: HTMLDivElement;
  public cellBackgroundHighlight!: HTMLDivElement;
  private cellHistory!: HTMLDivElement | null;
  // public cellBackgroundBorder: HTMLDivElement;
  public cellBorders!: HTMLDivElement;
  public cellBordersOffset!: HTMLDivElement;
  public cellTextContainer!: HTMLDivElement;
  public cellText!: HTMLDivElement;
  public cellCheckbox!: HTMLDivElement;
  public cellPopper!: HTMLDivElement;
  // public cellStatus: HTMLDivElement;
  public dataset: IGridCellDataset;
  public freeze: string = '';
  public permission: string = 'rw';
  public reference: string = '';
  public position: IGridContentCellPosition = {
    row: -1,
    col: -1
  };
  public index: IGridContentCellPosition = {
    row: -1,
    col: -1
  };
  public hasHistory: boolean = false;
  public isButton: boolean = false;
  public hasButtonOptions: boolean = false;
  public canEditButton: boolean = false;
  public button: IActionsButton | null = null;
  public link: IPageContentLink | null = null;
  public style: IActionsStyleConditionStyle | null = null;
  public isImage: boolean = false;
  public hidden: boolean = false;
  public row: number = -1;
  public col: number = -1;
  public checked: boolean = false;

  private highlightTimer: any = null;
  public input!: HTMLInputElement;
  // private initialized: boolean = false;
  private oldValue: string = '';
  private borderStyles: any = {};
  private mergedCells: IGridCellParamsMergedCells = {
    cells: [],
    last: []
  };
  private buttonPopper: any;
  private selectedOption: number = 0;

  constructor(public params: IGridCellParams) {
    super();
    // this.button = params.button;
    this.type = 'cell';
    // this.hidden = params.hidden;
    // this.link = params.col.link || null;
    // cell data
    let dataset: IGridCellDataset = this.getDataset(params);
    this.dataset = dataset;

    // backwards compatibility
    this.position.row = dataset.row;
    this.position.col = dataset.col;
    this.index.row = dataset.row;
    this.index.col = dataset.col;
    this.row = params.content.index.row;
    this.col = params.content.index.col;

    this.cell = document.createElement('div');
    this.cellBordersOffset = document.createElement('div');
    this.cellTextContainer = document.createElement('div');

    // soft merges
    // this.updateSoftMerge();

    // cell reference
    this.reference = `${helpers.toColumnName(params.colIndex + 1)}${params.rowIndex + 1}`;
  }

  public create() {
    this.init = true;
    this.cell.className = 'grid-cell';
    if (this.freeze === 'common') {
      this.cell.style.setProperty('transform', `translate(${this.dataset.x}px, ${this.dataset.y}px)`);
    } else {
      this.cell.style.setProperty('transform', `translateX(${this.dataset.x}px)`);
    }
    this.cell.style.setProperty('z-index', `${this.params.zIndexCol}`);
    this.cell.style.setProperty('width', `${this.dataset.width}px`);
    this.cell.style.setProperty('height', `${this.dataset.height}px`);
    this.cell.dataset.row = this.params.rowIndex.toString();
    this.cell.dataset.col = this.params.colIndex.toString();
    this.cell.dataset.r = this.params.rowIndex.toString();
    this.cell.dataset.c = this.params.colIndex.toString();
    // cell.dataset.width = dataset.width.toString();
    // cell.dataset.height = dataset.height.toString();
    // cell.dataset.overflow = dataset.width.toString();
    // cell.dataset.x = this.params.x.toString();
    // cell.dataset.y = this.params.y.toString();
    // this.cell = cell;

    // background
    // this.cellImage = document.createElement('img');
    this.cellBackground = document.createElement('div');
    this.cellBackground.className = 'grid-background';
    this.cellBackground.style.setProperty('background-color', `#${this.params.content.style['background-color']}`);
    // this.cellBackground.style.setProperty('width', `${this.dataset.width + 1}px`);
    // this.cellBackground.style.setProperty('height', `${this.dataset.height + 1}px`);
    // cell.appendChild(this.cellBackground);

    // borders
    this.cellBorders = document.createElement('div');
    this.cellBorders.className = 'grid-border';
    // this.cellBorders.style.setProperty('width', `${this.dataset.width}px`);
    // this.cellBordersOffset = document.createElement('div');
    this.cellBordersOffset.className = 'grid-border-offset';
    this.cellBorders.appendChild(this.cellBordersOffset);
    // this.cell.appendChild(this.cellBorders);

    // history

    // this.cell.appendChild(this.cellHistory);

    // text
    // this.cellTextContainer = document.createElement('div');
    this.cellTextContainer.className = 'grid-text';
    this.cellText = document.createElement('div');
    this.cellTextContainer.appendChild(this.cellText);
    // cell.appendChild(this.cellTextContainer);

    // button menu

    // update
    this.appendChildren();
    this.update(this.params.content);
    this.updateSoftMerge();
  }

  public update(content: IGridPageContentCell): void {
    this.params.content = { ...this.params.content, ...content };
    this.params.row[this.col] = this.params.content;
    this.refresh();
  }

  public refresh(): void {
    if (!this.init) return;
    let backgroundColor: string =
      this.style && this.style.background ? this.style.background : `#${this.params.content.style['background-color']}`;
    let textColor: string =
      this.style && this.style.color ? this.style.color : `#${this.params.content.style['color']}`;
    this.cellBackground.style.setProperty('background-color', backgroundColor);
    this.cell.classList.remove('light', 'dark');
    this.cell.classList.add(helpers.getContrastYIQ(backgroundColor));
    if (this.cellBackgroundHighlight) {
      this.cellBackgroundHighlight.style.setProperty('background-color', textColor);
    }

    if (`${this.params.content.value}`.indexOf('data:image') > -1) {
      this.cell.classList.add(`image`);
      if (!this.cellImage) {
        this.cellImage = document.createElement('img');
      }
      this.cellImage.src = `${this.params.content.value}`;
      this.cell.innerHTML = '';
      this.cell.appendChild(this.cellImage);
      this.isImage = true;
    } else {
      // this.cellBackground.style.removeProperty('background-image');
      this.cell.classList.remove(`image`);
      if (this.cell.contains(this.cellImage)) {
        this.cell.removeChild(this.cellImage);
        this.appendChildren();
      }
      this.isImage = false;
      this.applyButton();
      this.applyBorders();
      this.applyText(textColor);
    }
    // this.updateSoftMerge();
  }

  public renderer(): void {}

  public setButton(button: IActionsButton | null): void {
    this.button = button;
    this.refresh();
  }

  public setFound(n: boolean): void {
    if (!n) {
      this.cell.classList.remove('cell-found');
    } else {
      this.cell.classList.add(`cell-found`);
    }
  }

  public setStyle(style: IActionsStyleConditionStyle | null): void {
    this.style = style;
    this.refresh();
  }

  public setPermission(permission: string): void {
    this.permission = permission;
    this.cell.classList.add(`permission-${permission}`);
    this.refresh();
  }

  public setSorting(direction: string): void {
    if (!direction) {
      this.cell.classList.remove('sorting-asc', 'sorting-desc');
    } else {
      this.cell.classList.add(`sorting-${direction}`);
    }
  }

  public setRowIndex(row: number, y: number): void {
    this.dataset.row = row;
    this.index.row = row;
    this.dataset.y = y;
    this.cell.dataset.row = row.toString();
    // if (this.freeze === 'common') {
    //   this.cell.style.setProperty('transform', `translate(${this.dataset.x}px, ${this.dataset.y}px)`);
    // } else {
    //   this.cell.style.setProperty('transform', `translateX(${this.dataset.x}px)`);
    // }
  }

  public edit(value?: string, touch?: boolean): void {
    // let p: Promise<any> = new Promise((resolve, reject) => {
    if (this.permission !== 'rw') {
      // reject(this.permission);
      this.emit(this.STOP);
      return;
    }
    if (this.button && ['select', 'rotate'].indexOf(this.button.type) > -1) {
      return;
    }
    if (this.input && this.input.parentElement) {
      this.input.parentElement.removeChild(this.input);
    }
    let input = document.createElement('input');
    this.input = input;
    input.classList.add('grid-input');
    input.value = value || `${this.params.content.formatted_value}` || `${this.params.content.value}`;
    this.oldValue = `${this.params.content.formatted_value}` || `${this.params.content.value}`;
    this.cellText.innerHTML = this.params.content.formatted_value = this.params.content.value = input.value;
    input.style.setProperty('width', `${this.dataset.width}px`);
    this.inputStyles.forEach(style => {
      if (style === 'color') return;
      input.style.setProperty(style, this.params.content.style[style]);
    });
    this.cell.style.setProperty('z-index', '10000');
    let done: boolean = false;
    this.cell.appendChild(this.cellTextContainer);
    let interval = setInterval(() => {
      if (input.parentElement === null) return;
      clearInterval(interval);
      input.addEventListener('keydown', evt => {
        if (
          (['ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown'].indexOf(evt.key) > -1 && value) ||
          ['Enter', 'Tab'].indexOf(evt.key) > -1
        ) {
          done = true;
          this.emit(this.DONE, input.value);
          this.emit(this.STOP, evt.key);
        }
      });
      input.addEventListener('keyup', () => {
        this.cellText.textContent = this.params.content.formatted_value = this.params.content.value = input.value;
        this.emit(this.VALUE, input.value);
      });
      input.addEventListener('blur', evt => {
        console.log(evt);
        return;
        this.input.parentElement!.removeChild(this.input);
        this.cell.style.setProperty('z-index', `${this.params.zIndexCol}`);
        if (done) return;
        if (this.cellText.textContent === this.oldValue) {
          this.emit(this.CANCEL);
        } else {
          this.emit(this.DONE, input.value);
        }
        this.emit(this.STOP);
      });
      // if (!touch) {
      input.focus();
      // }
      if (!value) {
        input.selectionStart = 0;
        input.selectionEnd = 999;
      }
    }, 20);
    if (!touch) {
      this.cell.appendChild(input);
    }
    // });
    // return p;
  }

  public onButton(): boolean {
    if (!this.button || this.buttonPopper) return false;
    // if (this.button.type === 'select') {
    //   this.buttonPopper = new Popper(this.cell, this.cellPopper, { placement: 'bottom-start' });
    //   this.cellPopper.style.setProperty('display', 'block');
    //   setTimeout(() => {
    //     window.addEventListener('click', this.onWindowClick);
    //   }, 300);
    //   return true;
    // }
    this.oldValue = `${this.params.content.formatted_value}` || `${this.params.content.value}`;
    if (this.button.type === 'checkbox') {
      // return true;
    }
    if (this.button.type === 'rotate') {
      let value = helpers.getNextValueInArray(
        this.params.content.formatted_value || this.params.content.value,
        this.button.options
      );
      this.cellText.textContent = this.params.content.formatted_value = value;
      this.emit(this.VALUE, value);
      this.emit(this.DONE, value);
      this.emit(this.STOP);
      return true;
    }
    return false;
  }

  public cancel(restore?: boolean): void {
    this.destroyPopper();
    if (restore) {
      this.cellText.textContent = this.oldValue;
      this.params.content.value = this.oldValue;
      this.params.content.formatted_value = this.oldValue;
    }
    if (this.input) {
      this.input.blur();
    }
  }

  public selected(n: boolean, sides: string[] = []): void {
    this.cell.classList.remove('selected', 'top', 'left', 'bottom', 'right', 'first');
    if (n) {
      this.cell.classList.add('selected', ...sides);
    }
  }

  public setFreeze(which: string, cell?: IGridCell, cell2?: IGridCell): void {
    this.cell.style.setProperty('transform', `translateX(${this.dataset.x}px)`);
    if (!which && !this.freeze) return;
    this.freeze = which;
    this.hidden = false;
    // reset borders
    this.applyBorders();
    // this.cell.style.setProperty('width', `${parseFloat(this.params.col.style.width) + 1}px`);
    // this.cell.style.setProperty('height', `${parseFloat(this.params.col.style.height) + 1}px`);
    // take border properties from the next cell
    if (!cell) return;
    let borderStyles: any = this.transformStyle(cell.params.content.style, 'cell');
    let borderStyles2: any = {};
    if (cell2) {
      borderStyles2 = this.transformStyle(cell2.params.content.style, 'cell');
    }
    Object.keys(borderStyles).forEach(key => {
      if (which === 'common' || which === 'col') {
        if (key.indexOf('border-left') > -1)
          this.cellBordersOffset.style.setProperty(
            key.replace('left', 'right'),
            borderStyles[key].replace('left', 'right')
          );
      }
      if (which === 'common' || which === 'row') {
        if (key.indexOf('border-top') > -1)
          this.cellBordersOffset.style.setProperty(
            key.replace('top', 'bottom'),
            borderStyles[key].replace('top', 'bottom')
          );
      }
    });
    Object.keys(borderStyles2).forEach(key => {
      if (key.indexOf('border-top') > -1)
        this.cellBordersOffset.style.setProperty(
          key.replace('top', 'bottom'),
          borderStyles2[key].replace('top', 'bottom')
        );
    });
    // this.updateSoftMerge();
    if (which === 'common') {
      this.cell.style.setProperty('transform', `translate(${this.dataset.x}px, ${this.dataset.y}px)`);
    }
  }

  public highlight(show: number = 0.5, hide: number = 1): void {
    if (this.highlightTimer) {
      clearTimeout(this.highlightTimer);
      this.highlightTimer = null;
    }
    if (!this.cellBackgroundHighlight) {
      this.cellBackgroundHighlight = document.createElement('div');
      this.cellBackgroundHighlight.className = 'grid-highlight';
      this.cellBackgroundHighlight.style.setProperty('background-color', `#${this.params.content.style['color']}`);
      this.cellBackground.appendChild(this.cellBackgroundHighlight);
    }
    this.cellBackgroundHighlight.classList.remove('fade');
    this.cellBackgroundHighlight.classList.add('flash');
    this.highlightTimer = setTimeout(() => {
      // this.cellBackgroundHighlight.classList.remove('flash');
      this.cellBackgroundHighlight.classList.add('fade');
    }, show * 1000);
  }

  public history(n: boolean, user: number = 0): void {
    this.hasHistory = n;
    if (n) {
      if (!this.cellHistory) {
        this.cellHistory = document.createElement('div');
        this.cellHistory.className = 'grid-history';
        this.cell.appendChild(this.cellHistory);
      }
      this.cell.classList.add('history', `user-${user}`);
    } else {
      let users: string[] = Array(10)
        .fill(1)
        .map((x, i) => `user-${i}`);
      this.cell.classList.remove('history', ...users);
      if (this.cellHistory && this.cellHistory.parentNode) {
        this.cell.removeChild(this.cellHistory);
        this.cellHistory = null;
      }
    }
  }

  public updateSoftMerge(hiddenColumns: number[] = []): void {
    if (!this.init) return;
    this.cellBordersOffset.style.removeProperty('border-left-color');
    this.cellBordersOffset.style.removeProperty('border-right-color');
    this.applyBorders();
    this.cellTextContainer.style.setProperty('width', `${this.dataset.width}px`);
    this.cellText.style.removeProperty('margin-left');
    this.cellText.style.removeProperty('margin-right');
    this.dataset.overflow = this.dataset.width;

    // ensure merging cell is visible
    if (this.params.mergedCells.last.indexOf(this.params.colIndex) > -1) {
      this.cellBordersOffset.style.setProperty('border-left-color', 'transparent');
      // this.cellBackground.style.setProperty('background-color', 'transparent');
    } else if (this.params.mergedCells.cells.indexOf(this.params.colIndex) > -1) {
      this.cellBordersOffset.style.setProperty('border-left-color', 'transparent');
      this.cellBordersOffset.style.setProperty('border-right-color', 'transparent');
      // this.cellBackground.style.setProperty('background-color', 'transparent');
    } else {
      // this.cellBackground.style.setProperty('background-color', `#${this.params.col.style['background-color']}`);
    }

    // clear any current merge references
    this.mergedCells.cells.forEach(col => {
      if (this.params.mergedCells.cells.indexOf(col) > -1) {
        this.params.mergedCells.cells.splice(this.params.mergedCells.cells.indexOf(col), 1);
      }
    });
    this.mergedCells.last.forEach(col => {
      if (this.params.mergedCells.last.indexOf(col) > -1) {
        this.params.mergedCells.last.splice(this.params.mergedCells.last.indexOf(col), 1);
      }
    });
    this.mergedCells = {
      cells: [],
      last: []
    };

    // check if there is any point going further
    let align = this.cellTextContainer.style.getPropertyValue('justify-content');
    if (
      align === 'flex-end' ||
      this.cellText.textContent === '' ||
      this.params.content.style['text-wrap'] === 'wrap' ||
      (this.params.row[this.params.colIndex + 1] && this.params.row[this.params.colIndex + 1].formatted_value)
    ) {
      if (align === 'flex-end') {
        let offset = this.dataset.overflow - this.dataset.width;
        this.cellText.style.setProperty('margin-right', `${offset}px`);
      }
      return;
    }

    // measure cell width
    let clone = this.cell.cloneNode(true) as HTMLDivElement;
    let colWidth = this.dataset.width;
    this.params.mergeEl.innerHTML = '';
    this.params.mergeEl.appendChild(clone);
    const textContainer = clone.getElementsByClassName('grid-text')[0];
    if (!textContainer) return;
    textContainer.classList.remove('grid-button');
    const text = textContainer.getElementsByTagName('div')[0];
    text.style.setProperty('padding', '0');
    const textRect = text.getBoundingClientRect();
    if (textRect.width < this.dataset.width) {
      return;
    }
    // find where to merge to
    let colLast = this.params.colIndex;
    let colCells: number[] = [];
    for (let colMerge = this.params.colIndex + 1; colMerge < this.params.row.length; colMerge++) {
      if (
        !this.params.row[colMerge] ||
        this.params.row[colMerge].formatted_value !== '' ||
        colWidth > textRect.width ||
        hiddenColumns.indexOf(colMerge) > -1
      ) {
        break;
      }
      colLast++;
      colWidth += Math.round(parseFloat(this.params.row[colMerge].style.width));
      if (this.params.mergedCells.cells.indexOf(colMerge) < 0) {
        this.params.mergedCells.cells.push(colMerge);
      }
      colCells.push(colMerge);
    }
    if (this.params.mergedCells.last.indexOf(colLast) < 0) this.params.mergedCells.last.push(colLast);
    this.mergedCells.last = [colLast];
    this.mergedCells.cells = colCells;
    // adjust params
    this.cellBordersOffset.style.setProperty('border-right-color', 'transparent');
    this.cellTextContainer.style.setProperty('width', `${colWidth + 1}px`);
    // this.cellBackground.style.setProperty('width', `${colWidth}px`);
    this.dataset.overflow = colWidth;
    this.cell.dataset.overflow = colWidth.toString();
    // let align = this.cellTextContainer.style.getPropertyValue('justify-content');
    // if (align === 'center') {
    //   console.log('DATASET', this.dataset);
    //   let offset = (this.dataset.overflow - this.dataset.width) / 2;
    //   console.log('Offset', offset);
    //   this.cellText.style.setProperty('margin-left', `${offset}px`);
    // }
  }

  public setSize(which: string, size: number): void {
    this.dataset[which] = size;
    this.cell.style.setProperty(which, `${this.dataset[which]}px`);
    // this.cellBackground.style.setProperty(which, `${this.dataset[which] + 1}px`);
    if (which === 'width') {
      // this.cellBorders.style.setProperty('width', `${this.dataset.width}px`);
      this.cellTextContainer.style.setProperty(which, `${this.dataset.width}px`);
    }
  }

  public setOffset(offset: number, which: string): void {
    if (this.freeze === 'common') {
      let translate =
        which === 'width'
          ? `translate(${this.dataset.x + offset}px, ${this.dataset.y}px)`
          : `translate(${this.dataset.x}px, ${this.dataset.y + offset}px)`;
      // console.log(translate, which);
      this.cell.style.setProperty('transform', translate);
    } else {
      this.cell.style.setProperty('transform', `translateX(${this.dataset.x + offset}px)`);
    }
  }

  public setXPosition(position: number, move?: boolean): void {
    this.dataset.x += position;
    if (!move) return;
    this.cell.style.setProperty('transform', `translateX(${this.dataset.x}px)`);
  }

  public setYPosition(position: number, move?: boolean): void {
    this.dataset.y += position;
    if (this.freeze === 'common') {
      this.cell.style.setProperty('transform', `translate(${this.dataset.x}px, ${this.dataset.y}px)`);
    }
  }

  public getDetails(): IGridCellDetails {
    return {
      content: this.params.content,
      row: this.row,
      col: this.col,
      position: this.position,
      index: this.index,
      dataset: this.dataset,
      reference: this.reference,
      link: this.params.content.link,
      button: this.button,
      history: this.hasHistory,
      checked: this.checked
    };
  }

  public getFlattenStyles(only?: string[]): string {
    let htmlStyle: any = {};
    let styles: any = this.params.content.style;
    if (this.style) {
      styles['background-color'] = this.style.background;
      styles.color = this.style.color;
    }

    this.cellStyles.forEach(s => {
      htmlStyle[s] = styles[s];
    });

    for (let key in this.borders.names) {
      let name: string = this.borders.names[key];
      let width: string = this.borders.widths[styles[`${key}bw`]] + 'px';
      let style: string = styles[`${key}bs`];
      let color: string = '#' + styles[`${key}bc`].replace('#', '');
      htmlStyle[`border-${name}`] = `${width} ${style} ${color}`;
    }

    let str = '';
    for (let attr in htmlStyle) {
      let value = htmlStyle[attr];
      if ((attr === 'background-color' || attr === 'color') && value.indexOf('#') === -1) {
        value = `#${value}`;
      }
      if (only && only.length && only.indexOf(attr) < 0) continue;
      str += `${attr}:${value};`;
    }
    return str;
  }

  public getValue(): any {
    return this.params.content.formatted_value;
  }

  public getRawValue(): any {
    return this.params.content.value;
  }

  public openOptions(): void {
    if (!this.button) return;
    this.button.options.forEach((option, index) => {
      if (option !== this.cellText.textContent) return;
      this.selectedOption = index;
    });
    this.updateSelectOptions();
  }

  public toggleCheckbox(n?: boolean): any {
    this.checked = n === undefined ? !this.checked : n;
    this.params.content.checked = this.checked;
    let response: any = {
      checked: this.checked
    };
    if (this.button && this.button.checkboxTrueValue) {
      this.cellText.textContent = this.params.content.value = this.params.content.formatted_value = this.checked
        ? this.button.checkboxTrueValue
        : this.button.checkboxFalseValue;
      response.value = this.params.content.value;
    }
    if (this.checked) {
      this.cellTextContainer.classList.add('checked');
    } else {
      this.cellTextContainer.classList.remove('checked');
    }
    console.log('toggleCheckbox', this.checked);
    this.emit(this.CHECKED, this.checked);
    return response;
  }

  public selectOption(key: string): boolean {
    let dir = key.replace('Arrow', '').toLowerCase();
    if (!this.button || !this.hasButtonOptions) return false;
    if (dir === 'enter') {
      this.setSelectOption(key);
      return true;
    } else if (dir === 'down' || dir === 'right') {
      this.selectedOption++;
      if (this.selectedOption >= this.button.options.length) {
        this.selectedOption = 0;
      }
    } else {
      this.selectedOption--;
      if (this.selectedOption < 0) {
        this.selectedOption = this.button.options.length - 1;
      }
    }
    if (this.button.type === 'select') {
      this.updateSelectOptions();
    } else {
      this.setSelectOption();
    }
    return true;
  }

  private updateSelectOptions(): void {
    for (let i = 0; i < this.cellPopper.childNodes.length; i++) {
      let a: HTMLAnchorElement = this.cellPopper.childNodes[i] as HTMLAnchorElement;
      a.classList.remove('selected');
    }
    let a: HTMLAnchorElement = this.cellPopper.childNodes[this.selectedOption] as HTMLAnchorElement;
    a.classList.add('selected');
  }

  private setSelectOption(key?: string): void {
    if (!this.button) return;
    let option: string = this.button.options[this.selectedOption];
    if (option === this.params.content.formatted_value) {
      this.emit(this.STOP, key);
      return;
    }
    this.cellText.textContent = this.params.content.value = this.params.content.formatted_value = option;
    this.emit(this.VALUE, option);
    this.emit(this.DONE, option);
    this.emit(this.STOP, key);
  }

  private appendChildren(): void {
    this.cell.appendChild(this.cellBackground);
    this.cell.appendChild(this.cellBorders);
    // this.cell.appendChild(this.cellHistory);
    this.cell.appendChild(this.cellTextContainer);
  }

  private getDataset(params: IGridCellParams): IGridCellDataset {
    return {
      width: Math.round(parseFloat(params.content.style.width!)),
      height: Math.round(parseFloat(params.content.style.height!)),
      overflow: Math.round(parseFloat(params.content.style.width!)),
      x: params.x,
      y: params.y,
      row: params.rowIndex,
      col: params.colIndex
    };
  }

  private applyBorders(): void {
    if (!this.init) return;
    // this.cellBordersOffset.style.setProperty('border-color', `#${this.params.col.style['background-color']}`);
    this.borderStyles = this.transformStyle(this.params.content.style, 'cell');
    // if (!Object.keys(this.borderStyles).length) {
    //   if (this.cellBorders.parentNode) {
    //     this.cell.removeChild(this.cellBorders);
    //   }
    //   return;
    // }
    // if (!this.cellBorders.parentNode) {
    //   this.cell.appendChild(this.cellBorders);
    // }
    Object.keys(this.borderStyles).forEach(key => {
      this.cellBordersOffset.style.setProperty(key, this.borderStyles[key]);
    });
    // grab opposite border styles
    if (this.params.row[this.dataset.col + 1]) {
      let borderStyles2: any = this.transformStyle(this.params.row[this.dataset.col + 1].style, 'cell', true);
      Object.keys(borderStyles2).forEach(key => {
        if (key.indexOf('border-left') > -1)
          this.cellBordersOffset.style.setProperty(
            key.replace('left', 'right'),
            borderStyles2[key].replace('left', 'right')
          );
      });
    }
    if (GridContent.content[this.row + 1] && GridContent.content[this.row + 1][this.col]) {
      let borderStyles2: any = this.transformStyle(GridContent.content[this.row + 1][this.col].style, 'cell', true);
      Object.keys(borderStyles2).forEach(key => {
        if (key.indexOf('border-top') > -1)
          this.cellBordersOffset.style.setProperty(
            key.replace('top', 'bottom'),
            borderStyles2[key].replace('top', 'bottom')
          );
      });
    }
  }

  private applyText(textColor: string): void {
    let textStyles: any = this.transformStyle(this.params.content.style, 'text');
    this.cellTextContainer.removeAttribute('style');
    Object.keys(textStyles).forEach(key => {
      this.cellTextContainer.style.setProperty(key, textStyles[key]);
    });
    this.cellTextContainer.style.setProperty('color', textColor);
    if (!this.button && (this.permission === 'no' || !`${this.params.content.formatted_value || this.params.content.value}`)) {
      this.cellText.textContent = '';
      if (this.cellTextContainer.parentNode) {
        this.cell.removeChild(this.cellTextContainer);
      }
    } else {
      if (!this.cellTextContainer.parentNode) {
        this.cell.appendChild(this.cellTextContainer);
      }
      if (
        this.params.content.style['number-format'] &&
        this.params.content.style['number-format'].indexOf('$* #,##0.00') > -1 &&
        helpers.isNumber(this.params.content.value)
      ) {
        let symbol = `${this.params.content.formatted_value || this.params.content.value}`.trim().split(' ')[0];
        this.cellText.className = 'number';
        this.cellText.innerHTML = `<div class="symbol">${symbol}</div>${helpers.formatNumber(
          parseFloat(`${this.params.content.value}`)
        )}`;
      } else {
        this.cellText.className = 'text';
        // this.cellText.innerHTML = `${this.params.content.formatted_value || this.params.content.value}`.replace(/\n/g,'<br />');
        this.cellText.textContent = `${this.params.content.formatted_value || this.params.content.value}`;
      }
    }
    this.cellTextContainer.style.setProperty('width', this.cell.style.width);
    this.cell.classList.remove('permission-no', 'permission-rw', 'permission-ro');
    this.cell.classList.add(`permission-${this.permission}`);
    // this.cellStatus.style.setProperty('width', this.cell.style.width);
  }

  private applyButton(): void {
    if (this.button) {
      this.isButton = true;
      this.canEditButton = true;
      this.cellTextContainer.classList.add('grid-button');
      if (this.button.type === 'select') {
        if (!this.cellPopper) {
          this.cellPopper = document.createElement('div');
          this.cellPopper.className = 'grid-popper';
          this.cellPopper.style.setProperty('min-width', `${this.dataset.width}px`);
          this.cellPopper.addEventListener('mouseleave', evt => {
            this.emit(this.STOP);
          });
        }
        this.hasButtonOptions = true;
        this.canEditButton = false;
        this.cellPopper.innerHTML = '';
        this.button.options.forEach(option => {
          let a = document.createElement('a');
          a.textContent = option;
          a.addEventListener('click', () => {
            if (option === this.params.content.formatted_value) {
              this.emit(this.STOP);
              return;
            }
            this.cellText.textContent = this.params.content.formatted_value = option;
            this.emit(this.VALUE, option);
            this.emit(this.DONE, option);
            this.emit(this.STOP);
          });
          this.cellPopper.appendChild(a);
        });
        // this.cell.appendChild(this.cellPopper);
      } else if (this.button.type === 'rotate') {
        this.hasButtonOptions = true;
        this.canEditButton = false;
      } else if (this.button.type === 'checkbox') {
        if (!this.cellCheckbox) {
          this.cellCheckbox = document.createElement('div');
          this.cellCheckbox.className = 'checkbox';
        }
        // this.hasButtonOptions = true;
        this.canEditButton = true;
        this.cellTextContainer.classList.remove('grid-button');
        this.cellTextContainer.classList.add('grid-checkbox');
        // this.cellText.classList.add('checkbox');
        if (!this.cellTextContainer.contains(this.cellCheckbox)) {
          this.cellTextContainer.prepend(this.cellCheckbox);
        }
        if (this.button.checkboxTrueValue) {
          if (this.params.content.value == this.button.checkboxTrueValue) {
            this.checked = true;
            this.cellTextContainer.classList.add('checked');
          } else {
            this.checked = false;
            this.cellTextContainer.classList.remove('checked');
          }
        }
        // this.cellCheckbox.onclick = () => {
        // console.log('checkbox');
        // this.toggleCheckbox();
        // };
      }
    } else {
      this.isButton = false;
      this.hasButtonOptions = false;
      this.cellTextContainer.classList.remove('grid-button');
      this.cellTextContainer.classList.remove('grid-checkbox');
      if (this.cellCheckbox && this.cellTextContainer.contains(this.cellCheckbox)) {
        this.cellTextContainer.removeChild(this.cellCheckbox);
      }
      // this.cellText.classList.remove('checkbox');
      // if (this.cell.contains(this.cellPopper)) this.cell.removeChild(this.cellPopper);
    }
    if (this.params.content.link) {
      this.cellTextContainer.classList.add('link');
    } else {
      this.cellTextContainer.classList.remove('link');
    }
  }

  private destroyPopper(): void {
    if (!this.buttonPopper) return;
    this.buttonPopper.destroy();
    this.buttonPopper = undefined;
    this.cellPopper.style.setProperty('display', 'none');
    window.removeEventListener('click', this.onWindowClick);
  }

  private transformStyle(contentStyle: any, which: string, opposite?: boolean): any {
    let newStyle: any = {};
    if (which === 'cell') {
      // newStyle['background-color'] = '#' + contentStyle['background-color'];
      for (let key in this.borders.names) {
        // prevent border double up
        if (!opposite && key === 'l') {
          if (GridContent.content[this.row][this.col - 1]) continue;
        }
        if (!opposite && key === 't') {
          if (GridContent.content[this.row - 1] && GridContent.content[this.row - 1][this.col]) continue;
        }
        let name: string = this.borders.names[key];
        let width: string = this.borders.widths[contentStyle[`${key}bw`]] + 'px';
        let style: string = this.borders.styles[contentStyle[`${key}bs`]];
        let color: string = '#' + contentStyle[`${key}bc`].replace('#', '');
        if (style === 'none') {
          if (['left'].indexOf(name) > -1 && !this.dataset.col) {
            // newStyle[name] = '0px';
          }
          continue;
        }
        let widthN: number = parseFloat(width);
        let widthOffset = 0;
        if (['left'].indexOf(name) > -1) {
          // widthOffset = this.dataset.col ? 1 : 0;
          // if(this.dataset.col) widthN++;
        }
        if (['bottom'].indexOf(name) > -1) {
          widthOffset = -1;
        }
        // if ((name === 'top' && !this.params.rowIndex) || (name === 'left' && !this.params.colIndex)) {
        //   newStyle[name] = '0px';
        //   widthN--;
        // }
        if (widthN > 1) {
          let offset: number = widthN % 2 === 0 ? -widthN / 2 : -Math.ceil(widthN / 2);
          newStyle[name] = offset + 'px';
        } else if (widthN <= 1 && ['left'].indexOf(name) > -1) {
          // newStyle[name] = '0px';
        }
        newStyle[`border-${name}`] = `${width} ${style} ${color}`;
      }
    }
    if (which === 'text') {
      // newStyle['background-color'] = '#' + contentStyle['background-color'];
      this.textStyles.forEach(name => {
        if (name === 'justify-content') {
          if (contentStyle['text-align'] === 'right') {
            newStyle['justify-content'] = 'flex-end';
          }
          if (contentStyle['text-align'] === 'center') {
            newStyle['justify-content'] = 'center';
          }
        } else if (name === 'align-items') {
          // vertical-align
          if (contentStyle['vertical-align'] === 'bottom' || contentStyle['vertical-align'] === 'none') {
            newStyle['align-items'] = 'flex-end';
          } else if (contentStyle['vertical-align'] === 'middle' || contentStyle['vertical-align'] === 'center') {
            newStyle['align-items'] = 'center';
          }
        } else if (name === 'text-wrap') {
          if (contentStyle['text-wrap'] === 'wrap' || contentStyle['word-wrap'] === 'break-word') {
            newStyle['white-space'] = 'pre-wrap';
            newStyle['line-height'] = '1.1';
          } else {
            newStyle['white-space'] = 'nowrap';
          }
        } else if (name === 'color') {
          newStyle.color = '#' + contentStyle.color;
        } else if (name === 'font-family') {
          newStyle[`${name}`] = contentStyle[`${name}`] + ', sans-serif';
        } else {
          newStyle[`${name}`] = contentStyle[`${name}`];
        }
      });
    }
    return newStyle;
  }

  private onWindowClick = (evt: MouseEvent) => {
    let isPopper = helpers.isTarget(evt.target, this.cellPopper);
    let isCell = helpers.isTarget(evt.target, this.cell);
    if (isPopper || isCell) return;
    this.destroyPopper();
  };
}
