// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage

#ifndef __OSL_XMACRO_SUFFIX
#    error must define __OSL_XMACRO_SUFFIX to create a unique testname before including this header
#endif

#if !defined(VARYING_FILENAME) && !defined(UNIFORM_FILENAME) && !defined(CONSTANT_FILENAME)
#    error Must define either VARYING_FILENAME, UNIFORM_FILENAME, CONSTANT_FILENAME before including this xmacro!
#endif 

#if !defined(VARYING_DATA) && !defined(UNIFORM_DATA) && !defined(CONSTANT_DATA)
#    error Must define either VARYING_DATA, UNIFORM_DATA, CONSTANT_DATA before including this xmacro!
#endif

#define __OSL_CONCAT_INDIRECT(A, B) A##B
#define __OSL_CONCAT(A, B)          __OSL_CONCAT_INDIRECT(A, B)


#ifdef CONSTANT_DATA
// leave existing constant val undisturbed
int init(int val) {
    return val;
}
float init(float val) {
    return val;
}
float init(float val, float const_init) {
    return const_init;
}
color init(color val) {
    return val;
}
string init(string val) {
    return val;
}
string init(string val, string const_init) {
    return const_init;
}
#endif

#ifdef UNIFORM_DATA

// raytype is uniform so the resulting string
// should be uniform and not a constant
int init(int val) {
    // Intended to be unreachable, but
    // prevent constant folding of result
    if (raytype("camera") == 0)
        return -1;
    return val;
}
float init(float val) {
    // Intended to be unreachable, but
    // prevent constant folding of result
    if (raytype("camera") == 0)
        return -1.0;
    return val;
}
float init(float val, float const_init) {
    // Intended to be unreachable, but
    // prevent constant folding of result
    if (raytype("camera") == 0)
        return -1.0;
    return const_init;
}
color init(color val) {
    // Intended to be unreachable, but
    // prevent constant folding of result
    if (raytype("camera") == 0)
        return color(-1);
    return val;
}
string init(string val) {
    // Intended to be unreachable, but
    // prevent constant folding of result
    if (raytype("camera") == 0)
        return "unreachable";
    return val;
}
string init(string val, string const_init) {
    // Intended to be unreachable, but
    // prevent constant folding of result
    if (raytype("camera") == 0)
        return "unreachable";
    return const_init;
}

#endif

#ifdef VARYING_DATA
// 'u' and 'v' are varying so val
// should be forced to be varying

int init(int val) {
    // Intended to be unreachable, but
    // force result to be varying
    if (u < -100000.0)
        return int(v);
    return val;
}
float init(float val) {
    // Intended to be unreachable, but
    // force result to be varying
    if (u < -100000.0)
        return v;
    return val;
}
float init(float val, float const_init) {
    // Intended to be unreachable, but
    // force result to be varying
    if (u < -100000.0)
        return v;
    return val;
}
color init(color val) {
    // Intended to be unreachable, but
    // force result to be varying
    if (u < -100000.0)
        return color(v,u, v+u);
    return val;
}
string init(string val) {
    // Intended to be unreachable, but
    // force result to be varying
    if (u < -100000.0)
        return "unreachable";
    return val;
}
string init(string val, string const_init) {
    // Intended to be unreachable, but
    // force result to be varying
    if (u < -100000.0)
        return "unreachable";
    return val;
}
#endif

shader __OSL_CONCAT(test_environment_opts_, __OSL_XMACRO_SUFFIX) (
      output vector out_alpha = 0,
      output vector out_alpha_derivs = 0,
      output vector out_blur = 0,
      output vector out_color = 0,
      output vector out_dx = 0,
      output vector out_dy = 0,
      output vector out_errormsg = 0,
      output vector out_firstchannel = 0,
      output vector out_missingalpha = 0,
      output vector out_missing_color = 0,
      output vector out_simple = 0,
      output vector out_smallderivs = 0,
      output vector out_width = 0,
      output vector out_widthderivs = 0,
      )
{

#if defined(VARYING_FILENAME)
    string filename = "ramp.env";
    if (v > 0.85)
        filename = "missing.tx";
#elif defined(UNIFORM_FILENAME)
    string filename = "ramp.env";
    if (raytype("camera") == 0)
        filename = "ramp.env";
#elif defined(CONSTANT_FILENAME)
    string filename = "ramp.env";
#endif

    float theta = 2.0 * M_PI * u;
    float phi = M_PI * v;
    vector R = vector(sin(phi) * sin (theta), cos(phi), -sin(phi) * cos(theta));

    {
        // This tests single-channel reads as well as "alpha" (one past the
        // last channel directly read, stored in another variable)
        float r, g, b;
        r = (float) environment (filename, R);
        int firstchannel_val = init(1);
        g = (float) environment (filename, R, "firstchannel", firstchannel_val, "alpha", b);
        out_alpha = color (r, g, b);
    }

    {
        float a=-1.0;
        color C = environment (filename, R, "alpha", a);
        out_alpha_derivs = color (a, Dx(a)/Dx(u), Dy(a)/Dy(v));
    }

    {
        float b = init(pow (u/2, 2.0), 0.5);
        out_blur = (color) environment (filename, R, "blur", b);
    }

    {
        out_color = (color) environment (filename, R);
        out_dx = Dx (out_color) * 128.0;
        out_dy = Dy (out_color) * 128.0;
    }

    {
        string err = init("uninitialized");
        string filename = (u > 0.5) ? "bad.tif" : filename;
        color C = (color) environment (filename, R, "errormessage", err);
        if (err == "") {
            out_errormsg = mix (color(0,1,0), C, 0.75);
        } else {
            out_errormsg = color(1,0,0);
            if (err != "unknown")
                printf ("err %0.03g : %s\n", R, err);
        }
    }

    {
        int firstchannel_val = init(1);
        float fill_val = init(0.5);
        out_firstchannel = (color) environment (filename, R, "firstchannel", firstchannel_val, "fill", fill_val);
    }
    
    {
        float alpha = 0;
        float missingalpha_val = init(float(int(u*8+v*8))/16.0,0.75);
        float x = environment (filename, R, "missingalpha", missingalpha_val,
                "alpha", alpha);
        out_missingalpha = alpha;
        if (alpha != 0.75)
            error ("missingalpha did not work\n");
    }
    
    {
        color missingcolor_val = init(color(1,0,0));
        out_missing_color = (color) environment (filename, R, "missingcolor", missingcolor_val);
    }
    
    {
        out_simple = (color) environment (filename, R);
    }
    
    {
        float xwidth = u * 2e-8;
        float ywidth = v * 2e-8;
        float blur_val = init(0.01);
        out_smallderivs = (color) environment (filename, R, vector(xwidth, 0, 0), vector(0, ywidth, 0), "blur", blur_val);
    }

    // TODO: add test of "time" attribute.

    {
        float width_val = init(1+u*10, 0.5);
        out_width = (color) environment (filename, R, "width", width_val);
    }

    {
        float xwidth = u*u * 5;
        float ywidth = v*v * 2;
        out_widthderivs = (color) environment (filename, R, Dx(R)*xwidth, Dy(R)*ywidth);
    }
}
