/* make an image with the specified properties ... pixel is (eg.)
 * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and
 * type, generate a 3 band float image, and lab2labq it before handing it
 * back.
 */
image_new options w h
    = embed [$extend => $copy] 0 0 w h image'''
{
    // set most defaults from the pixel option (if any)
    pixel = get_option options [$pixel => 0] $pixel;
    image_pixel = vips_black [] 1 1 + to_vector pixel;

    defaults = get_option options [
        $format => get_format image_pixel,
        $interpretation => get_interpretation image_pixel,
        $bands => get_bands image_pixel,
        $coding => Coding.NOCODING,
        $xres => 0,
        $yres => 0,
        $xoffset => 0,
        $yoffset => 0,
        $pixel => 0
    ];

    coding = defaults $coding;
    format
        = Format.FLOAT, coding == Coding.LABPACK
        = defaults $format;
    bands
        = 3, coding == Coding.LABPACK
        = defaults $bands;
    interpretation
        = Interpretation.LAB, coding == Coding.LABPACK
        = defaults $interpretation;
    xres = defaults $xres;
    yres = defaults $yres;
    xoffset = defaults $xoffset;
    yoffset = defaults $yoffset;

    image = vips_black [$bands => to_real bands] 1 1 + to_vector pixel;
    image' = vips_cast [] image format;
    image''
        = vips_Lab2LabQ [] image', coding == Coding.LABPACK
        = image';
    image''' = copy [
        $interpretation => interpretation,
        $xres => xres,
        $yres => yres,
        $xoffset => xoffset,
        $yoffset => yoffset
    ] image'';
}

image_new_from_image options image
    = image_new options'
{
    // earlier options override later options, so we just append the image
    // properties to the option set
    options' = options ++ [
        $bands => get_bands image,
        $format => get_format image,
        $coding => get_coding image,
        $interpretation => get_interpretation image,
        $xoffset => get_xoffset image,
        $yoffset => get_yoffset image,
        $xres => get_xres image,
        $yres => get_yres image
    ];
}

/* generate a slice of LAB space size x size pixels for L* == l
 */
lab_slice size l
    = copy [
        $interpretation => $lab,
        $xoffset => size / 2,
        $yoffset => size / 2
    ] (L ++ a ++ b)
{
    L = image_new [
        $format => Format.FLOAT,
        $interpretation => Interpretation.B_W,
        $pixel => l
    ] size size;

    ramp = grey [] size size;
    a = ramp * 256 - 128;
    b = rot90 a;
}

/* Look at Image, try to make a Colour (failing that, a Vector) which is white
 * for that image type.
 */
image_white im
    = colourspace [] interpretation white_lab,
        bands == 3 &&
        coding == Coding.NOCODING &&
        Interpretation_3ary.has_value interpretation
    = white_lab,
        coding == Coding.LABPACK
    = Vector (replicate bands (Format._maxval format))
{
    bands = get_bands im;
    interpretation = get_interpretation im;
    format = get_format im;
    coding = get_coding im;

    // white as LAB
    white_lab = Colour "LAB" [100, 0, 0];
}

/* Make a separable square mask.
 */
matrix_blur radius
    = Matrix_con (sum mask_sq_line) 0 [mask_sq_line]
{
    mask_sq_line = replicate (2 * (to_real radius) - 1) 1;
}

gaussmat options sigma min_amp
    = vips_matrix_from_image matrix
{
    matrix = vips_gaussmat options (to_real sigma) (to_real min_amp);
}

logmat options sigma min_amp
    = vips_matrix_from_image matrix
{
    matrix = vips_logmat options (to_real sigma) (to_real min_amp);
}

/* Make a colour from a temperature.
 */
colour_from_temp T
    = error (_ "T out of range"), T < 1667 || T > 25000
    = Colour "YXY" [50, x, y]
{
    // Kim et all approximation
    // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation
    x
        = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 +
            0.8776956 * 10 ** 3 / T + 0.179910, T < 4000
        = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 +
            0.2226347 * 10 ** 3 / T + 0.240390;

    y
        = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 +
            2.18555832 * x - 0.20219638, T < 2222
        = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 +
            2.09137015 * x - 0.16748867, T < 4000
        =  3.0817580 * x ** 3 - 5.87338670 * x ** 2 +
            3.75112997 * x - 0.37001483;
}

temp_from_colour z
    = T
{
    c = colourspace [] Interpretation.YXY (to_colour z);
    x = c.value?1;
    y = c.value?2;

    // McCamy's approximation, see eg.
    // http://en.wikipedia.org/wiki/Color_temperature#Approximation

    xe = 0.332;
    ye = 0.1858;
    n = (x - xe) / (y - ye);
    T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33;
}

