// From https://stackoverflow.com/a/27709336/3850564

import {ɵisListLikeIterable} from '@angular/core';
import {Node} from './interfaces';

export function getGradientColor(startColor: string, endColor: string, percent: number) {
  // strip the leading # if it's there
  startColor = startColor.replace(/^\s*#|\s*$/g, '');
  endColor = endColor.replace(/^\s*#|\s*$/g, '');

  // convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
  if (startColor.length === 3) {
    startColor = startColor.replace(/(.)/g, '$1$1');
  }

  if (endColor.length === 3) {
    endColor = endColor.replace(/(.)/g, '$1$1');
  }

  // get colors
  const startRed = parseInt(startColor.substr(0, 2), 16);
  const startGreen = parseInt(startColor.substr(2, 2), 16);
  const startBlue = parseInt(startColor.substr(4, 2), 16);

  const endRed = parseInt(endColor.substr(0, 2), 16);
  const endGreen = parseInt(endColor.substr(2, 2), 16);
  const endBlue = parseInt(endColor.substr(4, 2), 16);

  // calculate new color
  const diffRed = endRed - startRed;
  const diffGreen = endGreen - startGreen;
  const diffBlue = endBlue - startBlue;

  let diffRedStr = `${((diffRed * percent) + startRed).toString(16).split('.')[0]}`;
  let diffGreenStr = `${((diffGreen * percent) + startGreen).toString(16).split('.')[0]}`;
  let diffBlueStr = `${((diffBlue * percent) + startBlue).toString(16).split('.')[0]}`;

  // ensure 2 digits by color
  if (diffRedStr.length === 1) {
    diffRedStr = '0' + diffRedStr;
  }
  if (diffGreenStr.length === 1) {
    diffGreenStr = '0' + diffGreenStr;
  }
  if (diffBlueStr.length === 1) {
    diffBlueStr = '0' + diffBlueStr;
  }

  return '#' + diffRedStr + diffGreenStr + diffBlueStr;
}

export function removeUnderscoreFromKeys(obj) {
  const result = {};
  Object.keys(obj).forEach(x => {
    const y = x.replace('_', '');
    result[y] = obj[x];
  });
  return result;
}

// https://gist.github.com/whitlockjc/9363016
function trim(str) {
  return str.replace(/^\s+|\s+$/gm, '');
}

export function rgbaToHex(rgba) {
  const inParts = rgba.substring(rgba.indexOf('(')).split(','),
    r = parseInt(trim(inParts[0].substring(1)), 10),
    g = parseInt(trim(inParts[1]), 10),
    b = parseInt(trim(inParts[2]), 10),
    a: number = parseFloat(parseFloat(trim(inParts[3].substring(0, inParts[3].length - 1))).toFixed(2));
  const outParts = [
    r.toString(16),
    g.toString(16),
    b.toString(16),
    Math.round(a * 255).toString(16).substring(0, 2)
  ];

  // Pad single-digit output values
  outParts.forEach(function(part, i) {
    if (part.length === 1) {
      outParts[i] = '0' + part;
    }
  });

  return ('#' + outParts.join(''));
}

// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
function componentToHex(c) {
  const hex = c.toString(16);
  return hex.length === 1 ? '0' + hex : hex;
}

export function rgbToHex(rgb) {
  const inParts = rgb.substring(rgb.indexOf('(')).split(','),
    r = parseInt(trim(inParts[0].substring(1)), 10),
    g = parseInt(trim(inParts[1]), 10),
    b = parseInt(trim(inParts[2]), 10);
  return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b);
}

export function rgbaWithoutAToHex(rgb) {
  const inParts = rgb.substring(rgb.indexOf('(')).split(','),
    r = parseInt(trim(inParts[0].substring(1)), 10),
    g = parseInt(trim(inParts[1]), 10),
    b = parseInt(trim(inParts[2]), 10);
  return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b);
}

