/**
* jQuery asColor v0.3.6
* https://github.com/amazingSurge/asColor
*
* Copyright (c) amazingSurge
* Released under the LGPL-3.0 license
*/
import $ from 'jquery';

var DEFAULTS = {
  format: false,
  shortenHex: false,
  hexUseName: false,
  reduceAlpha: false,
  alphaConvert: { // or false will disable convert
    'RGB': 'RGBA',
    'HSL': 'HSLA',
    'HEX': 'RGBA',
    'NAMESPACE': 'RGBA',
  },
  nameDegradation: 'HEX',
  invalidValue: '',
  zeroAlphaAsTransparent: true
};

function expandHex(hex) {
  if (hex.indexOf('#') === 0) {
    hex = hex.substr(1);
  }
  if (!hex) {
    return null;
  }
  if (hex.length === 3) {
    hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
  }
  return hex.length === 6 ? `#${hex}` : null;
}

function shrinkHex(hex) {
  if (hex.indexOf('#') === 0) {
    hex = hex.substr(1);
  }
  if (hex.length === 6 && hex[0] === hex[1] && hex[2] === hex[3] && hex[4] === hex[5]) {
    hex = hex[0] + hex[2] + hex[4];
  }
  return `#${hex}`;
}

function parseIntFromHex(val) {
  return parseInt(val, 16);
}

function isPercentage(n) {
  return typeof n === 'string' && n.indexOf('%') === n.length - 1;
}

function conventPercentageToRgb(n) {
  return parseInt(Math.round(n.slice(0, -1) * 2.55), 10);
}

function convertPercentageToFloat(n) {
  return parseFloat(n.slice(0, -1) / 100, 10);
}

function flip(o) {
  const flipped = {};
  for (const i in o) {
    if (o.hasOwnProperty(i)) {
      flipped[o[i]] = i;
    }
  }
  return flipped;
}

