import _ from "underscore";
import {
  IPageContent,
  IPageCellStyle,
  IPageContent2,
  IPageCellStyle2,
  IPageContent2OptionalFieldsLink,
  IPageContent2ParamMap,
} from "./Page/Content";
import { IPageContentCell } from "./Page/Content";
import { IPageColumnStyles } from "./Page/Page";
import { helpers } from ".";

export interface IUtils {
  parseApiError: (err: any, def?: string) => string;
  clonePageContent(content: IPageContent, removeStyles?: boolean): IPageContent;
  comparePageContent(
    currentContent: any,
    newContent: any,
    ignore_styles?: boolean
  ): IUtilsDiff | false;
  mergePageContent(_original: any, _deltas?: any, style?: any): any;
  mergePageContent2(original: IPageContent, deltas: IPageContent): any;
  getDefaultCellStyle(): IPageCellStyle;
  transformStyle(style: any, l2s?: boolean): IPageContent;
  transformContent(content: any): IPageContent;
}

export interface IUtilsDiff {
  content: IPageContent;
  content_diff: IPageContent;
  colDiff: number;
  rowDiff: number;
  sizeDiff: boolean;
}

class Utils implements IUtils {
  constructor() {
    return;
  }

  public static get DEFAULT_CELL_STYLE(): IPageCellStyle {
    return {
      "background-color": "FFFFFF",
      color: "000000",
      "font-family": "Calibri",
      "font-size": "11pt",
      "font-style": "normal",
      "font-weight": "normal",
      "text-wrap": "normal",
      "text-align": "left",
      "vertical-align": "bottom",
      width: "64px",
      height: "20px",
      "number-format": "General",
      lbs: "none",
      lbw: "none",
      lbc: "none",
      rbs: "none",
      rbw: "none",
      rbc: "none",
      tbs: "none",
      tbw: "none",
      tbc: "none",
      bbs: "none",
      bbw: "none",
      bbc: "none",
    };
  }

  public parseApiError(err: any, def: string = ""): string {
    let msg: string = def;

    if (err.data) {
      let keys: string[] = Object.keys(err.data);
      if (keys.length) {
        if (_.isArray(err.data[keys[0]])) {
          msg = err.data[keys[0]][0];
        } else if (typeof err.data[keys[0]] === "string") {
          msg = err.data[keys[0]];
        } else {
          msg = def;
        }
      } else {
        msg = def;
      }
    } else {
      msg = def;
    }

    return msg;
  }

  public clonePageContent(
    content: IPageContent,
    removeStyles?: boolean
  ): IPageContent {
    let copy: IPageContent = [];
    let deltaStyle: IPageCellStyle = {};
    let borders: string[] = ["t", "r", "b", "l"];

    for (let i: number = 0; i < content.length; i++) {
      copy[i] = [];

      for (let j: number = 0; j < content[i].length; j++) {
        let cell: IPageContentCell = { ...content[i][j] };
        let style: any = {};
        let hasBorder: string[] = [];
        borders.forEach((k) => {
          if (cell.style[`${k}bs`] != "none") hasBorder.push(k);
        });
        // let rb =  (cell.style.rbs != 'none')
        // let lb =  (cell.style.lbs != 'none')
        for (let k in cell.style) {
          let cont = false;
          if (cell.style[k] === deltaStyle[k]) cont = true;
          // borders.forEach(b => {
          //   if (cont && k.indexOf(`${b}b`) === 0 && hasBorder.includes(b)) cont = false;
          // })
          // if (cont && k.indexOf('rb') === 0 && rb) cont = false;
          // if (cont && k.indexOf('lb') === 0 && lb) cont = false;
          if (cont) continue;
          style[k] = cell.style[k];
        }
        deltaStyle = { ...cell.style };
        cell.style = removeStyles ? {} : style;
        if (cell.index) delete cell.index;
        // if (cell.formatting) delete cell.formatting;
        copy[i].push(cell);
      }
    }

    return copy;
  }

