import Helpers, { IHelpers } from '../Helpers';
import Emitter, { IEmitter } from '../Emitter';
import { IPageContentLink, IPageContentCell, IPageContentCellIndex, IPageCellStyle } from '../Page/Content';
import { IActionsButton } from '../Actions/Buttons';
import GridPage, { IGridPage } from './GridPage';
import { IActionsStyleConditionStyle } from '../Actions/Styles';
import GridEvents, { IGridEvents } from './GridEvents';
import { IGridSelection, IGrid } from './Grid';

const helpers: IHelpers = new Helpers();

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 IGridCellEvent {
  evt: any;
  cell: IGridCell;
  value: string;
  done: boolean;
  checked: boolean;
  data?: any;
}

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

export interface IGridCell extends IEmitter {
  Page: IGridPage;
  Events: IGridEvents;

  row: number;
  rowIndex: number;
  col: number;
  initialized: boolean;
  created: boolean;
  cell: HTMLDivElement;
  cellPopper: HTMLDivElement;
  dataset: IGridCellDataset;
  button: IActionsButton | null;
  style: IActionsStyleConditionStyle | null;
  reference: string;
  checked: boolean;
  hasHistory: boolean;
  content: IPageContentCell;
  input: HTMLInputElement;
  editing: boolean;
  isButton: boolean;
  hasButtonOptions: boolean;
  canEditButton: boolean;
  isImage: boolean;
  hidden: boolean;
  permission: string;
  position: IPageContentCellIndex;
  index: IGridContentCellPosition;
  type: string;
  found: boolean;
  sticky: boolean;

  // create(): void;
  // init(row: number, col: number): void;
  update(force?: boolean): void;
  updateSoftMerge(cols?: number[], direction?: string, zIndex?: number): void;
  updateMergeWidth(scrollLeft: number, freezeColWidth?: number): void;
  // refresh(): void;

  setData(): void;
  setFreeze(n: boolean): void;
  setSelected(selection: IGridSelection): void;
  setButton(button: IActionsButton | null): void;
  setStyle(style: IActionsStyleConditionStyle | null): void;
  setPermission(permission: string): void;
  setSorting(direction: string): void;
  setFound(n: boolean): void;
  highlight(show?: number, hide?: number): void;
  history(n: boolean, user?: number): void;

  getDetails(): IGridCellDetails;
  getFlattenStyles(only?: string[]): string;
  getInputStyles(): string;
  // edit(value?: string): void;
  cancel(restore?: boolean): void;
  selectOption(direction: string): boolean;
  openOptions(): void;
  onButton(): boolean;
}

export class GridCell extends Emitter implements IGridCell {
  // public Page!: IGridPage;
  // public Events!: IGridEvents;
  public type: string = 'cell';
  public created: boolean = false;
  public initialized: boolean = false;
  
  public selected: boolean = false;
  public found: boolean = false;
  public dataset: IGridCellDataset = {
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    overflow: 0,
    row: 0,
    col: 0
  };
  public button: IActionsButton | null = null;
  public link: IPageContentLink | null = null;
  public style: IActionsStyleConditionStyle | null = null;
  public reference: string = '';
  public checked: boolean = false;
  public hasHistory: boolean = false;
  public isButton: boolean = false;
  public hasButtonOptions: boolean = false;
  public canEditButton: boolean = false;
  public isImage: boolean = false;
  public hidden: boolean = false;
  public permission: string = 'rw';
  public position: IPageContentCellIndex = {
    row: -1,
    col: -1
  };
  public index: IGridContentCellPosition = {
    row: -1,
    col: -1
  };

  // dom
  public cell!: HTMLDivElement;
  public cellImage!: HTMLImageElement;
  public cellBackground!: HTMLDivElement;
  public cellBackgroundHighlight!: HTMLDivElement;
  private cellHistory!: HTMLDivElement | null;
  public cellBorders!: HTMLDivElement;
  public cellTextContainer!: HTMLDivElement;
  public cellText!: HTMLDivElement;
  public cellCheckbox!: HTMLDivElement;
  public cellPopper!: HTMLDivElement;
  public input!: HTMLInputElement;

