var Dimension = require(“../tree/dimension”),

Color = require("../tree/color"),
Quoted = require("../tree/quoted"),
Anonymous = require("../tree/anonymous"),
functionRegistry = require("./function-registry"),
colorFunctions;

function clamp(val) {

return Math.min(1, Math.max(0, val));

} function hsla(color) {

return colorFunctions.hsla(color.h, color.s, color.l, color.a);

} function number(n) {

if (n instanceof Dimension) {
    return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
} else if (typeof n === 'number') {
    return n;
} else {
    throw {
        type: "Argument",
        message: "color functions take numbers as parameters"
    };
}

} function scaled(n, size) {

if (n instanceof Dimension && n.unit.is('%')) {
    return parseFloat(n.value * size / 100);
} else {
    return number(n);
}

} colorFunctions = {

rgb: function (r, g, b) {
    return colorFunctions.rgba(r, g, b, 1.0);
},
rgba: function (r, g, b, a) {
    var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
    a = number(a);
    return new Color(rgb, a);
},
hsl: function (h, s, l) {
    return colorFunctions.hsla(h, s, l, 1.0);
},
hsla: function (h, s, l, a) {

    var m1, m2;

    function hue(h) {
        h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
        if (h * 6 < 1) {
            return m1 + (m2 - m1) * h * 6;
        }
        else if (h * 2 < 1) {
            return m2;
        }
        else if (h * 3 < 2) {
            return m1 + (m2 - m1) * (2 / 3 - h) * 6;
        }
        else {
            return m1;
        }
    }

    h = (number(h) % 360) / 360;
    s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));

    m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
    m1 = l * 2 - m2;

    return colorFunctions.rgba(hue(h + 1 / 3) * 255,
        hue(h)       * 255,
        hue(h - 1 / 3) * 255,
        a);
},

hsv: function(h, s, v) {
    return colorFunctions.hsva(h, s, v, 1.0);
},

hsva: function(h, s, v, a) {
    h = ((number(h) % 360) / 360) * 360;
    s = number(s); v = number(v); a = number(a);

    var i, f;
    i = Math.floor((h / 60) % 6);
    f = (h / 60) - i;

    var vs = [v,
        v * (1 - s),
        v * (1 - f * s),
        v * (1 - (1 - f) * s)];
    var perm = [[0, 3, 1],
        [2, 0, 1],
        [1, 0, 3],
        [1, 2, 0],
        [3, 1, 0],
        [0, 1, 2]];

    return colorFunctions.rgba(vs[perm[i][0]] * 255,
        vs[perm[i][1]] * 255,
        vs[perm[i][2]] * 255,
        a);
},