  public comparePageContent(
    currentContent: any,
    newContent: any,
    ignore_styles: boolean = false
  ): IUtilsDiff | false {
    if (!currentContent || !newContent) {
      return false;
    }
    let diffContent: any = [];
    let sizeDiff: boolean = false;
    // compare
    for (let i: number = 0; i < newContent.length; i++) {
      for (let k: number = 0; k < newContent[i].length; k++) {
        let diff: boolean = false;
        if (currentContent[i] && currentContent[i][k]) {
          // check for value changes
          if (newContent[i][k].value !== currentContent[i][k].value) {
            diff = true;
          }
          // check for style changes
          if (!diff && !ignore_styles) {
            for (let attr in newContent[i][k].style) {
              if (
                newContent[i][k].style[attr] !==
                currentContent[i][k].style[attr]
              ) {
                diff = true;
                if (["width", "height"].indexOf(attr) > -1) sizeDiff = true;
                break;
              }
            }
          }
        } else {
          diff = true;
        }
        if (diff) {
          if (!diffContent[i]) {
            diffContent[i] = [];
          }
          diffContent[i][k] = newContent[i][k];
        }
      }
    }
    if (
      diffContent.length ||
      newContent.length < currentContent.length ||
      newContent[0].length < currentContent[0].length
    ) {
      let rowDiff: number = newContent.length - currentContent.length;
      let colDiff: number = newContent[0].length - currentContent[0].length;
      return {
        content: newContent,
        content_diff: diffContent,
        colDiff: colDiff,
        rowDiff: rowDiff,
        sizeDiff,
      };
    }
    return false;
  }

  public mergePageContent(
    _original: any,
    _deltas?: any,
    cellStyle?: IPageCellStyle | IPageColumnStyles
  ): any {
    let current: IPageContent = [];
    // let style: IPageCellStyle = merge({}, _defaultStyles);
    let style = cellStyle;
    let colWidths: any[] = [];
    for (let rowIndex = 0; rowIndex < _original.length; rowIndex++) {
      let row = _original[rowIndex];
      current[rowIndex] = [];
      for (let colIndex = 0; colIndex < row.length; colIndex++) {
        if (cellStyle instanceof Array && cellStyle.length === 2) {
          if (!rowIndex && cellStyle[0][colIndex])
            style = cellStyle[0][colIndex];
          else if (cellStyle[1][colIndex]) style = cellStyle[1][colIndex];
        }
        let col: IPageContentCell;
        if (_deltas && _deltas[rowIndex] && _deltas[rowIndex][colIndex]) {
          col = _deltas[rowIndex][colIndex];
          // col.style = merge({}, style, col.style);
          col.style = { ...row[colIndex].style, ...col.style };
          style = { ...style, ...row[colIndex].style };
        } else {
          col = { ...row[colIndex] };
          col.style = { ...style, ...col.style };
          delete col.dirty;
          if (col.checked !== undefined) delete col.checked;
          style = { ...col.style };
        }
        if (!rowIndex && col.style) {
          colWidths[colIndex] = col.style.width;
        }
        if (col.style) col.style.width = colWidths[colIndex];
        // col.formatting = {};
        col.index = {
          row: rowIndex,
          col: colIndex,
        };
        current[rowIndex].push(col);
      }
    }
    return current;
  }

  public mergePageContent2(original: IPageContent, deltas: IPageContent): any {
    let content: IPageContent = JSON.parse(JSON.stringify(original));
    deltas.forEach((row, rowIndex: number) => {
      if (!row) return;
      row.forEach((col: IPageContentCell, colIndex: number) => {
        if (!col) return;
        let cell = helpers.getNested(content, `${rowIndex}.${colIndex}`);
        if (!cell) return;
        content[rowIndex][colIndex] = col;
      });
    });
    return content;
  }

  public getDefaultCellStyle(): IPageCellStyle {
    return {
      "background-color": "FFFFFF",
      color: "000000",
      "font-family": "Arial",
      "font-size": "11pt",
      "font-style": "normal",
      "font-weight": "normal",
      height: "20px",
      "number-format": "",
      "text-align": "left",
      "text-wrap": "normal",
      "vertical-align": "middle",
      width: "64px",
      tbs: "none",
      rbs: "none",
      bbs: "none",
      lbs: "none",
      tbc: "000000",
      rbc: "000000",
      bbc: "000000",
      lbc: "000000",
      tbw: "none",
      rbw: "none",
      bbw: "none",
      lbw: "none",
    };
  }