  public editing: boolean = false;
  public content!: IPageContentCell;

  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 cellStyles: string[] = [
    'background-color',
    'color',
    'font-family',
    'font-size',
    'font-style',
    'font-weight',
    'height',
    'text-align',
    'text-wrap',
    'width',
    'vertical-align'
  ];
  private highlightTimer: any = null;
  private oldValue: string = '';
  private _rowIndex: number = 0;
  private buttonPopper: any;
  private selectedOption: number = 0;
  private done: boolean = false;
  private editValue: any;
  private historyUser: number = -1;
  private mergedCells: number[] = [];
  private mergeWidth: number = 0;
  private _sticky: boolean = false;

  constructor(public Page: IGridPage, public Events: IGridEvents, public row: number, public col: number) {
    super();
    this.init(row, col);
  }

  public get rowIndex(): number {
    return this._rowIndex;
  }

  public set rowIndex(n: number) {
    this._rowIndex = n;
    this.index.row = n;
    if (this.created) this.cell.dataset.rowIndex = `${this.rowIndex}`;
  }

  public get sticky() {
    return this._sticky;
  }

  public set sticky(n: boolean) {
    this._sticky = n;
    if (n) {
      this.cell.classList.add('sticky');
    } else {
      this.cell.classList.remove('sticky');
    }    
  }