var NAMES = {
  aliceblue: 'f0f8ff',
  antiquewhite: 'faebd7',
  aqua: '0ff',
  aquamarine: '7fffd4',
  azure: 'f0ffff',
  beige: 'f5f5dc',
  bisque: 'ffe4c4',
  black: '000',
  blanchedalmond: 'ffebcd',
  blue: '00f',
  blueviolet: '8a2be2',
  brown: 'a52a2a',
  burlywood: 'deb887',
  burntsienna: 'ea7e5d',
  cadetblue: '5f9ea0',
  chartreuse: '7fff00',
  chocolate: 'd2691e',
  coral: 'ff7f50',
  cornflowerblue: '6495ed',
  cornsilk: 'fff8dc',
  crimson: 'dc143c',
  cyan: '0ff',
  darkblue: '00008b',
  darkcyan: '008b8b',
  darkgoldenrod: 'b8860b',
  darkgray: 'a9a9a9',
  darkgreen: '006400',
  darkgrey: 'a9a9a9',
  darkkhaki: 'bdb76b',
  darkmagenta: '8b008b',
  darkolivegreen: '556b2f',
  darkorange: 'ff8c00',
  darkorchid: '9932cc',
  darkred: '8b0000',
  darksalmon: 'e9967a',
  darkseagreen: '8fbc8f',
  darkslateblue: '483d8b',
  darkslategray: '2f4f4f',
  darkslategrey: '2f4f4f',
  darkturquoise: '00ced1',
  darkviolet: '9400d3',
  deeppink: 'ff1493',
  deepskyblue: '00bfff',
  dimgray: '696969',
  dimgrey: '696969',
  dodgerblue: '1e90ff',
  firebrick: 'b22222',
  floralwhite: 'fffaf0',
  forestgreen: '228b22',
  fuchsia: 'f0f',
  gainsboro: 'dcdcdc',
  ghostwhite: 'f8f8ff',
  gold: 'ffd700',
  goldenrod: 'daa520',
  gray: '808080',
  green: '008000',
  greenyellow: 'adff2f',
  grey: '808080',
  honeydew: 'f0fff0',
  hotpink: 'ff69b4',
  indianred: 'cd5c5c',
  indigo: '4b0082',
  ivory: 'fffff0',
  khaki: 'f0e68c',
  lavender: 'e6e6fa',
  lavenderblush: 'fff0f5',
  lawngreen: '7cfc00',
  lemonchiffon: 'fffacd',
  lightblue: 'add8e6',
  lightcoral: 'f08080',
  lightcyan: 'e0ffff',
  lightgoldenrodyellow: 'fafad2',
  lightgray: 'd3d3d3',
  lightgreen: '90ee90',
  lightgrey: 'd3d3d3',
  lightpink: 'ffb6c1',
  lightsalmon: 'ffa07a',
  lightseagreen: '20b2aa',
  lightskyblue: '87cefa',
  lightslategray: '789',
  lightslategrey: '789',
  lightsteelblue: 'b0c4de',
  lightyellow: 'ffffe0',
  lime: '0f0',
  limegreen: '32cd32',
  linen: 'faf0e6',
  magenta: 'f0f',
  maroon: '800000',
  mediumaquamarine: '66cdaa',
  mediumblue: '0000cd',
  mediumorchid: 'ba55d3',
  mediumpurple: '9370db',
  mediumseagreen: '3cb371',
  mediumslateblue: '7b68ee',
  mediumspringgreen: '00fa9a',
  mediumturquoise: '48d1cc',
  mediumvioletred: 'c71585',
  midnightblue: '191970',
  mintcream: 'f5fffa',
  mistyrose: 'ffe4e1',
  moccasin: 'ffe4b5',
  navajowhite: 'ffdead',
  navy: '000080',
  oldlace: 'fdf5e6',
  olive: '808000',
  olivedrab: '6b8e23',
  orange: 'ffa500',
  orangered: 'ff4500',
  orchid: 'da70d6',
  palegoldenrod: 'eee8aa',
  palegreen: '98fb98',
  paleturquoise: 'afeeee',
  palevioletred: 'db7093',
  papayawhip: 'ffefd5',
  peachpuff: 'ffdab9',
  peru: 'cd853f',
  pink: 'ffc0cb',
  plum: 'dda0dd',
  powderblue: 'b0e0e6',
  purple: '800080',
  red: 'f00',
  rosybrown: 'bc8f8f',
  royalblue: '4169e1',
  saddlebrown: '8b4513',
  salmon: 'fa8072',
  sandybrown: 'f4a460',
  seagreen: '2e8b57',
  seashell: 'fff5ee',
  sienna: 'a0522d',
  silver: 'c0c0c0',
  skyblue: '87ceeb',
  slateblue: '6a5acd',
  slategray: '708090',
  slategrey: '708090',
  snow: 'fffafa',
  springgreen: '00ff7f',
  steelblue: '4682b4',
  tan: 'd2b48c',
  teal: '008080',
  thistle: 'd8bfd8',
  tomato: 'ff6347',
  turquoise: '40e0d0',
  violet: 'ee82ee',
  wheat: 'f5deb3',
  white: 'fff',
  whitesmoke: 'f5f5f5',
  yellow: 'ff0',
  yellowgreen: '9acd32'
};

/* eslint no-bitwise: "off" */
const hexNames = flip(NAMES);

