// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// ignore_for_file: deprecated_member_use_from_same_package

import 'dart:math' as math;
import 'dart:typed_data';

import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:vector_math/vector_math.dart';
import 'package:vector_math/vector_math_operations.dart';

class MatrixMultiplyBenchmark extends BenchmarkBase {
  MatrixMultiplyBenchmark() : super('MatrixMultiply');
  final A = Float32List(16);
  final B = Float32List(16);
  final C = Float32List(16);

  static void main() {
    MatrixMultiplyBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 200; i++) {
      Matrix44Operations.multiply(C, 0, A, 0, B, 0);
    }
  }
}

class SIMDMatrixMultiplyBenchmark extends BenchmarkBase {
  SIMDMatrixMultiplyBenchmark() : super('SIMDMatrixMultiply');
  final A = Float32x4List(4);
  final B = Float32x4List(4);
  final C = Float32x4List(4);

  static void main() {
    SIMDMatrixMultiplyBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 200; i++) {
      Matrix44SIMDOperations.multiply(C, 0, A, 0, B, 0);
    }
  }
}

class VectorTransformBenchmark extends BenchmarkBase {
  VectorTransformBenchmark() : super('VectorTransform');
  final A = Float32List(16);
  final B = Float32List(4);
  final C = Float32List(4);

  static void main() {
    VectorTransformBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 200; i++) {
      Matrix44Operations.transform4(C, 0, A, 0, B, 0);
    }
  }
}

class SIMDVectorTransformBenchmark extends BenchmarkBase {
  SIMDVectorTransformBenchmark() : super('SIMDVectorTransform');
  final A = Float32x4List(4);
  final B = Float32x4List(1);
  final C = Float32x4List(1);

  static void main() {
    SIMDVectorTransformBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 200; i++) {
      Matrix44SIMDOperations.transform4(C, 0, A, 0, B, 0);
    }
  }
}

class ViewMatrixBenchmark extends BenchmarkBase {
  ViewMatrixBenchmark() : super('setViewMatrix');

  final M = Matrix4.zero();
  final P = Vector3.zero();
  final F = Vector3.zero();
  final U = Vector3.zero();

  static void main() {
    ViewMatrixBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      setViewMatrix(M, P, F, U);
    }
  }
}

class Aabb2TransformBenchmark extends BenchmarkBase {
  Aabb2TransformBenchmark() : super('aabb2Transform');

  static final M = Matrix3.rotationZ(math.pi / 4);
  static final P1 = Vector2(10.0, 10.0);
  static final P2 = Vector2(20.0, 30.0);
  static final P3 = Vector2(100.0, 50.0);
  static final B1 = Aabb2.minMax(P1, P2);
  static final B2 = Aabb2.minMax(P1, P3);
  static final B3 = Aabb2.minMax(P2, P3);
  static final temp = Aabb2();

  static void main() {
    Aabb2TransformBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.copyFrom(B1);
      temp.transform(M);
      temp.copyFrom(B2);
      temp.transform(M);
      temp.copyFrom(B3);
      temp.transform(M);
    }
  }
}

class Aabb2RotateBenchmark extends BenchmarkBase {
  Aabb2RotateBenchmark() : super('aabb2Rotate');

  static final M = Matrix3.rotationZ(math.pi / 4);
  static final P1 = Vector2(10.0, 10.0);
  static final P2 = Vector2(20.0, 30.0);
  static final P3 = Vector2(100.0, 50.0);
  static final B1 = Aabb2.minMax(P1, P2);
  static final B2 = Aabb2.minMax(P1, P3);
  static final B3 = Aabb2.minMax(P2, P3);
  static final temp = Aabb2();

  static void main() {
    Aabb2RotateBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.copyFrom(B1);
      temp.rotate(M);
      temp.copyFrom(B2);
      temp.rotate(M);
      temp.copyFrom(B3);
      temp.rotate(M);
    }
  }
}

class Aabb3TransformBenchmark extends BenchmarkBase {
  Aabb3TransformBenchmark() : super('aabb3Transform');

  static final M = Matrix4.rotationZ(math.pi / 4);
  static final P1 = Vector3(10.0, 10.0, 0.0);
  static final P2 = Vector3(20.0, 30.0, 1.0);
  static final P3 = Vector3(100.0, 50.0, 10.0);
  static final B1 = Aabb3.minMax(P1, P2);
  static final B2 = Aabb3.minMax(P1, P3);
  static final B3 = Aabb3.minMax(P2, P3);
  static final temp = Aabb3();