  private create(): void {
    if (this.created) return;

    this.created = true;

    // main cell
    this.cell = document.createElement('div');
    this.cell.classList.add('grid-cell');
    this.cell.classList.add(`grid-col-${this.col}`);
    this.cell.dataset.row = `${this.row}`;
    this.cell.dataset.col = `${this.col}`;
    this.cell.dataset.rowIndex = `${this.rowIndex}`;
    this.cell.style.setProperty('z-index', `${this.Page.cols - this.col}`);

    // background
    this.cellBackground = document.createElement('div');
    this.cellBackground.className = 'grid-background';
    this.cell.appendChild(this.cellBackground);

    // borders
    this.cellBorders = document.createElement('div');
    this.cellBorders.className = 'grid-border';

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

  public setData(): void {
    let pos = this.Page.cellPos(this.row, this.col);
    this.dataset.x = pos.x;
    this.dataset.y = pos.y;
    this.dataset.width = parseFloat(this.content.style.width!);
    this.dataset.overflow = parseFloat(this.content.style.width!);
    this.dataset.height = parseFloat(this.content.style.height!);
  }

  private init(row: number, col: number): void {
    let content = { value: '', formatted_value: '', style: {}, ...this.Page.originalContent[row][col] };
    this.content = content;
    this.rowIndex = row;
    this.index.row = this.row;
    this.index.col = this.col;
    this.position = this.content.index!;
    this.row = row;
    this.col = col;
    this.reference = `${helpers.toColumnName(this.col + 1)}${this.row + 1}`;
    this.setData();
  }

  private refresh(): void {
    if (!this.created) return;
    this.update(true);
  }

  public update(force?: boolean): void {
    let update = force || false;
    if (!this.created) {
      update = true;
      this.create();
    }
    if (this.Page.deltaContent[this.row] && this.Page.deltaContent[this.row][this.col]) {
      update = true;
      this.content = { ...this.Page.deltaContent[this.row][this.col] };
      delete this.Page.deltaContent[this.row][this.col];
    }
    if (!update) return;
    this.setData();
    let style: IPageCellStyle = { ...this.content.style, ...this.style };
    if (style.background) {
      style['background-color'] = style.background;
    }
    // merge column def style
    if (!this.row && this.Page.columnsDefs[this.col] && this.Page.columnsDefs[this.col].style) {
      style = { ...style, ...this.Page.columnsDefs[this.col].style };
    }
    let backgroundColor: string = `#${style['background-color']}`.replace('##', '#');
    let textColor: string = `#${style['color']}`.replace('##', '#');
    this.cellBackground.style.setProperty('background-color', backgroundColor);
    this.cell.classList.remove('light', 'dark');
    this.cell.classList.add(helpers.getContrastYIQ(backgroundColor));
    this.cell.classList.add(`permission-${this.permission}`);
    if (this.cellBackgroundHighlight) {
      this.cellBackgroundHighlight.style.setProperty('background-color', textColor);
    }
    if (`${this.content.value}`.indexOf('data:image') > -1) {
      this.cell.classList.add(`image`);
      if (!this.cellImage) {
        this.cellImage = document.createElement('img');
      }
      this.cellImage.src = `${this.content.value}`;
      this.cell.innerHTML = '';
      this.cell.appendChild(this.cellImage);
      this.isImage = true;
    } else {
      this.cell.classList.remove(`image`);
      if (this.cell.contains(this.cellImage)) {
        this.cell.removeChild(this.cellImage);
      }
      if (!this.found) {
        this.cell.classList.remove('cell-found');
      } else {
        this.cell.classList.add(`cell-found`);
      }      
      this.history(this.hasHistory, this.historyUser);
      this.applyButton();
      this.applyBorders();
      this.applyText(false, textColor);
      let interval: any = setInterval(() => {
        if (!this.cell.parentNode) return;
        clearInterval(interval);
        interval = null;
        this.applySoftMerge();
      }, 10);
    }
  }

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

  public getFlattenStyles(only?: string[]): string {
    let htmlStyle: any = {};
    let styles: any = this.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 setFreeze(n: boolean): void {
    // if (n === this.sticky) return;
    this.sticky = n;
    if (n) {
      // let left = this.Page.colWidths.slice(0, this.col).reduce((a: any, b: any) => a + b, 0);
      this.cell.classList.add('sticky');
      let offset = this.Page.headings ? 40 : 0;
      this.cell.style.setProperty('left', `${this.dataset.x + offset}px`);
    } else {
      this.cell.classList.remove('sticky');
      this.cell.style.removeProperty('left');
    }
  }

  public setSelected(selection: IGridSelection): void {
    this.selected = this.Page.selectedRows.includes(this.row) && this.Page.selectedCols.includes(this.col);
    // this.col >= selection.colFrom &&
    // this.row >= selection.rowFrom &&
    // this.col <= selection.colTo &&
    // this.row <= selection.rowTo;
    // if (n === this.selected) return;
    // console.log(this.selected, this.row, this.col);
    if (this.selected) {
      this.cell.classList.add('selected');
      if (this.col == selection.from!.col && this.row == selection.from!.row) this.cell.classList.add('selected-start');
      else this.cell.classList.remove('selected-start');
    } else {
      this.cell.classList.remove('selected', 'selected-start');
    }
  }

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

  public setFound(n: boolean): void {
    this.found = n;
    this.refresh();
  }

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

  public setPermission(permission: string): void {
    if (this.content.column_name) permission = 'ro';
    this.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 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.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;
    this.historyUser = user;
    if (!this.created) return;
    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 cancel(restore?: boolean): void {
    // this.destroyPopper();
    console.log('cancel');
    if (restore) {
      this.cellText.textContent = this.oldValue;
      this.content.value = this.oldValue;
      this.content.formatted_value = this.oldValue;
    }
    if (this.input) {
      this.input.blur();
    }
  }

  public onButton(): boolean {
    if (!this.button || this.buttonPopper) return false;
    this.oldValue = `${this.content.formatted_value}` || `${this.content.value}`;
    if (this.button.type === 'checkbox') {
      this.toggleCheckbox();
      return true;
    }
    if (this.button.type === 'rotate') {
      let value = helpers.getNextValueInArray(this.content.formatted_value || this.content.value, this.button.options);
      this.cellText.textContent = this.content.formatted_value = this.content.value = value;
      // this.emit(this.editValue, value);
      // this.emit(this.DONE, value);
      // this.emit(this.STOP);
      this.Events.emit(this.Events.CELL, {
        cell: this,
        evt: {
          type: 'blur'
        },
        done: true
      });
      return true;
    }
    return false;
  }

  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;
  }

  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.content.checked = this.checked;
    let response: any = {
      checked: this.checked
    };
    if (this.button && !this.button.checkboxToggle) {
      this.cellText.textContent = this.content.value = this.content.formatted_value = this.checked
        ? this.button.checkboxTrueValue
        : this.button.checkboxFalseValue;
      response.value = this.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);
    this.Events.emit(this.Events.CELL, {
      cell: this,
      evt: {
        type: 'check'
      },
      value: this.content.value,
      checked: this.checked
    });
    return response;
  }

  public getInputStyles(): string {
    const textStyles = ['font-family', 'font-size', 'font-style', 'font-weight', 'text-align'];

    let inputStyle: any = [];

    textStyles.forEach(key => {
      if (key === 'font-family') {
        inputStyle.push(`${key}:${this.content.style[key]}, sans-serif`);
      } else {
        inputStyle.push(`${key}:${this.content.style[key]}`);
      }
    });

    return inputStyle.join(';');
  }

  public updateSoftMerge(cols?: number[], direction?: string, zIndex?: number): void {
    if (!cols || !cols.includes(this.col)) {
      return;
    }
    if (direction == 'left') {
      this.cell.style.setProperty('z-index', `${zIndex}`);
    } else {
      this.cellBorders.style.setProperty('border-right-color', 'transparent');
    }
  }

  public updateMergeWidth(scrollLeft: number, freezeColWidth?: number): void {
    if (!this.mergeWidth) return;
    let width = this.mergeWidth - scrollLeft;
    if (width < this.dataset.width) width = this.dataset.width;
    this.cellTextContainer.style.setProperty('width', `${width}px`);
    if (this.content.style['text-align'] === 'center') {
      let marginLeft = width / 2 - this.dataset.width / 2;
      this.cellTextContainer.style.setProperty('margin-left', `-${marginLeft}px`);
    }    
  }

  private applyBorders(): void {
    let border: any = {
      top: 'none',
      bottom: 'none',
      right: 'none',
      left: 'none'
    };
    let borderOffset: any = {
      left: '0px',
      right: '-1px',
      top: '0px',
      bottom: '-1px'
    };

    let cellStyle = { ...this.content.style };
    // merge column def style
    if (!this.row && this.Page.columnsDefs[this.col] && this.Page.columnsDefs[this.col].style) {
      cellStyle = { ...cellStyle, ...this.Page.columnsDefs[this.col].style };
    }    

    // get next row cell top border
    if (
      this.Page.originalContent[this.row + 1] &&
      this.Page.originalContent[this.row + 1][this.col] &&
      this.Page.originalContent[this.row + 1][this.col].style[`tbs`] !== 'none'
    ) {
      cellStyle['bbs'] = this.Page.originalContent[this.row + 1][this.col].style[`tbs`];
      cellStyle['bbw'] = this.Page.originalContent[this.row + 1][this.col].style[`tbw`];
      cellStyle['bbc'] = this.Page.originalContent[this.row + 1][this.col].style[`tbc`];
    }
    // get right cell left border
    if (
      this.Page.originalContent[this.row] &&
      this.Page.originalContent[this.row][this.col + 1] &&
      this.Page.originalContent[this.row][this.col + 1].style[`lbs`] !== 'none'
    ) {
      cellStyle['rbs'] = this.Page.originalContent[this.row][this.col + 1].style[`lbs`];
      cellStyle['rbw'] = this.Page.originalContent[this.row][this.col + 1].style[`lbw`];
      cellStyle['rbc'] = this.Page.originalContent[this.row][this.col + 1].style[`lbc`];
    }

    Object.keys(this.borders.names).forEach(name => {
      if (name === 'l') {
        if (this.Page.originalContent[this.row][this.col - 1]) return;
      }

      if (name === 't') {
        if (this.Page.originalContent[this.row - 1] && this.Page.originalContent[this.row - 1][this.col]) return;
      }

      if (cellStyle[`${name}bs`] === 'none') {
        border[this.borders.names[name]] = 'none';
        return;
      }

      let widthN = parseFloat(this.borders.widths[cellStyle[`${name}bw`]]);

      let s = `${widthN}px ${cellStyle[`${name}bs`]} #${cellStyle[`${name}bc`]}`;

      // if (opposite) {
      //   if (name == "l") border["right"] = s;
      //   if (name === "t") border["bottom"] = s;
      // } else {
      border[this.borders.names[name]] = s;
      // }

      if (widthN > 1) {
        let offset = widthN % 2 === 0 ? -widthN / 2 : -Math.ceil(widthN / 2);
        borderOffset[this.borders.names[name]] = offset + 'px';
      } else {
        borderOffset[this.borders.names[name]] = ['t', 'l'].includes(name) ? '0px' : '-1px';
      }
    });

    let styles: any = [];
    Object.keys(border).forEach(key => {
      // this.cellBorders.style.setProperty(`border-${key}`, border[key]);
      if (border[key] != 'none') styles.push(`border-${key}:${border[key]}`);
    });
    if (styles.length)
      Object.keys(borderOffset).forEach(key => {
        // this.cellBorders.style.setProperty(key, border[key]);
        styles.push(`${key}:${border[key]}`);
      });
    if (styles.length) {
      this.cellBorders.style.cssText = styles.join(';');
      if (!this.cellBorders.parentNode) this.cell.appendChild(this.cellBorders);
    }
  }

  private applyText(input?: boolean, textColor?: string): void {
    // if (!this.content.formatted_value && !this.content.value) {
    //   return;
    // }

    const textStyles = [
      // 'background-color',
      'color',
      'font-family',
      'font-size',
      'font-style',
      'font-weight',
      // 'height',
      // 'number-format',
      'text-align',
      'vertical-align',
      'text-wrap'
      // 'width',
      // 'vertical-align',
    ];

    let textStyle: any = [];
    let inputStyle: any = [];

    textStyles.forEach(key => {
      if (key === 'color') {
        if (!input) {
          let color = textColor || `#${this.content.style.color}`;
          textStyle.push(`${key}:${color}`);
        }
      } else if (key === 'font-family') {
        textStyle.push(`${key}:${this.content.style[key]}, sans-serif`);
        inputStyle.push(`${key}:${this.content.style[key]}, sans-serif`);
      } else if (key === 'text-align') {
        textStyle.push(`${key}:${this.content.style[key]}`);
        inputStyle.push(`${key}:${this.content.style[key]}`);
        if (!input) if (this.content.style[key] === 'right') textStyle.push('justify-content:flex-end');
        if (!input) if (this.content.style[key] === 'center') textStyle.push('justify-content:center');
      } else if (key === 'vertical-align') {
        if (this.content.style[key] === 'bottom' || this.content.style[key] === 'none')
          if (!input) textStyle.push('align-items:flex-end');
        if (this.content.style[key] === 'center' || this.content.style[key] === 'middle')
          if (!input) textStyle.push('align-items:center');
      } else if (key === 'text-wrap') {
        if (this.content.style[key] === 'wrap' || this.content.style[key] === 'break-word') {
          if (!input) textStyle.push('white-space:pre-wrap');
        } else textStyle.push('white-space:nowrap');
      } else {
        inputStyle.push(`${key}:${this.content.style[key]}`);
        textStyle.push(`${key}:${this.content.style[key]}`);
      }
    });
    // textStyle.push(`width:${this.content.style.width!}`);

    if (input) {
      this.input.style.cssText = inputStyle.join(';');
      return;
    }

    this.cellTextContainer.style.cssText = textStyle.join(';');

    if (this.permission === 'no' || (!this.button && !this.content.column_name && (!`${this.content.formatted_value || this.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.Page.columnsDefs.length && !this.row) {
      //   this.cellText.className = 'text';
      //   let text = this.Page.columnsDefs[this.col] ? (this.Page.columnsDefs[this.col].display_name || this.Page.columnsDefs[this.col].name) : `Column ${this.col + 1}`;
      //   this.cellText.textContent = `${text}`;
      // } else if (
      if (
        this.content.style['number-format'] &&
        this.content.style['number-format'].indexOf('$* #,##0.00') > -1 &&
        helpers.isNumber(this.content.value)
      ) {
        let symbol = `${this.content.formatted_value || this.content.value}`.trim().split(' ')[0];
        this.cellText.className = 'number';
        this.cellText.innerHTML = `<div class="symbol">${symbol}</div>${helpers.formatNumber(
          parseFloat(`${this.content.value}`)
        )}`;
      } else {
        this.cellText.className = 'text';
        // this.cellText.innerHTML = `${this.content.formatted_value || this.content.value}`.replace(/\n/g,'<br />');
        this.cellText.textContent = `${this.content.formatted_value || this.content.value}`;
      }
    }

    // this.cellText.style.cssText = '';
    this.cell.classList.remove('permission-no', 'permission-rw', 'permission-ro');
    this.cell.classList.add(`permission-${this.permission}`);
  }

  private applySoftMerge(hiddenColumns: number[] = []): void {
    if (!this.created) return;

    // reset width
    this.cellTextContainer.style.removeProperty('width');
    this.cellTextContainer.style.removeProperty('margin-left');
    this.cell.style.setProperty('z-index', `${this.Page.cols - this.col}`);
    this.cell.classList.remove('align-right','align-center','align-left','merge');
    this.mergeWidth = 0;

    // check if there is any point going further
    let align = this.cellTextContainer.style.getPropertyValue('justify-content');
    if (
      this.button ||
      align === 'flex-end' ||
      !this.content.formatted_value ||
      this.content.style['text-wrap'] === 'wrap' ||
      !this.Page.originalContent[this.row][this.col + 1] ||
      this.Page.originalContent[this.row][this.col + 1].formatted_value
    ) {
      return;
    }

    // measure cell width
    let cellWidth = this.Page.colWidths[this.col];
    // let clone = this.cell.cloneNode(true) as HTMLDivElement;
    const textRect = this.cellText.getBoundingClientRect();
    let textWidth = (textRect.width / this.Page.scale) - 4;
    let marginLeft = 0;
    if (this.content.style['text-align'] === 'center') {
      textWidth = textWidth / 2 + cellWidth / 2;
    }
    // const textWidth = (textRect.width * size - 2) / this.Page.scale;
    if (textWidth < cellWidth) {
      return;
    }
    let colWidth = cellWidth;

    // find where to merge to
    let colCells: number[] = [this.col];
    for (let colMerge = this.col + 1; colMerge < this.Page.originalContent[this.row].length; colMerge++) {
      if (
        !this.Page.originalContent[this.row][colMerge] ||
        this.Page.originalContent[this.row][colMerge].formatted_value !== '' ||
        colWidth > textWidth ||
        hiddenColumns.indexOf(colMerge) > -1
      ) {
        break;
      }
      colWidth += Math.round(parseFloat(this.Page.originalContent[this.row][colMerge].style.width!));
      colCells.push(colMerge);
    }
    let colLeftWidth = cellWidth;
    let colCellsLeft: number[] = [];
    if (this.content.style['text-align'] === 'center') {
      for (let colMerge = this.col -1; colMerge >= 0; colMerge--) {
        if (
          !this.Page.originalContent[this.row][colMerge] ||
          this.Page.originalContent[this.row][colMerge].formatted_value !== '' ||
          colLeftWidth > textWidth ||
          hiddenColumns.indexOf(colMerge) > -1
        ) {
          break;
        }
        colLeftWidth += Math.round(parseFloat(this.Page.originalContent[this.row][colMerge].style.width!));
        colCellsLeft.push(colMerge);
      }
    }
    colCells.pop();
    this.cellBorders.style.setProperty('border-right-color', 'transparent');
    this.cellTextContainer.style.setProperty('width', `${colWidth}px`);
    if (this.content.style['text-align'] === 'center') {
      marginLeft = colWidth / 2 - cellWidth / 2;
      this.cellTextContainer.style.setProperty('margin-left', `-${marginLeft}px`);
    } else if (this.content.style['text-align'] === 'left') {
      this.mergeWidth = colWidth;
    }
    this.cell.classList.add(`align-${this.content.style['text-align']}`);
    this.dataset.overflow = colWidth;
    this.updateMergeWidth(this.Page.scrollLeft);
    this.Events.emit(this.Events.CELL, {
      cell: this,
      data: {
        left: colCellsLeft,
        right: colCells,
        zIndex: this.Page.cols - this.col
      },
      evt: {
        type: 'merge'
      }
    });    
  }

  private applyButton(): void {
    if (this.button && this.permission != 'no' && (!this.Page.columnsDefs.length || (this.Page.columnsDefs && this.row))) {
      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.Events.emit(this.Events.CELL, {
              cell: this,
              evt: {
                type: 'blur'
              }
            });
          });
        }
        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', () => {
            let done = false;
            if (option !== this.content.formatted_value) {
              this.cellText.textContent = this.content.value = this.content.formatted_value = option;
              done = true;
            }
            // this.emit(this.editValue, option);
            // this.emit(this.DONE, option);
            // this.emit(this.STOP);
            this.Events.emit(this.Events.CELL, {
              cell: this,
              evt: {
                type: 'blur'
              },
              done
            });
          });
          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.checkboxToggle) {
          if (this.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.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 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 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();
  };

  private setSelectOption(key?: string): void {
    if (!this.button) return;
    let option: string = this.button.options[this.selectedOption];
    let done = false;
    if (option !== this.content.formatted_value) {
      this.cellText.textContent = this.content.value = this.content.formatted_value = option;
      done = true;
    }
    this.Events.emit(this.Events.CELL, {
      cell: this,
      evt: {
        type: 'blur'
      },
      done
    });
  }
}