var Converter = {
  HSLtoRGB: function(hsl) {
    const h = hsl.h / 360;
    const s = hsl.s;
    const l = hsl.l;
    let m1;
    let m2;
    let rgb;
    if (l <= 0.5) {
      m2 = l * (s + 1);
    } else {
      m2 = l + s - (l * s);
    }
    m1 = l * 2 - m2;
    rgb = {
      r: this.hueToRGB(m1, m2, h + 1 / 3),
      g: this.hueToRGB(m1, m2, h),
      b: this.hueToRGB(m1, m2, h - 1 / 3)
    };
    if (typeof hsl.a !== 'undefined') {
      rgb.a = hsl.a;
    }
    if (hsl.l === 0) {
      rgb.h = hsl.h;
    }
    if (hsl.l === 1) {
      rgb.h = hsl.h;
    }
    return rgb;
  },

  hueToRGB: function(m1, m2, h) {
    let v;
    if (h < 0) {
      h += 1;
    } else if (h > 1) {
      h -= 1;
    }
    if ((h * 6) < 1) {
      v = m1 + (m2 - m1) * h * 6;
    } else if ((h * 2) < 1) {
      v = m2;
    } else if ((h * 3) < 2) {
      v = m1 + (m2 - m1) * (2 / 3 - h) * 6;
    } else {
      v = m1;
    }
    return Math.round(v * 255);
  },

  RGBtoHSL: function(rgb) {
    const r = rgb.r / 255;
    const g = rgb.g / 255;
    const b = rgb.b / 255;
    const min = Math.min(r, g, b);
    const max = Math.max(r, g, b);
    const diff = max - min;
    const add = max + min;
    const l = add * 0.5;
    let h;
    let s;

    if (min === max) {
      h = 0;
    } else if (r === max) {
      h = (60 * (g - b) / diff) + 360;
    } else if (g === max) {
      h = (60 * (b - r) / diff) + 120;
    } else {
      h = (60 * (r - g) / diff) + 240;
    }
    if (diff === 0) {
      s = 0;
    } else if (l <= 0.5) {
      s = diff / add;
    } else {
      s = diff / (2 - add);
    }

    return {
      h: Math.round(h) % 360,
      s,
      l
    };
  },

  RGBtoHEX: function(rgb) {
    let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];

    $.each(hex, (nr, val) => {
      if (val.length === 1) {
        hex[nr] = `0${val}`;
      }
    });
    return `#${hex.join('')}`;
  },

  HSLtoHEX: function(hsl) {
    const rgb = this.HSLtoRGB(hsl);
    return this.RGBtoHEX(rgb);
  },

  HSVtoHEX: function(hsv) {
    const rgb = this.HSVtoRGB(hsv);
    return this.RGBtoHEX(rgb);
  },

  RGBtoHSV: function(rgb) {
    const r = rgb.r / 255;
    const g = rgb.g / 255;
    const b = rgb.b / 255;
    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    let h;
    let s;
    const v = max;
    const diff = max - min;
    s = (max === 0) ? 0 : diff / max;
    if (max === min) {
      h = 0;
    } else {
      switch (max) {
        case r: {
          h = (g - b) / diff + (g < b ? 6 : 0);
          break;
        }
        case g: {
          h = (b - r) / diff + 2;
          break;
        }
        case b: {
          h = (r - g) / diff + 4;
          break;
        }
        default: {
          break;
        }
      }
      h /= 6;
    }

    return {
      h: Math.round(h * 360),
      s,
      v
    };
  },

  HSVtoRGB: function(hsv) {
    let r;
    let g;
    let b;
    let h = (hsv.h % 360) / 60;
    const s = hsv.s;
    const v = hsv.v;
    const c = v * s;
    const x = c * (1 - Math.abs(h % 2 - 1));

    r = g = b = v - c;
    h = ~~h;

    r += [c, x, 0, 0, x, c][h];
    g += [x, c, c, x, 0, 0][h];
    b += [0, 0, x, c, c, x][h];

    let rgb = {
      r: Math.round(r * 255),
      g: Math.round(g * 255),
      b: Math.round(b * 255)
    };

    if (typeof hsv.a !== 'undefined') {
      rgb.a = hsv.a;
    }

    if (hsv.v === 0) {
      rgb.h = hsv.h;
    }

    if (hsv.v === 1 && hsv.s === 0) {
      rgb.h = hsv.h;
    }

    return rgb;
  },

  HEXtoRGB: function(hex) {
    if (hex.length === 4) {
      hex = expandHex(hex);
    }
    return {
      r: parseIntFromHex(hex.substr(1, 2)),
      g: parseIntFromHex(hex.substr(3, 2)),
      b: parseIntFromHex(hex.substr(5, 2))
    };
  },

  isNAME: function(string) {
    if (NAMES.hasOwnProperty(string)) {
      return true;
    }
    return false;
  },

  NAMEtoHEX: function(name) {
    if (NAMES.hasOwnProperty(name)) {
      return `#${NAMES[name]}`;
    }
    return null;
  },

  NAMEtoRGB: function(name) {
    const hex = this.NAMEtoHEX(name);

    if (hex) {
      return this.HEXtoRGB(hex);
    }
    return null;
  },

  hasNAME: function(rgb) {
    let hex = this.RGBtoHEX(rgb);

    hex = shrinkHex(hex);

    if (hex.indexOf('#') === 0) {
      hex = hex.substr(1);
    }

    if (hexNames.hasOwnProperty(hex)) {
      return hexNames[hex];
    }
    return false;
  },

  RGBtoNAME: function(rgb) {
    const hasName = this.hasNAME(rgb);
    if (hasName) {
      return hasName;
    }

    return null;
  }
};