// https://stackoverflow.com/questions/1573053/javascript-function-to-convert-color-names-to-hex-codes/47355187#47355187
export function standardizeColor(str) {
  var ctx = document.createElement('canvas').getContext('2d');
  ctx.fillStyle = str;
  return ctx.fillStyle.toString();
}

export function removeDuplicateObjectsFromList(nodes: Node[], attribute: string): Node[] {
  const seenIds = new Set();
  const filteredArray = new Array();
  for (const node of nodes) {
    if (node[attribute] != null && seenIds.has(node[attribute])) {
      continue;
    }
    filteredArray.push(node);
    seenIds.add(node[attribute]);
  }
  return filteredArray;
}

/**
 * Method is use to download file.
 * @param data - Array Buffer data
 * @param type - type of the document.
 */
export function downLoadFile(data: any, type: string, fmt: string) {
  let blob = new Blob([data], {type: type});
  var a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = `drugstone_network_${new Date().getTime()}.${fmt}`;
  a.click();
}

export function RGBAtoRGBwithoutA(rgbaString) {
  const rgbaStringSplit = rgbaString.slice(5, -1).split(',');
  const RGBA = {
    red: rgbaStringSplit[0],
    green: rgbaStringSplit[1],
    blue: rgbaStringSplit[2],
    alpha: rgbaStringSplit[3]
  };
  return `rgb(${RGBA.red},${RGBA.green},${RGBA.blue})`;
}

function hexToRGBA(hex, alpha) {
  let r;
  let g;
  let b;
  if (hex.length < 5) {
    r = parseInt(hex.slice(1, 2) + hex.slice(1, 2), 16);
    g = parseInt(hex.slice(2, 3) + hex.slice(2, 3), 16);
    b = parseInt(hex.slice(3, 4) + hex.slice(3, 4), 16);
  } else {
    r = parseInt(hex.slice(1, 3), 16);
    g = parseInt(hex.slice(3, 5), 16);
    b = parseInt(hex.slice(5, 7), 16);
  }
  if (alpha) {
    return 'rgba(' + (isNaN(r) ? 0 : r) + ', ' + (isNaN(g) ? 0 : g) + ', ' + (isNaN(b) ? 0 : b) + ', ' + alpha + ')';
  } else {
    return 'rgb(' + (isNaN(r) ? 0 : r) + ', ' + isNaN(g) ? 0 : g + ', ' + isNaN(b) ? 0 : b + ')';
  }
}

// https://gist.github.com/JordanDelcros/518396da1c13f75ee057?permalink_comment_id=2075095#gistcomment-2075095
export function blendColors(args: any) {
  let base = [0, 0, 0, 0];
  let mix;
  for (let added of args) {
    added = RGBAtoArray(added);
    if (typeof added[3] === 'undefined') {
      added[3] = 1;
    }
    // check if both alpha channels exist.
    if (base[3] && added[3]) {
      mix = [0, 0, 0, 0];
      // alpha
      mix[3] = 1 - (1 - added[3]) * (1 - base[3]);
      // red
      mix[0] = Math.round((added[0] * added[3] / mix[3]) + (base[0] * base[3] * (1 - added[3]) / mix[3]));
      // green
      mix[1] = Math.round((added[1] * added[3] / mix[3]) + (base[1] * base[3] * (1 - added[3]) / mix[3]));
      // blue
      mix[2] = Math.round((added[2] * added[3] / mix[3]) + (base[2] * base[3] * (1 - added[3]) / mix[3]));

    } else if (added) {
      mix = added;
    } else {
      mix = base;
    }
    base = mix;
  }

  return 'rgba(' + mix[0] + ', ' + mix[1] + ', ' + mix[2] + ', ' + mix[3] + ')';
}

export function RGBAtoArray(rgbaString) {
  const rgbaStringSplit = rgbaString.slice(5, -1).split(',');
  return [
    rgbaStringSplit[0],
    rgbaStringSplit[1],
    rgbaStringSplit[2],
    rgbaStringSplit[3]
  ];
}


