import { Colors } from 'const/colors';

const KnownColors = {};

const Color = function(arg) {
    const rgbaMatch = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(,\s*([\d.]+))?\)$/.exec(arg);
    if (rgbaMatch) {
        this.r = +rgbaMatch[1];
        this.g = +rgbaMatch[2];
        this.b = +rgbaMatch[2];
        this.a = rgbaMatch[4] ? rgbaMatch[4] : 1;
        this.setHsl();
    } else {
        const hexMatch = /^#?([0-9a-f]{3,6})$/i.exec(arg);
        if (hexMatch) {
            const digits = hexMatch[1];
            const len = digits.length === 3 ? 1 : 2;
            this.r = parseInt(digits.substr(0, len), 16);
            this.g = parseInt(digits.substr(len, len), 16);
            this.b = parseInt(digits.substr(len * 2, len), 16);
            this.a = 1;
            this.setHsl();
        } else if (arg instanceof Color) {
            this.r = arg.r;
            this.g = arg.g;
            this.b = arg.b;
            this.h = arg.h;
            this.s = arg.s;
            this.l = arg.l;
            this.a = arg.a;
        } else {
            this.r = this.g = this.b = this.h = this.s = this.l = 0;
            this.a = 1;
        }
    }
};

Color.prototype.setHsl = function() {
    const r = this.r / 255;
    const g = this.g / 255;
    const b = this.b / 255;

    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    let h;
    let s;
    const l = (max + min) / 2;

    if (max === min) {
        h = s = 0; // achromatic
    } else {
        const d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }

    this.h = h;
    this.s = s;
    this.l = l;
};

Color.prototype.toHex = function() {
    return '#' + hex(this.r) + hex(this.g) + hex(this.b);
};

Color.prototype.toRgba = function() {
    return `rgba(${Math.round(this.r)},${Math.round(this.g)},${Math.round(this.b)},${this.a})`;
};

Color.prototype.toHsla = function() {
    return `hsla(${Math.round(this.h * 100)},${Math.round(this.s * 100)}%,${Math.round(
        this.l * 100
    )}%,${this.a})`;
};

Color.prototype.distanceTo = function(color) {
    return Math.abs(this.h - color.h);
};

Color.prototype.mix = function(another, weight) {
    const res = new Color(this);
    const anotherWeight = 1 - weight;
    res.r = this.r * weight + another.r * anotherWeight;
    res.g = this.g * weight + another.g * anotherWeight;
    res.b = this.b * weight + another.b * anotherWeight;
    res.a = this.a * weight + another.a * anotherWeight;
    return res;
};

Color.getNearest = function(colorStr) {
    const color = new Color(colorStr);
    if (!color.s) {
        return null;
    }
    let selected = null,
        minDistance = Number.MAX_VALUE;
    for (const [name, col] of Object.entries(KnownColors)) {
        const distance = color.distanceTo(col);
        if (distance < minDistance) {
            minDistance = distance;
            selected = name;
        }
    }
    return selected;
};

Color.getKnownBgColor = function(knownColor) {
    return Colors.BgColors[knownColor] ? '#' + Colors.BgColors[knownColor] : undefined;
};

for (const [name, val] of Object.entries(Colors.ColorsValues)) {
    KnownColors[name] = new Color(val);
}

Color.black = new Color('#000');

function hex(num) {
    const str = (num || 0).toString(16);
    return str.length < 2 ? '0' + str : str;
}

export { Color };