  public cellStyleMap(): any {
    return [
      {
        short: "bc",
        long: "background-color",
      },
      {
        short: "c",
        long: "color",
      },
      {
        short: "ff",
        long: "font-family",
      },
      {
        short: "fs",
        long: "font-size",
      },
      {
        short: "fw",
        long: "font-weight",
      },
      {
        short: "h",
        long: "height",
      },
      {
        short: "f",
        long: "number-format",
      },
      {
        short: "ta",
        long: "text-align",
        values: true,
      },
      {
        short: "tw",
        long: "text-wrap",
      },
      {
        short: "va",
        long: "vertical-align",
        values: true,
      },
      {
        short: "w",
        long: "width",
      },
      {
        short: "tbs",
        long: "tbs",
        values: true,
      },
      {
        short: "rbs",
        long: "rbs",
        values: true,
      },
      {
        short: "bbs",
        long: "bbs",
        values: true,
      },
      {
        short: "lbs",
        long: "lbs",
        values: true,
      },
      {
        short: "tbc",
        long: "tbc",
        values: true,
      },
      {
        short: "rbc",
        long: "rbc",
        values: true,
      },
      {
        short: "bbc",
        long: "bbc",
        values: true,
      },
      {
        short: "lbc",
        long: "lbc",
        values: true,
      },
      {
        short: "tbw",
        long: "tbw",
        values: true,
      },
      {
        short: "rbw",
        long: "rbw",
        values: true,
      },
      {
        short: "bbw",
        long: "bbw",
        values: true,
      },
      {
        short: "lbw",
        long: "lbw",
        values: true,
      },
    ];
  }
  public cellStyleValueMap(): any {
    return [
      { short: "s", long: "solid" },
      { short: "d", long: "dot" },
      { short: "a", long: "dash" },
      { short: "ad", long: "dashdot" },
      { short: "add", long: "dashdotdot" },
      { short: "sad", long: "slantdashdot" },
      { short: "db", long: "double" },
      { short: "h", long: "hairline" },
      { short: "t", long: "thin" },
      { short: "k", long: "thick" },
      { short: "m", long: "medium" },
      { short: "n", long: "normal" },
      { short: "b", long: "bold" },
      { short: "i", long: "italic" },
      { short: "T", long: "top" },
      { short: "M", long: "middle" },
      { short: "B", long: "bottom" },
      { short: "C", long: "center" },
      { short: "L", long: "left" },
      { short: "R", long: "right" },
      { short: "J", long: "justify" },
    ];
  }

  public transformStyle(style: any, l2s?: boolean): any {
    let newStyle: any = {};

    Object.keys(style).forEach((key) => {
      let map = this.cellStyleMap().find((m: any) => {
        if (l2s) return m.long == key;
        return m.short == key;
      });
      if (!map) return;
      let v = style[key];
      if (map.values) {
        let value = this.cellStyleValueMap().find((m: any) => {
          if (l2s) return m.long == style[key];
          return m.short == style[key];
        });
        if (value) v = value.long;
      }
      newStyle[l2s ? map.short : map.long] = v;
    });
    return newStyle;
  }

  public transformContent(rawContent: IPageContent2): IPageContent {
    let content: IPageContent = [];
    let style = this.getDefaultCellStyle();
    rawContent.values.forEach((row: any, rowIndex) => {
      content[rowIndex] = [];
      row.forEach((value: any, col: number) => {
        // formatted value
        let formatted_value = helpers.getNested(
          rawContent.formatted_values,
          `${rowIndex}.${col}`
        );
        if (formatted_value === undefined || formatted_value === null)
          formatted_value = value;

        // styles
        let index: number = helpers.getNested(
          rawContent.cell_styles,
          `${rowIndex}.${col}`
        );
        let uStyle: any = helpers.getNested(
          rawContent.unique_styles,
          `${index}`
        );
        if (uStyle) {
          // style = { ...style, ...this.transformStyle(uStyle) };
          style = { ...style, ...uStyle };
        }
        content[rowIndex].push({
          value,
          formatted_value,
          style,
          index: {
            row: rowIndex,
            col,
          },
        });
      });
    });
    // custom fields
    if (rawContent.custom_fields) {
      Object.keys(rawContent.custom_fields).forEach((key) => {
        let values: any = rawContent.custom_fields![key];
        if (!(values instanceof Array)) return;
        values.forEach((value: any) => {
          let cell: IPageContentCell = helpers.getNested(
            content,
            `${value.row}.${value.col}`
          );
          if (!cell) return;
          cell[key] = value;
        });
      });
    }
    // optional fields
    if (rawContent.optional_fields) {
      Object.keys(rawContent.optional_fields).forEach((key) => {
        let values: any = rawContent.optional_fields![key];
        if (!(values instanceof Array)) return;
        values.forEach((value: any) => {
          let cell: IPageContentCell = helpers.getNested(
            content,
            `${value.row}.${value.col}`
          );
          if (!cell) return;
          cell[key] = value;
        });
      });
    }
    // params
    if (
      rawContent.parameter_mapping &&
      rawContent.parameter_mapping instanceof Array
    ) {
      rawContent.parameter_mapping.forEach((map: IPageContent2ParamMap) => {
        let cell: IPageContentCell = helpers.getNested(
          content,
          `${map.row}.${map.col}`
        );
        if (!cell) return;
        cell.map = map;
      });
    }
    // let links = helpers.getNested(rawContent, 'optional_fields.link') || [];
    // links.forEach((link: IPageContent2OptionalFieldsLink) => {
    //   let cell: IPageContentCell = helpers.getNested(content, `${link.row}.${link.col}`);
    //   if (!cell) return;
    //   cell.link = link;
    // });
    return content;
  }
}

export default Utils;