const CSS_INTEGER = '[-\\+]?\\d+%?';
const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;

const PERMISSIVE_MATCH3 = `[\\s|\\(]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})\\s*\\)`;
const PERMISSIVE_MATCH4 = `[\\s|\\(]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})\\s*\\)`;

const ColorStrings = {
  RGB: {
    match: new RegExp(`^rgb${PERMISSIVE_MATCH3}$`, 'i'),
    parse: function(result) {
      return {
        r: isPercentage(result[1]) ? conventPercentageToRgb(result[1]) : parseInt(result[1], 10),
        g: isPercentage(result[2]) ? conventPercentageToRgb(result[2]) : parseInt(result[2], 10),
        b: isPercentage(result[3]) ? conventPercentageToRgb(result[3]) : parseInt(result[3], 10),
        a: 1
      };
    },
    to: function(color) {
      return `rgb(${color.r}, ${color.g}, ${color.b})`;
    }
  },
  RGBA: {
    match: new RegExp(`^rgba${PERMISSIVE_MATCH4}$`, 'i'),
    parse: function(result) {
      return {
        r: isPercentage(result[1]) ? conventPercentageToRgb(result[1]) : parseInt(result[1], 10),
        g: isPercentage(result[2]) ? conventPercentageToRgb(result[2]) : parseInt(result[2], 10),
        b: isPercentage(result[3]) ? conventPercentageToRgb(result[3]) : parseInt(result[3], 10),
        a: isPercentage(result[4]) ? convertPercentageToFloat(result[4]) : parseFloat(result[4], 10)
      };
    },
    to: function(color) {
      return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
    }
  },
  HSL: {
    match: new RegExp(`^hsl${PERMISSIVE_MATCH3}$`, 'i'),
    parse: function(result) {
      const hsl = {
        h: ((result[1] % 360) + 360) % 360,
        s: isPercentage(result[2]) ? convertPercentageToFloat(result[2]) : parseFloat(result[2], 10),
        l: isPercentage(result[3]) ? convertPercentageToFloat(result[3]) : parseFloat(result[3], 10),
        a: 1
      };

      return Converter.HSLtoRGB(hsl);
    },
    to: function(color) {
      const hsl = Converter.RGBtoHSL(color);
      return `hsl(${parseInt(hsl.h, 10)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%)`;
    }
  },
  HSLA: {
    match: new RegExp(`^hsla${PERMISSIVE_MATCH4}$`, 'i'),
    parse: function(result) {
      const hsla = {
        h: ((result[1] % 360) + 360) % 360,
        s: isPercentage(result[2]) ? convertPercentageToFloat(result[2]) : parseFloat(result[2], 10),
        l: isPercentage(result[3]) ? convertPercentageToFloat(result[3]) : parseFloat(result[3], 10),
        a: isPercentage(result[4]) ? convertPercentageToFloat(result[4]) : parseFloat(result[4], 10)
      };

      return Converter.HSLtoRGB(hsla);
    },
    to: function(color) {
      const hsl = Converter.RGBtoHSL(color);
      return `hsla(${parseInt(hsl.h, 10)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%, ${color.a})`;
    }
  },
  HEX: {
    match: /^#([a-f0-9]{6}|[a-f0-9]{3})$/i,
    parse: function(result) {
      const hex = result[0];
      const rgb = Converter.HEXtoRGB(hex);
      return {
        r: rgb.r,
        g: rgb.g,
        b: rgb.b,
        a: 1
      };
    },
    to: function(color, instance) {
      let hex = Converter.RGBtoHEX(color);

      if (instance) {
        if (instance.options.hexUseName) {
          const hasName = Converter.hasNAME(color);
          if (hasName) {
            return hasName;
          }
        }
        if (instance.options.shortenHex) {
          hex = shrinkHex(hex);
        }
      }
      return `${hex}`;
    }
  },
  TRANSPARENT: {
    match: /^transparent$/i,
    parse: function() {
      return {
        r: 0,
        g: 0,
        b: 0,
        a: 0
      };
    },
    to: function() {
      return 'transparent';
    }
  },
  NAME: {
    match: /^\w+$/i,
    parse: function(result) {
      const rgb = Converter.NAMEtoRGB(result[0]);
      if(rgb) {
        return {
          r: rgb.r,
          g: rgb.g,
          b: rgb.b,
          a: 1
        };
      }

      return null;
    },
    to: function(color, instance) {
      let name = Converter.RGBtoNAME(color);

      if(name) {
        return name;
      }

      return ColorStrings[instance.options.nameDegradation.toUpperCase()].to(color);
    }
  }
};

