import $ from "jquery";
import * as util from "./util";
import keywordAngleMap from "./keywordAngleMap";

const angleKeywordMap = util.flip(keywordAngleMap);

const RegExpStrings = (() => {
  const color =
    /(?:rgba|rgb|hsla|hsl)\s*\([\s\d\.,%]+\)|#[a-z0-9]{3,6}|[a-z]+/i;
  const position = /\d{1,3}%/i;
  const angle = /(?:to ){0,1}(?:(?:top|left|right|bottom)\s*){1,2}|\d+deg/i;
  const stop = new RegExp(
    `(${color.source})\\s*(${position.source}){0,1}`,
    "i"
  );
  const stops = new RegExp(stop.source, "gi");
  const parameters = new RegExp(
    `(?:(${angle.source})){0,1}\\s*,{0,1}\\s*(.*?)\\s*`,
    "i"
  );
  const full = new RegExp(
    `^(-webkit-|-moz-|-ms-|-o-){0,1}(linear|radial|repeating-linear)-gradient\\s*\\(\\s*(${parameters.source})\\s*\\)$`,
    "i"
  );

  return {
    FULL: full,
    ANGLE: angle,
    COLOR: color,
    POSITION: position,
    STOP: stop,
    STOPS: stops,
    PARAMETERS: new RegExp(`^${parameters.source}$`, "i"),
  };
})();

export default {
  matchString: function (string) {
    const matched = this.parseString(string);
    if (
      matched &&
      matched.value &&
      matched.value.stops &&
      matched.value.stops.length > 1
    ) {
      return true;
    }
    return false;
  },

  parseString: function (string) {
    string = $.trim(string);
    let matched;
    if ((matched = RegExpStrings.FULL.exec(string)) !== null) {
      let value = this.parseParameters(matched[3]);

      return {
        prefix: typeof matched[1] === "undefined" ? null : matched[1],
        type: matched[2],
        value: value,
      };
    } else {
      return false;
    }
  },

  parseParameters: function (string) {
    let matched;
    if ((matched = RegExpStrings.PARAMETERS.exec(string)) !== null) {
      let stops = this.parseStops(matched[2]);
      return {
        angle: typeof matched[1] === "undefined" ? 0 : matched[1],
        stops: stops,
      };
    } else {
      return false;
    }
  },

  parseStops: function (string) {
    let matched;
    const result = [];
    if ((matched = string.match(RegExpStrings.STOPS)) !== null) {
      $.each(matched, (i, item) => {
        const stop = this.parseStop(item);
        if (stop) {
          result.push(stop);
        }
      });
      return result;
    } else {
      return false;
    }
  },

  formatStops: function (stops, cleanPosition) {
    let stop;
    const output = [];
    let positions = [];
    const colors = [];
    let position;

    for (let i = 0; i < stops.length; i++) {
      stop = stops[i];
      if (typeof stop.position === "undefined" || stop.position === null) {
        if (i === 0) {
          position = 0;
        } else if (i === stops.length - 1) {
          position = 1;
        } else {
          position = undefined;
        }
      } else {
        position = stop.position;
      }
      positions.push(position);
      colors.push(stop.color.toString());
    }

    positions = ((data) => {
      let start = null;
      let average;
      for (let i = 0; i < data.length; i++) {
        if (isNaN(data[i])) {
          if (start === null) {
            start = i;
            continue;
          }
        } else if (start) {
          average = (data[i] - data[start - 1]) / (i - start + 1);
          for (let j = start; j < i; j++) {
            data[j] = data[start - 1] + (j - start + 1) * average;
          }
          start = null;
        }
      }

      return data;
    })(positions);

    for (let x = 0; x < stops.length; x++) {
      if (
        cleanPosition &&
        ((x === 0 && positions[x] === 0) ||
          (x === stops.length - 1 && positions[x] === 1))
      ) {
        position = "";
      } else {
        position = ` ${this.formatPosition(positions[x])}`;
      }

      output.push(colors[x] + position);
    }
    return output.join(", ");
  },

  parseStop: function (string) {
    let matched;
    if ((matched = RegExpStrings.STOP.exec(string)) !== null) {
      let position = this.parsePosition(matched[2]);

      return {
        color: matched[1],
        position: position,
      };
    } else {
      return false;
    }
  },

  parsePosition: function (string) {
    if (typeof string === "string" && string.substr(-1) === "%") {
      string = parseFloat(string.slice(0, -1) / 100);
    }

    if (typeof string !== "undefined" && string !== null) {
      return parseFloat(string, 10);
    } else {
      return null;
    }
  },

  formatPosition: function (value) {
    return `${parseInt(value * 100, 10)}%`;
  },

  parseAngle: function (string, notStandard) {
    if (typeof string === "string" && string.includes("deg")) {
      string = string.replace("deg", "");
    }
    if (!isNaN(string)) {
      if (notStandard) {
        string = this.fixOldAngle(string);
      }
    }
    if (typeof string === "string") {
      const directions = string.split(" ");

      const filtered = [];
      for (const i in directions) {
        if (util.isDirection(directions[i])) {
          filtered.push(directions[i].toLowerCase());
        }
      }
      let keyword = filtered.join(" ");

      if (!string.includes("to ")) {
        keyword = util.reverseDirection(keyword);
      }
      keyword = `to ${keyword}`;
      if (keywordAngleMap.hasOwnProperty(keyword)) {
        string = keywordAngleMap[keyword];
      }
    }
    let value = parseFloat(string, 10);

    if (value > 360) {
      value %= 360;
    } else if (value < 0) {
      value %= -360;

      if (value !== 0) {
        value += 360;
      }
    }
    return value;
  },

  fixOldAngle: function (value) {
    value = parseFloat(value);
    value = Math.abs(450 - value) % 360;
    value = parseFloat(value.toFixed(3));
    return value;
  },

  formatAngle: function (value, notStandard, useKeyword) {
    value = parseInt(value, 10);
    if (useKeyword && angleKeywordMap.hasOwnProperty(value)) {
      value = angleKeywordMap[value];
      if (notStandard) {
        value = util.reverseDirection(value.substr(3));
      }
    } else {
      if (notStandard) {
        value = this.fixOldAngle(value);
      }
      value = `${value}deg`;
    }

    return value;
  },
};