  static void main() {
    Aabb3TransformBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.copyFrom(B1);
      temp.transform(M);
      temp.copyFrom(B2);
      temp.transform(M);
      temp.copyFrom(B3);
      temp.transform(M);
    }
  }
}

class Aabb3RotateBenchmark extends BenchmarkBase {
  Aabb3RotateBenchmark() : super('aabb3Rotate');

  static final M = Matrix4.rotationZ(math.pi / 4);
  static final P1 = Vector3(10.0, 10.0, 0.0);
  static final P2 = Vector3(20.0, 30.0, 1.0);
  static final P3 = Vector3(100.0, 50.0, 10.0);
  static final B1 = Aabb3.minMax(P1, P2);
  static final B2 = Aabb3.minMax(P1, P3);
  static final B3 = Aabb3.minMax(P2, P3);
  static final temp = Aabb3();

  static void main() {
    Aabb3RotateBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.copyFrom(B1);
      temp.rotate(M);
      temp.copyFrom(B2);
      temp.rotate(M);
      temp.copyFrom(B3);
      temp.rotate(M);
    }
  }
}

class Matrix3DeterminantBenchmark extends BenchmarkBase {
  Matrix3DeterminantBenchmark() : super('Matrix3.determinant');

  final MX = Matrix3.rotationX(math.pi / 4);
  final MY = Matrix3.rotationY(math.pi / 4);
  final MZ = Matrix3.rotationZ(math.pi / 4);

  static void main() {
    Matrix3DeterminantBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 800; i++) {
      MX.determinant();
      MY.determinant();
      MZ.determinant();
    }
  }
}

class Matrix3TransformVector3Benchmark extends BenchmarkBase {
  Matrix3TransformVector3Benchmark() : super('Matrix3.transform(Vector3)');

  final MX = Matrix3.rotationX(math.pi / 4);
  final MY = Matrix3.rotationY(math.pi / 4);
  final MZ = Matrix3.rotationZ(math.pi / 4);
  final V1 = Vector3(10.0, 20.0, 1.0);
  final V2 = Vector3(-10.0, 20.0, 1.0);
  final V3 = Vector3(10.0, -20.0, 1.0);

  static void main() {
    Matrix3TransformVector3Benchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 800; i++) {
      MX.transform(V1);
      MX.transform(V2);
      MX.transform(V3);
      MY.transform(V1);
      MY.transform(V2);
      MY.transform(V3);
      MZ.transform(V1);
      MZ.transform(V2);
      MZ.transform(V3);
    }
  }
}

class Matrix3TransformVector2Benchmark extends BenchmarkBase {
  Matrix3TransformVector2Benchmark() : super('Matrix3.transform(Vector2)');

  final MX = Matrix3.rotationX(math.pi / 4);
  final MY = Matrix3.rotationY(math.pi / 4);
  final MZ = Matrix3.rotationZ(math.pi / 4);
  final V1 = Vector2(10.0, 20.0);
  final V2 = Vector2(-10.0, 20.0);
  final V3 = Vector2(10.0, -20.0);

  static void main() {
    Matrix3TransformVector2Benchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 800; i++) {
      MX.transform2(V1);
      MX.transform2(V2);
      MX.transform2(V3);
      MY.transform2(V1);
      MY.transform2(V2);
      MY.transform2(V3);
      MZ.transform2(V1);
      MZ.transform2(V2);
      MZ.transform2(V3);
    }
  }
}

class Matrix3TransposeMultiplyBenchmark extends BenchmarkBase {
  Matrix3TransposeMultiplyBenchmark() : super('Matrix3.transposeMultiply');

  final MX = Matrix3.rotationX(math.pi / 4);
  final MY = Matrix3.rotationY(math.pi / 4);
  final MZ = Matrix3.rotationZ(math.pi / 4);
  final temp = Matrix3.zero();

  static void main() {
    Matrix3TransposeMultiplyBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.setIdentity();
      temp.transposeMultiply(MX);
      temp.transposeMultiply(MY);
      temp.transposeMultiply(MZ);
    }
  }
}

class Matrix4TranslateByDoubleGenericBenchmark extends BenchmarkBase {
  Matrix4TranslateByDoubleGenericBenchmark()
      : super('Matrix4.translateByDoubleGeneric');

  final temp = Matrix4.zero()..setIdentity();

  static void main() {
    Matrix4TranslateByDoubleGenericBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.translate(10.0, 20.0, 30.0);
    }
  }
}

