import * as uuid from "uuid";
import { IGridContentCellPosition } from "./Grid4/GridCell";
import { IPageContentCellIndex } from "./Page/Content";

export interface IButtonTask {
  actions: any;
  conditions: string[];
}

export interface ICellRange {
  from: IGridContentCellPosition;
  to: IGridContentCellPosition;
}

export interface IHelpers {
  isTouch(): boolean;
  isTouchDevice(): boolean;

  // cell reference helpers
  getRefIndex(str: string, obj?: boolean): any;
  cellRange(str: string, rows: number, cols: number): ICellRange;
  getCellsReference(
    from: IPageContentCellIndex,
    to: IPageContentCellIndex,
    headingSelected?: string
  ): string;
  toColumnName(num: number): string;
  expandCellsReference(
    str: string,
    direction: string,
    rowLen: number,
    colLen: number
  ): string;

  getUuid(): string;
  getNextValueInArray(value: string | number, arr: string[]): string;
  isNumber(value: any): boolean;
  convertToNumber(value: string): number | null;
  validHex(hex: string): boolean;
  rgbToHex(rgb: string): string;
  componentToHex(c: any): string;
  addEvent(
    element: HTMLElement | Document | Window,
    eventName: string,
    func: any
  ): void;
  removeEvent(
    element: HTMLElement | Document | Window,
    eventName: string,
    func: any
  ): void;
  capitalizeFirstLetter(str: string): string;
  createSlug(str: string): string;
  getContrastYIQ(hexcolor: string): string;
  parseExcelDate(excelTimestamp: number): any;
  parseDateExcel(excelTimestamp: number): any;
  toExcelDate(timestamp: number): any;
  isValidEmail(str: string): boolean;
  convertTime(timestamp: any): string;
  serializeObject(obj: any): string;
  safeTitle(str: string, replaceWith?: string): string;
  safeXmlChars(str: string): string;
  isTarget(el: any, target: any): any;
  formatNumber(n: number, precision?: number): string;
  sentenceCase(str: string): string;
  tableFromArray(arr: any): string;
  getURLParameter(name: string, url: string): string | null;
  arrayDiff(a1: any, a2: any): any;
  getSymphonyStyles(): string[];
  factorsOf(num: number): number[];
  getObjectKeys(obj: any): string[];
  isDarkModeEnabled(): boolean;
  parseURL(str: string): any;
  arrayMove(arr: any, old_index: number, new_index: number): any;
  generatePassword(length: number): string;
  chunkArray(arr: any, n: any): any;
  getNested(obj: any, path: string): any;
  safeSymphonyUrl(str: string): string;
  getScrollbarWidth(parent?: any): number;
  toNumberSuffix(n: number): string;
}

class Helpers implements IHelpers {
  public letters: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  constructor() {
    return;
  }

  public getUuid(): string {
    return uuid.v4();
  }

  public getNextValueInArray(value: string, arr: string[]): string {
    if (!arr || !arr.length) {
      return value;
    }
    let n: string = value;
    for (let i: number = 0; i < arr.length; i++) {
      if (n !== arr[i]) {
        continue;
      }
      if (arr[i + 1] === undefined) {
        n = arr[0];
      } else {
        n = arr[i + 1];
      }
      break;
    }
    if (n === value) {
      n = arr[0];
    }
    return n;
  }

  public isNumber(value: any): boolean {
    return !/^[0-9\.\-]+$/.test(value) || isNaN(value) ? false : true;
  }

  public convertToNumber(value: any): number | null {
    // if (!isNaN(value)) return value;

    value = `${value}`;

    const regex = /[0-9\.\-]+/gm;
    const str = `${value}`;
    let m;
    let matches: string[] = [];

    while ((m = regex.exec(str)) !== null) {
      // This is necessary to avoid infinite loops with zero-width matches
      if (m.index === regex.lastIndex) {
        regex.lastIndex++;
      }

      // The result can be accessed through the `m`-variable.
      m.forEach((match, groupIndex) => {
        matches.push(match);
      });
    }

    if (!matches.length) return null;

    let segments: string[] = value.split(matches[0]);
    let suffix: string = "";
    segments.forEach((segment, index) => {
      if (segment === "") return;
      suffix = segment;
    });

    let number = parseFloat(matches[0]);

    switch (suffix) {
      case "k":
      case "K":
        number *= 1000;
        break;
      case "m":
      case "M":
      case "mm":
      case "MM":
        number *= 1000000;
        break;
      default:
        break;
    }

    return number;
  }