hue: function (color) {
    return new Dimension(color.toHSL().h);
},
saturation: function (color) {
    return new Dimension(color.toHSL().s * 100, '%');
},
lightness: function (color) {
    return new Dimension(color.toHSL().l * 100, '%');
},
hsvhue: function(color) {
    return new Dimension(color.toHSV().h);
},
hsvsaturation: function (color) {
    return new Dimension(color.toHSV().s * 100, '%');
},
hsvvalue: function (color) {
    return new Dimension(color.toHSV().v * 100, '%');
},
red: function (color) {
    return new Dimension(color.rgb[0]);
},
green: function (color) {
    return new Dimension(color.rgb[1]);
},
blue: function (color) {
    return new Dimension(color.rgb[2]);
},
alpha: function (color) {
    return new Dimension(color.toHSL().a);
},
luma: function (color) {
    return new Dimension(color.luma() * color.alpha * 100, '%');
},
luminance: function (color) {
    var luminance =
        (0.2126 * color.rgb[0] / 255) +
            (0.7152 * color.rgb[1] / 255) +
            (0.0722 * color.rgb[2] / 255);

    return new Dimension(luminance * color.alpha * 100, '%');
},
saturate: function (color, amount, method) {
    // filter: saturate(3.2);
    // should be kept as is, so check for color
    if (!color.rgb) {
        return null;
    }
    var hsl = color.toHSL();

    if (typeof method !== "undefined" && method.value === "relative") {
        hsl.s +=  hsl.s * amount.value / 100;
    }
    else {
        hsl.s += amount.value / 100;
    }
    hsl.s = clamp(hsl.s);
    return hsla(hsl);
},
desaturate: function (color, amount, method) {
    var hsl = color.toHSL();

    if (typeof method !== "undefined" && method.value === "relative") {
        hsl.s -=  hsl.s * amount.value / 100;
    }
    else {
        hsl.s -= amount.value / 100;
    }
    hsl.s = clamp(hsl.s);
    return hsla(hsl);
},
lighten: function (color, amount, method) {
    var hsl = color.toHSL();

    if (typeof method !== "undefined" && method.value === "relative") {
        hsl.l +=  hsl.l * amount.value / 100;
    }
    else {
        hsl.l += amount.value / 100;
    }
    hsl.l = clamp(hsl.l);
    return hsla(hsl);
},
darken: function (color, amount, method) {
    var hsl = color.toHSL();

    if (typeof method !== "undefined" && method.value === "relative") {
        hsl.l -=  hsl.l * amount.value / 100;
    }
    else {
        hsl.l -= amount.value / 100;
    }
    hsl.l = clamp(hsl.l);
    return hsla(hsl);
},
fadein: function (color, amount, method) {
    var hsl = color.toHSL();

    if (typeof method !== "undefined" && method.value === "relative") {
        hsl.a +=  hsl.a * amount.value / 100;
    }
    else {
        hsl.a += amount.value / 100;
    }
    hsl.a = clamp(hsl.a);
    return hsla(hsl);
},
fadeout: function (color, amount, method) {
    var hsl = color.toHSL();

    if (typeof method !== "undefined" && method.value === "relative") {
        hsl.a -=  hsl.a * amount.value / 100;
    }
    else {
        hsl.a -= amount.value / 100;
    }
    hsl.a = clamp(hsl.a);
    return hsla(hsl);
},
fade: function (color, amount) {
    var hsl = color.toHSL();

    hsl.a = amount.value / 100;
    hsl.a = clamp(hsl.a);
    return hsla(hsl);
},
spin: function (color, amount) {
    var hsl = color.toHSL();
    var hue = (hsl.h + amount.value) % 360;

    hsl.h = hue < 0 ? 360 + hue : hue;

    return hsla(hsl);
},
//
// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
// http://sass-lang.com
//
mix: function (color1, color2, weight) {
    if (!color1.toHSL || !color2.toHSL) {
        console.log(color2.type);
        console.dir(color2);
    }
    if (!weight) {
        weight = new Dimension(50);
    }
    var p = weight.value / 100.0;
    var w = p * 2 - 1;
    var a = color1.toHSL().a - color2.toHSL().a;

    var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
    var w2 = 1 - w1;

    var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
        color1.rgb[1] * w1 + color2.rgb[1] * w2,
        color1.rgb[2] * w1 + color2.rgb[2] * w2];

    var alpha = color1.alpha * p + color2.alpha * (1 - p);

    return new Color(rgb, alpha);
},
greyscale: function (color) {
    return colorFunctions.desaturate(color, new Dimension(100));
},
contrast: function (color, dark, light, threshold) {
    // filter: contrast(3.2);
    // should be kept as is, so check for color
    if (!color.rgb) {
        return null;
    }
    if (typeof light === 'undefined') {
        light = colorFunctions.rgba(255, 255, 255, 1.0);
    }
    if (typeof dark === 'undefined') {
        dark = colorFunctions.rgba(0, 0, 0, 1.0);
    }
    //Figure out which is actually light and dark!
    if (dark.luma() > light.luma()) {
        var t = light;
        light = dark;
        dark = t;
    }
    if (typeof threshold === 'undefined') {
        threshold = 0.43;
    } else {
        threshold = number(threshold);
    }
    if (color.luma() < threshold) {
        return light;
    } else {
        return dark;
    }
},
argb: function (color) {
    return new Anonymous(color.toARGB());
},
color: function(c) {
    if ((c instanceof Quoted) &&
        (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
        return new Color(c.value.slice(1));
    }
    if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
        c.value = undefined;
        return c;
    }
    throw {
        type:    "Argument",
        message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF"
    };
},
tint: function(color, amount) {
    return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
},
shade: function(color, amount) {
    return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
}

}; functionRegistry.addMultiple(colorFunctions);