class Matrix4TranslateByVector3GenericBenchmark extends BenchmarkBase {
  Matrix4TranslateByVector3GenericBenchmark()
      : super('Matrix4.translateByVector3Generic');

  final temp = Matrix4.zero()..setIdentity();
  final vec = Vector3(10.0, 20.0, 30.0);

  static void main() {
    Matrix4TranslateByVector3GenericBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.translate(vec);
    }
  }
}

class Matrix4TranslateByVector4GenericBenchmark extends BenchmarkBase {
  Matrix4TranslateByVector4GenericBenchmark()
      : super('Matrix4.translateByVector4Generic');

  final temp = Matrix4.zero()..setIdentity();
  final vec = Vector4(10.0, 20.0, 30.0, 40.0);

  static void main() {
    Matrix4TranslateByVector4GenericBenchmark().report();
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.translate(vec);
    }
  }
}

class Matrix4TranslateByDoubleBenchmark extends BenchmarkBase {
  Matrix4TranslateByDoubleBenchmark() : super('Matrix4.translateByDouble');

  final temp = Matrix4.zero()..setIdentity();

  static void main() {
    Matrix4TranslateByDoubleBenchmark().report();
  }

  // Call the benchmarked method with random arguments to make sure TFA won't
  // specialize it based on the arguments passed and wasm-opt won't inline it,
  // for fair comparison with the generic case.
  @override
  void setup() {
    for (var i = 0; i < 10; i++) {
      temp.translateByDouble(
          i.toDouble(), (i * 10).toDouble(), (i * 5).toDouble(), 1.0);
    }
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.translateByDouble(10.0, 20.0, 30.0, 1.0);
    }
  }
}

class Matrix4TranslateByVector3Benchmark extends BenchmarkBase {
  Matrix4TranslateByVector3Benchmark() : super('Matrix4.translateByVector3');

  final temp = Matrix4.zero()..setIdentity();
  final vec = Vector3(10.0, 20.0, 30.0);

  static void main() {
    Matrix4TranslateByVector3Benchmark().report();
  }

  // Call the benchmarked method with random arguments to make sure TFA won't
  // specialize it based on the arguments passed and wasm-opt won't inline it,
  // for fair comparison with the generic case.
  @override
  void setup() {
    for (var i = 0; i < 10; i++) {
      temp.translateByVector3(
          Vector3(i.toDouble(), (i * 10).toDouble(), (i * 5).toDouble()));
    }
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.translateByVector3(vec);
    }
  }
}

class Matrix4TranslateByVector4Benchmark extends BenchmarkBase {
  Matrix4TranslateByVector4Benchmark() : super('Matrix4.translateByVector4');

  final temp = Matrix4.zero()..setIdentity();
  final vec = Vector4(10.0, 20.0, 30.0, 40.0);

  static void main() {
    Matrix4TranslateByVector4Benchmark().report();
  }

  // Call the benchmarked method with random arguments to make sure TFA won't
  // specialize it based on the arguments passed and wasm-opt won't inline it,
  // for fair comparison with the generic case.
  @override
  void setup() {
    for (var i = 0; i < 10; i++) {
      temp.translateByVector4(Vector4(i.toDouble(), (i * 10).toDouble(),
          (i * 5).toDouble(), (i * 20).toDouble()));
    }
  }

  @override
  void run() {
    for (var i = 0; i < 100; i++) {
      temp.translateByVector4(vec);
    }
  }
}

void main() {
  MatrixMultiplyBenchmark.main();
  SIMDMatrixMultiplyBenchmark.main();
  VectorTransformBenchmark.main();
  SIMDVectorTransformBenchmark.main();
  ViewMatrixBenchmark.main();
  Aabb2TransformBenchmark.main();
  Aabb2RotateBenchmark.main();
  Aabb3TransformBenchmark.main();
  Aabb3RotateBenchmark.main();
  Matrix3DeterminantBenchmark.main();
  Matrix3TransformVector3Benchmark.main();
  Matrix3TransformVector2Benchmark.main();
  Matrix3TransposeMultiplyBenchmark.main();
  Matrix4TranslateByDoubleGenericBenchmark.main();
  Matrix4TranslateByVector3GenericBenchmark.main();
  Matrix4TranslateByVector4GenericBenchmark.main();
  Matrix4TranslateByDoubleBenchmark.main();
  Matrix4TranslateByVector3Benchmark.main();
  Matrix4TranslateByVector4Benchmark.main();
}