/* eslint no-extend-native: "off" */
if (!String.prototype.includes) {
  String.prototype.includes = function(search, start) {
    'use strict';
    if (typeof start !== 'number') {
      start = 0;
    }

    if (start + search.length > this.length) {
      return false;
    }
    return this.indexOf(search, start) !== -1;
  };
}

class AsColor {
  constructor(string, options) {
    if (typeof string === 'object' && typeof options === 'undefined') {
      options = string;
      string = undefined;
    }
    if(typeof options === 'string'){
      options = {
        format: options
      };
    }
    this.options = $.extend(true, {}, DEFAULTS, options);
    this.value = {
      r: 0,
      g: 0,
      b: 0,
      h: 0,
      s: 0,
      v: 0,
      a: 1
    };
    this._format = false;
    this._matchFormat = 'HEX';
    this._valid = true;

    this.init(string);
  }

  init(string) {
    this.format(this.options.format);
    this.fromString(string);
    return this;
  }

  isValid() {
    return this._valid;
  }

  val(value) {
    if (typeof value === 'undefined') {
      return this.toString();
    }
    this.fromString(value);
    return this;
  }

  alpha(value) {
    if (typeof value === 'undefined' || isNaN(value)) {
      return this.value.a;
    }

    value = parseFloat(value);

    if (value > 1) {
      value = 1;
    } else if (value < 0) {
      value = 0;
    }
    this.value.a = value;
    return this;
  }

  matchString(string) {
    return AsColor.matchString(string);
  }

  fromString(string, updateFormat) {
    if (typeof string === 'string') {
      string = $.trim(string);
      let matched = null;
      let rgb;
      this._valid = false;
      for (let i in ColorStrings) {
        if ((matched = ColorStrings[i].match.exec(string)) !== null) {
          rgb = ColorStrings[i].parse(matched);

          if (rgb) {
            this.set(rgb);
            if(i === 'TRANSPARENT'){
              i = 'HEX';
            }
            this._matchFormat = i;
            if (updateFormat === true) {
              this.format(i);
            }
            break;
          }
        }
      }
    } else if (typeof string === 'object') {
      this.set(string);
    }
    return this;
  }

  format(format) {
    if (typeof format === 'string' && (format = format.toUpperCase()) && typeof ColorStrings[format] !== 'undefined') {
      if (format !== 'TRANSPARENT') {
        this._format = format;
      } else {
        this._format = 'HEX';
      }
    } else if(format === false) {
      this._format = false;
    }
    if(this._format === false){
      return this._matchFormat;
    }
    return this._format;
  }

