// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkNew.h"
#include "vtkOpenGLUniforms.h"

#include <algorithm>
#include <iostream>
#include <vector>

int UnitTestOpenGLUniforms(int, char*[])
{
  vtkNew<vtkOpenGLUniforms> uni;

  // uniform i
  int ini = 1;
  int outi = 0;
  uni->SetUniformi("i", ini);
  uni->GetUniformi("i", outi);
  if (outi != ini)
  {
    return EXIT_FAILURE;
  }

  // Test generic get functions
  std::vector<int> vi;
  if (!uni->GetUniform("i", vi))
  {
    std::cerr << "<1>" << std::endl;
    return EXIT_FAILURE;
  }
  if (vi[0] != ini)
  {
    std::cerr << "<2>" << std::endl;
    return EXIT_FAILURE;
  }
  std::vector<float> vf;
  if (uni->GetUniform("i", vf))
  {
    std::cerr << "<3>" << std::endl;
    return EXIT_FAILURE;
  }

  // uniform f
  float inf = 1.0f;
  float outf = 0.0f;
  uni->SetUniformf("f", inf);
  uni->GetUniformf("f", outf);
  if (outf != inf)
  {
    return EXIT_FAILURE;
  }

  // Test generic get functions
  if (!uni->GetUniform("f", vf))
  {
    std::cerr << "<4>" << std::endl;
    return EXIT_FAILURE;
  }
  if (vf.size() != 1 && vf[0] != inf)
  {
    std::cerr << "<5>" << std::endl;
    return EXIT_FAILURE;
  }
  if (uni->GetUniform("f", vi))
  {
    std::cerr << "<6>" << std::endl;
    return EXIT_FAILURE;
  }

  // uniform 2i
  int in2i[2] = { 1, 2 };
  int out2i[2] = { 0, 0 };
  uni->SetUniform2i("2i", in2i);
  uni->GetUniform2i("2i", out2i);
  if (!std::equal(in2i, in2i + 2, out2i))
  {
    return EXIT_FAILURE;
  }

  // uniform 2f
  float in2f[2] = { 1.0f, 2.0f };
  float out2f[2] = { 0.0f, 0.0f };
  uni->SetUniform2f("2f", in2f);
  uni->GetUniform2f("2f", out2f);
  if (!std::equal(in2f, in2f + 2, out2f))
  {
    return EXIT_FAILURE;
  }

  // uniform 3f
  float in3f[3] = { 1.0f, 2.0f, 3.0f };
  float out3f[3] = { 0.0f, 0.0f, 0.0f };
  uni->SetUniform3f("3f", in3f);
  uni->GetUniform3f("3f", out3f);
  if (!std::equal(in3f, in3f + 3, out3f))
  {
    return EXIT_FAILURE;
  }

  // uniform 4f
  float in4f[4] = { 1.0f, 2.0f, 3.0f };
  float out4f[4] = { 0.0f, 0.0f, 0.0f };
  uni->SetUniform4f("4f", in4f);
  uni->GetUniform4f("4f", out4f);
  if (!std::equal(in4f, in4f + 4, out4f))
  {
    return EXIT_FAILURE;
  }

  // uniform Mat3x3
  float inMat3x3[9] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
  float outMat3x3[9];
  std::fill(outMat3x3, outMat3x3 + 9, 0.0f);
  uni->SetUniformMatrix3x3("Mat3x3f", inMat3x3);
  uni->GetUniformMatrix3x3("Mat3x3f", outMat3x3);
  if (!std::equal(inMat3x3, inMat3x3 + 9, outMat3x3))
  {
    return EXIT_FAILURE;
  }

  // uniform Mat4x4
  float inMat4x4[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f };
  float outMat4x4[16];
  std::fill(outMat4x4, outMat4x4 + 16, 0.0f);
  uni->SetUniformMatrix4x4("Mat4x4f", inMat4x4);
  uni->GetUniformMatrix4x4("Mat4x4f", outMat4x4);
  if (!std::equal(inMat4x4, inMat4x4 + 16, outMat4x4))
  {
    return EXIT_FAILURE;
  }

  // uniform 1iv
  int in1iv[2] = { 1, 2 };
  std::vector<int> out1iv;
  uni->SetUniform1iv("1iv", 2, in1iv);
  uni->GetUniform1iv("1iv", out1iv);
  if (!std::equal(in1iv, in1iv + 2, out1iv.begin()))
  {
    return EXIT_FAILURE;
  }

  // uniform 1fv
  float in1fv[2] = { 1.0f, 2.0f };
  std::vector<float> out1fv;
  uni->SetUniform1fv("1fv", 2, in1fv);
  uni->GetUniform1fv("1fv", out1fv);
  if (!std::equal(in1fv, in1fv + 2, out1fv.begin()))
  {
    return EXIT_FAILURE;
  }

  // uniform 2fv
  float in2fv[2][2] = { { 1.0f, 2.0f }, { 3.0f, 4.0f } };
  std::vector<float> out2fv;
  uni->SetUniform2fv("2fv", 2, in2fv);
  uni->GetUniform2fv("2fv", out2fv);
  float* in2fvArray = reinterpret_cast<float*>(in2fv);
  if (!std::equal(in2fvArray, in2fvArray + 4, out2fv.begin()))
  {
    return EXIT_FAILURE;
  }

  // uniform 3fv
  float in3fv[2][3] = { { 1.0f, 2.0f, 3.0f }, { 4.0f, 5.0f, 6.0f } };
  std::vector<float> out3fv;
  uni->SetUniform3fv("3fv", 2, in3fv);
  uni->GetUniform3fv("3fv", out3fv);
  float* in3fvArray = reinterpret_cast<float*>(in3fv);
  if (!std::equal(in3fvArray, in3fvArray + 6, out3fv.begin()))
  {
    return EXIT_FAILURE;
  }

  // uniform 4fv
  float in4fv[2][4] = { { 1.0f, 2.0f, 3.0f, 4.0f }, { 5.0f, 6.0f, 7.0f, 8.0f } };
  std::vector<float> out4fv;
  uni->SetUniform4fv("4fv", 2, in4fv);
  uni->GetUniform4fv("4fv", out4fv);
  float* in4fvArray = reinterpret_cast<float*>(in4fv);
  if (!std::equal(in4fvArray, in4fvArray + 8, out4fv.begin()))
  {
    return EXIT_FAILURE;
  }

  // uniform Matrix4x4v
  float inMat4x4v[32] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 2.0f };
  std::vector<float> outMat4x4v;
  uni->SetUniformMatrix4x4v("Mat4x4v", 2, inMat4x4v);
  uni->GetUniformMatrix4x4v("Mat4x4v", outMat4x4v);
  if (!std::equal(inMat4x4v, inMat4x4v + 32, outMat4x4v.begin()))
  {
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