  public getRefIndex(str: string, obj?: boolean): any {
    str = str.toUpperCase();

    const regex = /^[a-zA-Z]+/gm;
    let m;
    // let col = -1;
    let count = 0;
    let letters = "";

    while ((m = regex.exec(str)) !== null) {
      if (m.index === regex.lastIndex) {
        regex.lastIndex++;
      }
      m.forEach((match) => {
        letters = match;
      });
    }
    let i;
    let j;
    let col = 0;
    for (i = 0, j = letters.length - 1; i < letters.length; i += 1, j -= 1) {
      col +=
        Math.pow(this.letters.length, j) *
        (this.letters.indexOf(letters[i]) + 1);
    }
    col--;

    let numbers = str.match(/\d/g);
    let row = numbers ? parseFloat(numbers.join("")) - 1 : -1;

    return obj ? { row, col } : [row, col];
  }

  public toColumnName(num: number): string {
    let name = "";
    for (let a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) {
      name = String.fromCharCode((num % b) / a + 65) + name;
    }
    return name;
  }

  public cellRange(str: string, rows: number, cols: number): ICellRange {
    let [from, to] = str.split(":");

    let fromRow = 0,
      fromCol = 0,
      toRow = 0,
      toCol = 0;

    let isFromNumber = this.isNumber(from);
    let fromCell = this.getRefIndex(from, true);

    if (to) {
      let isToNumber = this.isNumber(to);
      let toCell = this.getRefIndex(to, true);

      // 1:? - multiple rows
      if (isFromNumber) {
        fromRow = parseInt(from) - 1;
        fromCol = 0;
      } else {
        // A1:? - cell range
        // A:? - multiple columns
        fromCol = fromCell.col;
        fromRow = fromCell.row;
        if (fromCell.row < 0) {
          fromRow = 0;
        }
      }

      // ?:2, ?:-1 - to end of row
      if (isToNumber) {
        toRow = parseInt(to) < 0 ? rows - 1 : parseInt(to) - 1;
        toCol = cols - 1;
        // ?:B, ?:B3
      } else {
        toRow = toCell.row < 0 ? rows - 1 : toCell.row;
        toCol = toCell.col < 0 ? cols - 1 : toCell.col;
      }
    } else {
      // 1 - single row
      if (isFromNumber) {
        fromRow = parseInt(from) - 1;
        toRow = fromRow;
        fromCol = 0;
        toCol = cols - 1;
      } else {
        // A1 - single cell
        // A - single column
        fromCol = fromCell.col;
        fromRow = fromCell.row;
        toCol = fromCol;
        if (fromCell.row < 0) {
          fromRow = 0;
          toRow = rows - 1;
        } else {
          toRow = fromRow;
        }
      }
    }

    return {
      from: { row: fromRow, col: fromCol },
      to: { row: toRow, col: toCol },
    };
  }

  public validHex(hex: string): boolean {
    return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
  }

  public rgbToHex(rgb: string): string {
    rgb = rgb.replace("rgba(", "").replace("rgb(", "").replace(")", "");
    let parts: string[] = rgb.split(",");
    if (parts.length === 4) {
      if (parts[3].trim() === "0") {
        return "FFFFFF";
      }
    }
    return (
      this.componentToHex(parseInt(parts[0], 10)) +
      this.componentToHex(parseInt(parts[1], 10)) +
      this.componentToHex(parseInt(parts[2], 10))
    );
  }

  public componentToHex(c: any): string {
    let hex: string = c.toString(16);
    return hex.length === 1 ? "0" + hex : hex;
  }

  public addEvent(
    element: HTMLElement | Document | Window,
    eventName: string,
    func: any
  ): void {
    if (!element) {
      return;
    }
    element.addEventListener(eventName, func, false);
    if (eventName === "click") {
      element.addEventListener("touchstart", func, false);
    }
  }

  public capitalizeFirstLetter(str: string): string {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }

  /**
   * Remove event from element
   *
   * @param element
   * @param eventName
   * @param func
   * @returns {any}
   */
  public removeEvent(
    element: HTMLElement | Document | Window,
    eventName: string,
    func: any
  ): void {
    element.removeEventListener(eventName, func, false);
    if (eventName === "click") {
      element.removeEventListener("touchstart", func, false);
    }
  }

  public isTouch(): boolean {
    return "ontouchstart" in document.documentElement;
  }

  public isTouchDevice(): boolean {
    return (
      /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
        navigator.userAgent
      ) ||
      /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
        navigator.userAgent.substr(0, 4)
      )
    );
  }

  public clickEvent(): string {
    return this.isTouch() ? "touchstart" : "click";
  }

  public openWindow(
    link: string,
    target: string = "_blank",
    params: { [s: string]: string } = {}
  ): Window | null {
    let paramsStr: string = "";
    for (let key in params) {
      if (!params.hasOwnProperty(key)) {
        continue;
      }

      paramsStr += `${key}=${params[key]},`;
    }
    paramsStr = paramsStr.substring(0, paramsStr.length - 1);

    return window.open(link, target, paramsStr);
  }

  public createSlug(str: string): string {
    return str.split(" ").join("_");
  }

  public getScrollbarWidth(parent?: any): number {
    var outer = document.createElement("div");
    outer.style.visibility = "hidden";
    outer.style.width = "100px";
    outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps

    if (parent) parent.appendChild(outer);
    else document.body.appendChild(outer);

    var widthNoScroll = outer.offsetWidth;
    // force scrollbars
    outer.style.overflow = "scroll";

    // add innerdiv
    var inner = document.createElement("div");
    inner.style.width = "100%";
    outer.appendChild(inner);

    var widthWithScroll = inner.offsetWidth;

    // remove divs
    if (outer.parentNode) {
      outer.parentNode.removeChild(outer);
    }

    return widthNoScroll - widthWithScroll;
  }

  public parseExcelDate(excelTimestamp: number): any {
    return this.parseDateExcel(excelTimestamp);
  }
  public parseDateExcel(excelTimestamp: number): any {
    const secondsInDay = 24 * 60 * 60;
    const excelEpoch = new Date(1899, 11, 31);
    const excelEpochAsUnixTimestamp = excelEpoch.getTime();
    const missingLeapYearDay = secondsInDay * 1000;
    const delta = excelEpochAsUnixTimestamp - missingLeapYearDay;
    const excelTimestampAsUnixTimestamp = excelTimestamp * secondsInDay * 1000;
    const parsed = excelTimestampAsUnixTimestamp + delta;
    return isNaN(parsed) ? null : parsed;
  }

  public toExcelDate(timestamp: number): any {
    const secondsInDay = 24 * 60 * 60;
    const excelEpoch = new Date(1899, 11, 31);
    const excelEpochAsUnixTimestamp = excelEpoch.getTime();
    const missingLeapYearDay = secondsInDay * 1000;
    const delta = excelEpochAsUnixTimestamp - missingLeapYearDay;
    // const excelTimestampAsUnixTimestamp = timestamp
    const parsed = (timestamp - delta) / secondsInDay / 1000;
    return isNaN(parsed) ? null : parsed;
  }

  public getContrastYIQ(hexcolor: string): string {
    hexcolor = hexcolor.replace("#", "");
    let r: number = parseInt(hexcolor.substr(0, 2), 16);
    let g: number = parseInt(hexcolor.substr(2, 2), 16);
    let b: number = parseInt(hexcolor.substr(4, 2), 16);
    let yiq: number = (r * 299 + g * 587 + b * 114) / 1000;
    return yiq >= 128 ? "light" : "dark";
  }

  public convertTime(timestamp: any): string {
    if (typeof timestamp === "string") {
      timestamp = new Date(timestamp);
    }
    let hours = timestamp.getHours();
    if (hours < 10) {
      hours = `0${hours}`;
    }
    let minutes = timestamp.getMinutes();
    if (minutes < 10) {
      minutes = `0${minutes}`;
    }
    let seconds = timestamp.getSeconds();
    if (seconds < 10) {
      seconds = `0${seconds}`;
    }
    return `${hours}:${minutes}:${seconds}`;
  }

  public isValidEmail(str: string): boolean {
    let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(str);
  }
  public serializeObject(obj: any): string {
    let str = "";
    for (var key in obj) {
      if (str != "") {
        str += "&";
      }
      str += key + "=" + encodeURI(obj[key]);
    }
    return str;
  }
  public getCellsReference(
    from: IGridContentCellPosition,
    to: IGridContentCellPosition,
    headingSelected?: string
  ): string {
    let colFrom = this.toColumnName(from.col + 1);
    let rowFrom = from.row + 1;
    let colTo = this.toColumnName(to.col + 1);
    let rowTo = to.row + 1;
    if (headingSelected === "all") {
      return "1:-1";
    } else if (headingSelected === "col") {
      if (colFrom === colTo) return colFrom;
      return `${colFrom}:${colTo}`;
    } else if (headingSelected === "row") {
      if (rowFrom === rowTo) return `${rowFrom}`;
      return `${rowFrom}:${rowTo}`;
    }
    if (colFrom === colTo && rowFrom === rowTo) {
      return `${colFrom}${rowFrom}`;
    }
    if (!rowTo) {
      return `${colFrom}${rowFrom}:${colTo}`;
    }
    return `${colFrom}${rowFrom}:${colTo}${rowTo}`;
  }
  public safeTitle(str: string, replaceWith: string = "_"): string {
    return str.replace(/[^a-zA-Z0-9_\.\-]/g, replaceWith);
  }
  public safeXmlChars(str: string): string {
    const expressions = [
      /<div(.*?)<\/div>/gm,
      /<table(.*?)<\/table>/gm,
      /<[\w](.*?)\/>/gm,
    ];

    let m;
    let ignore: string[] = [];

    expressions.forEach((regex) => {
      while ((m = regex.exec(str)) !== null) {
        if (m.index === regex.lastIndex) {
          regex.lastIndex++;
        }
        ignore.push(m[0]);
      }
    });

    ignore.forEach((s, i) => {
      str = str.replace(s, `[$${i}]`);
    });
    str = str
      // .replace(/\<br \/\>/g, '[BR]')
      .replace(/&/g, "&amp;")
      .replace(/>/g, "&gt;")
      .replace(/</g, "&lt;")
      .replace(/'/g, "&apos;")
      .replace(/"/g, "&quot;");
    // .replace(/\[BR\]/g, '<br />');

    ignore.forEach((s, i) => {
      str = str.replace(`[$${i}]`, s);
    });

    return str;
  }
  public isTarget(el: any, target: any): any {
    let clickedEl = target;
    while (clickedEl && clickedEl !== el) {
      clickedEl = clickedEl.parentNode;
    }
    return clickedEl === el;
  }
  public formatNumber(n: number, precision: number = 2): string {
    let nStr = n.toFixed(precision);
    nStr += "";
    let x = nStr.split(".");
    let x1 = x[0];
    let x2 = x.length > 1 ? "." + x[1] : "";
    let rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
      x1 = x1.replace(rgx, "$1" + "," + "$2");
    }
    return x1 + x2;
  }
  public sentenceCase(str: string): string {
    if (str === null || str === "") return str;
    else str = str.toString();

    return str.replace(/\w\S*/g, function (txt) {
      return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });
  }
  public tableFromArray(arr: any = []): string {
    let table = `<div class="excel-table"><table>`;
    // columns
    arr.forEach((row: any[], rowIndex: number) => {
      if (!rowIndex) {
        table += `<thead>`;
      } else if (rowIndex === 1) {
        table += `<tbody>`;
      }
      table += `<tr>`;
      let t: string = rowIndex ? "td" : "th";
      row.forEach((value) => {
        table += `<${t}>${value}</${t}>`;
      });
      table += `</tr>`;
      if (!rowIndex) {
        table += `</thead>`;
      } else if (rowIndex === arr.length - 1) {
        table += `</tbody>`;
      }
    });
    // values
    table += `</table></div>`;
    return table;
  }
  public getURLParameter(name: string, url: string): string | null {
    let reg: RegExpExecArray | null = new RegExp(
      "[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)"
    ).exec(url || location.search);
    if (!reg) return null;
    return decodeURIComponent(reg[1].replace(/\+/g, "%20")) || null;
  }
  public arrayDiff(a1: any, a2: any): any {
    return a1.filter(function (i: any) {
      return a2.indexOf(i) < 0;
    });
  }
  public expandCellsReference(
    str: string,
    direction: string,
    rowLen: number,
    colLen: number
  ): string {
    let index = this.cellRange(str, rowLen, colLen);
    let reference = this.getCellsReference(index.from, index.from);
    const colName = this.toColumnName(index.to.col + 1);
    if (direction === "down") {
      reference += `:${colName}`;
    } else {
      reference += `:${index.to.row + 1}`;
    }
    return reference;
  }
  public downloadFile(contentType: string, name: string, data: any) {
    let types: any = {
      json: "text/json",
      csv: "text/csv",
    };

    for (let k in types) {
      if (!types.hasOwnProperty(k)) {
        continue;
      }

      if (contentType === k) {
        contentType = types[k];
        break;
      }
    }

    let a = document.createElement("a");
    let blob = new Blob([data], { type: contentType });
    a.href = URL.createObjectURL(blob);
    a.download = name;

    let e = document.createEvent("MouseEvents");
    e.initMouseEvent(
      "click",
      true,
      true,
      window,
      1,
      0,
      0,
      0,
      0,
      false,
      false,
      false,
      false,
      0,
      null
    );
    a.dispatchEvent(e);
  }

  public factorsOf(num: number): number[] {
    let half = Math.floor(num / 2), // Ensures a whole number <= num.
      i,
      j;
    let factors = [];
    factors.push(1);

    // Determine our increment value for the loop and starting point.
    num % 2 === 0 ? ((i = 2), (j = 1)) : ((i = 3), (j = 2));

    for (i; i <= half; i += j) {
      if (num % i === 0) {
        factors.push(i);
      }
    }

    factors.push(num); // Always include the original number.

    return factors;
  }

  public getSymphonyStyles(): string[] {
    return [
      "background",
      "background-attachment",
      "background-blend-mode",
      "background-clip",
      "background-color",
      "background-image",
      "background-position",
      "background-repeat",
      "background-size",
      "border",
      "border-bottom",
      "border-bottom-color",
      "border-bottom-left-radius",
      "border-bottom-right-radius",
      "border-bottom-style",
      "border-bottom-width",
      "border-collapse",
      "border-color",
      "border-image",
      "border-image-outset",
      "border-image-repeat",
      "border-image-slice",
      "border-image-source",
      "border-image-width",
      "border-left",
      "border-left-color",
      "border-left-style",
      "border-left-width",
      "border-radius",
      "border-right",
      "border-right-color",
      "border-right-style",
      "border-right-width",
      "border-spacing",
      "border-style",
      "border-top",
      "border-top-color",
      "border-top-left-radius",
      "border-top-right-radius",
      "border-top-style",
      "border-top-width",
      "border-width",
      "box-shadow",
      "box-sizing",
      "caption-side",
      "clear",
      "color",
      "content",
      "counter-increment",
      "counter-reset",
      "display",
      "empty-cells",
      "font",
      "font-family",
      "font-kerning",
      "font-size",
      "font-size-adjust",
      "font-stretch",
      "font-style",
      "font-variant",
      "font-weight",
      "height",
      "letter-spacing",
      "line-height",
      "list-style",
      "list-style-image",
      "list-style-position",
      "list-style-type",
      "margin",
      "margin-bottom",
      "margin-left",
      "margin-right",
      "margin-top",
      "max-height",
      "max-width",
      "min-height",
      "min-width",
      "opacity",
      "outline",
      "outline-color",
      "outline-offset",
      "outline-style",
      "outline-width",
      "overflow",
      "overflow-x",
      "overflow-y",
      "padding",
      "padding-bottom",
      "padding-left",
      "padding-right",
      "padding-top",
      "table-layout",
      "text-align",
      "text-align-last",
      "text-decoration",
      "text-decoration-color",
      "text-decoration-line",
      "text-decoration-style",
      "text-indent",
      "text-justify",
      "text-overflow",
      "text-shadow",
      "text-transform",
      "visibility",
      "white-space",
      "width",
      "word-break",
      "word-spacing",
      "word-wrap",
    ];
  }

  /**
   * Returns array of keys in an object
   *
   * @param obj
   * @returns {Array}
   */
  public getObjectKeys(obj: any): string[] {
    var keys = [];
    for (var k in obj) {
      if (!obj.hasOwnProperty(k)) {
        continue;
      }

      keys.push(k);
    }

    return keys;
  }
  /**
   * Deep copy of object
   *
   * @todo Should be using angular clone..
   *
   * @param o
   * @returns {any}
   */
  public cloneObject(o: any): any {
    const g: any = function (this: any) {
      let gdcc: any = "__getDeepCircularCopy__";
      this.getGdcc = function () {
        return gdcc;
      };
    };
    let constants: any = new g();

    if (o !== Object(o)) {
      return o; // primitive value
    }

    let set: any = constants.getGdcc() in o,
      cache: any = o[constants.getGdcc()],
      result: any;
    if (set && typeof cache == "function") {
      return cache();
    }
    // else
    o[constants.getGdcc()] = function () {
      return result;
    }; // overwrite
    if (o instanceof Array) {
      result = [];
      for (let i: number = 0; i < o.length; i++) {
        result[i] = this.cloneObject(o[i]);
      }
    } else {
      result = {};
      for (let prop in o) {
        if (!o.hasOwnProperty(prop)) {
          continue;
        }

        if (prop != constants.getGdcc()) {
          result[prop] = this.cloneObject(o[prop]);
        } else if (set) {
          result[prop] = this.cloneObject(cache);
        }
      }
    }

    if (set) {
      o[constants.getGdcc()] = cache; // reset
    } else {
      delete o[constants.getGdcc()]; // unset again
    }

    return result;
  }

  public isDarkModeEnabled(): boolean {
    return (
      window.matchMedia &&
      window.matchMedia("(prefers-color-scheme: dark)").matches
    );
  }

  public parseURL(str: string) {
    var pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;

    const valid = pattern.test(str);

    if (!valid) return false;

    let parser = document.createElement("a");
    parser.href = str;
    const url = {
      protocol: parser.protocol,
      hostname: parser.hostname,
      port: parser.port,
      pathname: parser.pathname,
      search: parser.search,
      hash: parser.hash,
      host: parser.host,
      url: str,
    };
    // parser = undefined;
    return url;
  }

  public arrayMove(arr: any, old_index: number, new_index: number) {
    if (new_index >= arr.length) {
      var k = new_index - arr.length + 1;
      while (k--) {
        arr.push(undefined);
      }
    }
    arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
    return arr; // for testing
  }

  public generatePassword(len: number): string {
    let length = len ? len : 10;
    let string = "abcdefghijklmnopqrstuvwxyz"; //to upper
    let numeric = "0123456789";
    let punctuation = "!@#$%^&*()_+~`|}{[]:;?><,./-=";
    let password = "";
    let character = "";
    // let crunch = true;
    while (password.length < length) {
      let entity1 = Math.ceil(string.length * Math.random() * Math.random());
      let entity2 = Math.ceil(numeric.length * Math.random() * Math.random());
      let entity3 = Math.ceil(
        punctuation.length * Math.random() * Math.random()
      );
      let hold = string.charAt(entity1);
      hold = password.length % 2 == 0 ? hold.toUpperCase() : hold;
      character += hold;
      character += numeric.charAt(entity2);
      character += punctuation.charAt(entity3);
      password = character;
    }
    password = password
      .split("")
      .sort(function () {
        return 0.5 - Math.random();
      })
      .join("");
    return password.substr(0, len);
  }
  public chunkArray(arr: any, n: any): any {
    return Array.from(Array(Math.ceil(arr.length / n)), (_, i) =>
      arr.slice(i * n, i * n + n)
    );
  }
  public getNested(obj: any, path: string): any {
    return path
      .split(".")
      .reduce((obj: any, level: any) => obj && obj[level], obj);
  }
  public safeSymphonyUrl(str: string): string {
    return str.replace(/\//gi, "_").replace(/\+/g, "-").replace(/\=/g, "");
  }
  public toNumberSuffix(n: number): string {
    if (Math.abs(n / 1000000) >= 1) {
      return `${n / 1000000}MM`;
    }
    if (Math.abs(n / 1000) >= 1) {
      return `${n / 1000}k`;
    }
    return `${n}`;
  }
}

export default Helpers;