  toRGBA() {
    return ColorStrings.RGBA.to(this.value, this);
  }

  toRGB() {
    return ColorStrings.RGB.to(this.value, this);
  }

  toHSLA() {
    return ColorStrings.HSLA.to(this.value, this);
  }

  toHSL() {
    return ColorStrings.HSL.to(this.value, this);
  }

  toHEX() {
    return ColorStrings.HEX.to(this.value, this);
  }

  toNAME() {
    return ColorStrings.NAME.to(this.value, this);
  }

  to(format) {
    if (typeof format === 'string' && (format = format.toUpperCase()) && typeof ColorStrings[format] !== 'undefined') {
      return ColorStrings[format].to(this.value, this);
    }
    return this.toString();
  }

  toString() {
    let value = this.value;
    if (!this._valid) {
      value = this.options.invalidValue;

      if (typeof value === 'string') {
        return value;
      }
    }

    if (value.a === 0 && this.options.zeroAlphaAsTransparent) {
      return ColorStrings.TRANSPARENT.to(value, this);
    }

    let format;
    if(this._format === false){
      format = this._matchFormat;
    } else {
      format = this._format;
    }

    if (this.options.reduceAlpha && value.a === 1) {
      switch (format) {
        case 'RGBA':
          format = 'RGB';
          break;
        case 'HSLA':
          format = 'HSL';
          break;
        default:
          break;
      }
    }

    if (value.a !== 1 && format!=='RGBA' && format !=='HSLA' && this.options.alphaConvert){
      if(typeof this.options.alphaConvert === 'string'){
        format = this.options.alphaConvert;
      }
      if(typeof this.options.alphaConvert[format] !== 'undefined'){
        format = this.options.alphaConvert[format];
      }
    }
    return ColorStrings[format].to(value, this);
  }

  get() {
    return this.value;
  }

  set(color) {
    this._valid = true;
    let fromRgb = 0;
    let fromHsv = 0;
    let hsv;
    let rgb;

    for (const i in color) {
      if ('hsv'.includes(i)) {
        fromHsv++;
        this.value[i] = color[i];
      } else if ('rgb'.includes(i)) {
        fromRgb++;
        this.value[i] = color[i];
      } else if (i === 'a') {
        this.value.a = color.a;
      }
    }
    if (fromRgb > fromHsv) {
      hsv = Converter.RGBtoHSV(this.value);
      if (this.value.r === 0 && this.value.g === 0 && this.value.b === 0) {
        // this.value.h = color.h;
      } else {
        this.value.h = hsv.h;
      }

      this.value.s = hsv.s;
      this.value.v = hsv.v;
    } else if (fromHsv > fromRgb) {
      rgb = Converter.HSVtoRGB(this.value);
      this.value.r = rgb.r;
      this.value.g = rgb.g;
      this.value.b = rgb.b;
    }
    return this;
  }

  static matchString(string) {
    if (typeof string === 'string') {
      string = $.trim(string);
      let matched = null;
      let rgb;
      for (const i in ColorStrings) {
        if ((matched = ColorStrings[i].match.exec(string)) !== null) {
          rgb = ColorStrings[i].parse(matched);

          if (rgb) {
            return true;
          }
        }
      }
    }
    return false;
  }

  static setDefaults(options) {
    $.extend(true, DEFAULTS, $.isPlainObject(options) && options);
  }
}

var info = {
  version:'0.3.6'
};

const OtherAsColor = $.asColor;

const jQueryAsColor = function(...args) {
  return new AsColor(...args);
};

$.asColor = jQueryAsColor;
$.asColor.Constructor = AsColor;

$.extend($.asColor, {
  matchString: AsColor.matchString,
  setDefaults: AsColor.setDefaults,
  noConflict: function() {
    $.asColor = OtherAsColor;
    return jQueryAsColor;
  }
}, Converter, info);

var main = $.asColor;

export default main;