export function pieChartContextRenderer({ctx, x, y, state: {selected, hover}, style, label}) {
  ctx.drawPieLabel = function(style, x, y, label) {
    ctx.font = 'normal 12px sans-serif';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = window.getComputedStyle(document.documentElement).getPropertyValue('--drgstn-text-primary');
    ctx.fillText(label, x, y + style.size + 12);
  };

  function startShadow() {
    if (style.shadow) {
      ctx.save();
      ctx.shadowColor = style.shadowColor;
      ctx.shadowOffsetX = style.shadowX;
      ctx.shadowOffsetY = style.shadowY;
      ctx.shadowBlur = 10;
    }
  }

  function colorToHex(color) {
    if (color.startsWith('#')) {
      return color;
    }
    if (color.startsWith('rgba')) {
      return rgbToHex(color);
    }
    if (color.startsWith('rgb')) {
      return rgbToHex(color);
    }
    return null;
  }

  function endShadow() {
    if (style.shadow) {
      // removing shadow application of future fill or stroke calls
      ctx.restore();
    }
  }

  ctx.drawPie = function(style, x, y, state: { selected, hover }) {
    const selection = RGBAtoRGBwithoutA(style.borderColor) !== RGBAtoRGBwithoutA(style.color);
    const bgOpacity = 0.15;
    const fgOpacity = 0.5;
    const lineOpacity = 0.6;
    const fullCircle = 2 * Math.PI;
    const fallbackColor = '#FF0000';
    const colorOrFallback = style.color ? colorToHex(style.color) : fallbackColor;
    let outerBorderColor = style.borderColor;
    if (selection) {
      outerBorderColor = style.borderColor ? rgbaWithoutAToHex(style.borderColor) : fallbackColor;
    }
    if (selected) {
      style.borderColor = style.color;
    }

    ctx.beginPath();
    ctx.arc(x, y, style.size - 1, 0, 2 * Math.PI, false);
    // fill like background of graph panel
    ctx.fillStyle = RGBAtoRGBwithoutA(blendColors([hexToRGBA(window.getComputedStyle(document.documentElement).getPropertyValue('--drgstn-panel'), 1), hexToRGBA(colorOrFallback, bgOpacity)]));
    startShadow();
    ctx.fill();
    endShadow();
    ctx.stroke();

    // draw pi-chart
    ctx.fillStyle = hexToRGBA(colorOrFallback, fgOpacity);

    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.arc(x, y, style.size - 1, 0, style.opacity * fullCircle, false);
    ctx.fill();
    ctx.lineTo(x, y);

    ctx.lineWidth = selected ? 3 : 2;
    if (style.opacity < 1) {
      // avoid the inner line when circle is complete
      ctx.strokeStyle = hexToRGBA(outerBorderColor, lineOpacity);
      ctx.stroke();
    }

    // draw outer circle
    ctx.strokeStyle = outerBorderColor;
    ctx.beginPath();
    ctx.arc(x, y, style.size - (selected ? 0 : 1), 0, fullCircle);
    ctx.stroke();

    // draw inner circle (double circle if selected)
    if (selected || selection) {
      ctx.beginPath();
      ctx.strokeStyle = hexToRGBA(colorOrFallback, lineOpacity);
      ctx.arc(x, y, style.size - 2, 0, fullCircle);
      ctx.stroke();
    }
  };

  return {
    // bellow arrows
    // primarily meant for nodes and the labels inside of their boundaries
    drawNode() {
      ctx.drawPie(style, x, y);
    },
    // above arrows
    // primarily meant for labels outside of the node
    drawExternalLabel() {
      ctx.drawPieLabel(style, x, y, label);
    },
    // node dimensions defined by node drawing
    // nodeDimensions: { width: style.size*2, height: style.size*2 },
  };
}