const clipboard = navigator.clipboard;

let app = new Vue({
  el: '#app',

  data: {
    defaultInputs: [
      "ccc",
      "#db2777aa",
      "rgb(64, 240, 18)",
      "hsla(240, 86.9%, 50.7%, 0.8)",
    ],
    inputBox: "ccc\n#4f46e5\n#db2777aa\n",

    showStatusMessage: false,
    showErrorMessage: false,

    hexRegex: /^\#?[a-f0-9]{3,9}/i,
    rgbRegex: /^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/i,
    rgbaRegex: /^rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(0(\.[0-9])?|1(\.0)?)\s*\)/i,
    hslRegex: /^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}(\.[0-9])?%\s*,\s*\d{1,3}(\.[0-9])?%\s*\)/i,
    hslaRegex: /^hsla\(\s*\d{1,3}\s*,\s*\d{1,3}(\.[0-9])?%\s*,\s*\d{1,3}(\.[0-9])?%\s*,\s*(0(\.[0-9])?|1(\.0)?)\s*\)/i,

    convertedColors: [],
  },

  beforeMount: function () {
    this.inputBox = this.defaultInputs.join("\n");
  },

  mounted: function () {
    this.batchConvert();
  },

  methods: {
    batchConvert: function () {
      this.convertedColors = [];

      // first attempt to split by new-line characters
      let inputArray = this.inputBox.split('\n');
      let count = inputArray.length;

      for (let i = 0; i < count; i++) {
        // check for empty line, non hex(a)/rgb(a)/hsl(a) string
        let inputLine = inputArray[i].toLowerCase().trim();
        if (inputLine.length === 0) {
          continue;
        }

        let converted = {
          error: false,
          raw: inputLine,
          hex: "",
          rgb: "",
          hsl: "",
        };

        if (this.hexRegex.test(inputLine)) {
          let hexString = inputLine;

          // doing this just to add pound symbol for the table output
          // if it doesn't already exist and to allow the use of
          // the hex value without leading pound symbol
          // for conversion to other formats.
          hexString = hexString.replace('#', '');
          converted.hex = "#" + hexString;

          // check if it has an alpha channel, and possibly shorthand
          if (hexString.length === 4 || hexString.length === 8) {
            rgb = this.hexaToRgba(hexString);
            converted.rgb = "rgba(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ", " + rgb.alpha + ")";

            hsl = this.rgbaToHsla(rgb.red, rgb.green, rgb.blue, rgb.alpha);
            converted.hsl = "hsla(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%, " + hsl.alpha + ")";
          } else {
            rgb = this.hexToRgb(hexString);
            converted.rgb = "rgb(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ")";

            hsl = this.rgbToHsl(rgb.red, rgb.green, rgb.blue);
            converted.hsl = "hsl(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%)";
          }
        }

        if (this.rgbRegex.test(inputLine)) {
          // extract the values for red, green, blue.
          let rgbStrArray = inputLine.split(',');
          let red = rgbStrArray[0].replace('rgb(', '').trim();
          let green = rgbStrArray[1].trim();
          let blue = rgbStrArray[2].replace(')', '').trim();

          red = Number.parseInt(red);
          green = Number.parseInt(green);
          blue = Number.parseInt(blue);

          converted.hex = "#" + this.rgbToHex(red, green, blue);

          converted.rgb = "rgb(" + red + ", " + green + ", " + blue + ")";

          let hsl = this.rgbToHsl(red, green, blue);
          converted.hsl = converted.hsl = "hsl(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%)";
        }

        if (this.rgbaRegex.test(inputLine)) {
          // extract the values for red, green, blue, alpha.
          let rgbaStrArray = inputLine.split(',');
          let red = rgbaStrArray[0].replace('rgba(', '').trim();
          let green = rgbaStrArray[1].trim();
          let blue = rgbaStrArray[2].trim();
          let alpha = rgbaStrArray[3].replace(')', '').trim();

          red = Number.parseInt(red);
          green = Number.parseInt(green);
          blue = Number.parseInt(blue);

          converted.hex = "#" + this.rgbaToHexa(red, green, blue, alpha);

          converted.rgb = "rgba(" + red + ", " + green + ", " + blue + ", " + alpha + ")";

          let hsl = this.rgbToHsl(red, green, blue);
          converted.hsl = "hsla(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%, " + alpha + ")";
        }

        if (this.hslRegex.test(inputLine)) {
          // extract the values for hue, saturation, lightness.
          let hslStrArray = inputLine.split(',');
          let hue = hslStrArray[0].replace('hsl(', '').trim();
          let saturation = hslStrArray[1].replace('%', '').trim();
          let lightness = hslStrArray[2].replace('%', '').replace(')', '').trim();

          converted.hex = "#" + this.hslToHex(hue, saturation, lightness);

          let rgb = this.hslToRgb(hue, saturation, lightness);
          converted.rgb = "rgb(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ")";

          converted.hsl = "hsl(" + hue + ", " + saturation + "%, " + lightness + "%)";
        }

        if (this.hslaRegex.test(inputLine)) {
          // extract the values for hue, saturation, lightness, alpha.
          let hslaStrArray = inputLine.split(',');
          let hue = hslaStrArray[0].replace('hsla(', '').trim();
          let saturation = hslaStrArray[1].replace('%', '').trim();
          let lightness = hslaStrArray[2].replace('%', '').trim();
          let alpha = hslaStrArray[3].replace(')', '').trim();

          converted.hex = "#" + this.hslaToHexa(hue, saturation, lightness, alpha);

          let rgb = this.hslaToRgba(hue, saturation, lightness, alpha);
          converted.rgb = "rgb(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ", " + alpha + ")";

          converted.hsl = "hsla(" + hue + ", " + saturation + "%, " + lightness + "%, " + alpha + ")";
        }

        this.convertedColors.push(converted);
      }
    },

    // hex -> RGB
    hexToRgb: function (hexString) {
      let hex = 'ffffff';
      if (hexString.length === 3) {
        hex = '';
        hex += hexString.substr(2, 1) + hexString.substr(2, 1);
        hex += hexString.substr(1, 1) + hexString.substr(1, 1);
        hex += hexString.substr(0, 1) + hexString.substr(0, 1);
      } else {
        hex = hexString;
      }

      let bigint = Number.parseInt(hex, 16);
      let r = (bigint >> 16) & 255;
      let g = (bigint >> 8) & 255;
      let b = bigint & 255;

      return { "red": r, "green": g, "blue": b };
    },

    // hexa -> RGBa
    hexaToRgba: function (hexString) {
      let hex = 'ffffff';
      let alphaHex = 'ff';
      let alpha = 1;

      if (hexString.length === 4) {
        alphaHex = hexString.substr(3, 1) + hexString.substr(3, 1);
        hex = '';
        hex += hexString.substr(2, 1) + hexString.substr(2, 1);
        hex += hexString.substr(1, 1) + hexString.substr(1, 1);
        hex += hexString.substr(0, 1) + hexString.substr(0, 1);
      } else {
        alphaHex = hexString.substr(6, 2);
        hex = hexString.substr(0, 6);
      }

      let rgb = this.hexToRgb(hex);
      alpha = Number.parseFloat((Number.parseInt(alphaHex, 16) / 255)).toFixed(1);

      return { "red": rgb.red, "green": rgb.green, "blue": rgb.blue, "alpha": alpha };
    },

    // hex -> HSL
    hexToHsl: function (hexString) {
console.error('hex to HSL not implemented yet');
    },

    // hexa -> HSLa
    hexaToHsla: function (hexString) {
console.error('hexa to HSLa not implemented yet');
    },

    // RGB -> hex
    rgbToHex: function (r, g, b) {
      let red = r.toString(16);
      let green = g.toString(16);
      let blue = b.toString(16);

      if (red.length === 1) { red = "0" + red; }
      if (green.length === 1) { green = "0" + green; }
      if (blue.length === 1) { blue = "0" + blue; }
      return "" + red + green + blue;
    },

    // RGBa -> hexa
    rgbaToHexa: function (r, g, b, a) {
      let hex = this.rgbToHex(r, g, b);
      let alpha = Math.round(this.sanitizeAlphaFloat(a) * 255).toString(16);
      if (alpha.length == 1) { alpha = "0" + alpha; }
      return hex + alpha;
    },

    // RGB -> HSL
    rgbToHsl: function (red, green, blue) {
      let hue = 0, saturation = 0, lightness = 0;

      let redFrac = Number.parseFloat(red / 255),
        greenFrac = Number.parseFloat(green / 255),
        blueFrac = Number.parseFloat(blue / 255);

      let channelMin = Math.min(redFrac, blueFrac, greenFrac),
        channelMax = Math.max(redFrac, blueFrac, greenFrac),
        delta = channelMax - channelMin;

      if (delta === 0) { hue = 0; }
      // Red is the maximum color
      else if (channelMax === redFrac) {
        hue = ((greenFrac - blueFrac) / delta) % 6;
      }
      // Green is the maximum color
      else if (channelMax === greenFrac) {
        hue = (blueFrac - redFrac) / delta + 2;
      }
      // Green is the maximum color
      else {
        hue = (redFrac - greenFrac) / delta + 4;
      }

      hue = Math.round(hue * 60);
      // Make negative hues positive behind 360°
      if (hue < 0) { hue += 360; }

      lightness = (channelMax + channelMin) / 2;
      if (delta === 0) { saturation = 0; }
      else { saturation = delta / (1 - Math.abs(2 * lightness - 1)); }

      lightness = +(lightness * 100).toFixed(1);
      saturation = +(saturation * 100).toFixed(1);

      return { "hue": hue, "saturation": saturation, "lightness": lightness };
    },

    // RGBa -> HSLa
    rgbaToHsla: function (r, g, b, al) {
      let hsl = this.rgbToHsl(r, g, b);
      hsl.alpha = this.sanitizeAlphaFloat(al);
      return hsl;
    },

    // HSL -> hex
    hslToHex: function (hue, saturation, lightness) {
      let rgb = this.hslToRgb(hue, saturation, lightness);
      let red = rgb.red.toString(16);
      let green = rgb.green.toString(16);
      let blue = rgb.blue.toString(16);

      if (red.length === 1) { red = "0" + red; }
      if (green.length === 1) { green = "0" + green; }
      if (blue.length === 1) { blue = "0" + blue; }

      return "" + red + green + blue;
    },

    // HSLa -> hexa
    hslaToHexa: function (hue, saturation, lightness, alpha) {
      let rgb = this.hslToRgb(hue, saturation, lightness);
      let red = rgb.red.toString(16);
      let green = rgb.green.toString(16);
      let blue = rgb.blue.toString(16);
      let alphaHex = Math.round(this.sanitizeAlphaFloat(alpha) * 255).toString(16);

      if (red.length === 1) { red = "0" + red; }
      if (green.length === 1) { green = "0" + green; }
      if (blue.length === 1) { blue = "0" + blue; }

      return "" + red + green + blue + alphaHex;
    },

    // HSL -> RGB
    hslToRgb: function (hue, saturation, lightness) {
      if (hue === 360) { hue = 0; }
      if (hue < 0) { hue += 360; }

      saturation /= 100;
      lightness /= 100;

      let chroma = (1 - Math.abs(2 * lightness - 1)) * saturation,
        xComponent = chroma * (1 - Math.abs((hue / 60) % 2 - 1)),
        lightnessMatch = lightness - (chroma / 2),
        red = 0,
        green = 0,
        blue = 0;

      // set red and green if hue between 0 and 119
      if (0 <= hue && hue < 60) {
        red = chroma;
        green = xComponent;
      } else if (60 <= hue && hue < 120) {
        red = xComponent;
        green = chroma;
      }
      // set green and blue if hue between 120 and 239
      else if (120 <= hue && hue < 180) {
        green = chroma;
        blue = xComponent;
      } else if (180 <= hue && hue < 240) {
        green = xComponent;
        blue = chroma;
      }
      // set red and blue if hue between 0 and 119
      else if (240 <= hue && hue < 300) {
        red = xComponent;
        blue = chroma;
      } else if (300 <= hue && hue < 360) {
        red = chroma;
        blue = xComponent;
      }

      red = Math.round((red + lightnessMatch) * 255);
      green = Math.round((green + lightnessMatch) * 255);
      blue = Math.round((blue + lightnessMatch) * 255);

      return { "red": red, "green": green, "blue": blue };
    },

    // HSLa -> RGBa
    hslaToRgba: function (hue, saturation, lightness, alpha) {
      let rgb = this.hslToRgb(hue, saturation, lightness);
      return { "red": rgb.red, "green": rgb.green, "blue": rgb.blue, "alpha": this.sanitizeAlphaFloat(alpha) };
    },

    // --------------------------------
    // | helper methods
    // --------------------------------

    // Normalize the alpha to between 0.0 and 1.0
    // return single decimal place float value
    sanitizeAlphaFloat: function (alphaRaw) {
      let alpha = Number.parseFloat(1);

      if (alphaRaw >= 0 && alphaRaw <= 1) {
        alpha = Number.parseFloat(alphaRaw);
      } else if (alphaRaw < 0) {
        alpha = Number.parseFloat(0);
      }

      return alpha.toFixed(1);
    },

    updateClipboard: function (text) {
      let _this = this;
      clipboard.writeText(text).then(() => {
        _this.showErrorMessage = false;
        _this.showStatusMessage = true;
        setTimeout(() => {
          _this.showStatusMessage = false;
        }, 1000);
      })
      .catch(() => {
        _this.showStatusMessage = false;
        _this.showErrorMessage = true;
        setTimeout(() => {
          _this.showErrorMessage = false;
        }, 1000);
      });
    },
  },
});
