#ifndef NATIVE_TYPE_TEST_IMPL_H
#define NATIVE_TYPE_TEST_IMPL_H

#include <vector>

namespace Native {
struct Vector3D {
  float x;
  float y;
  float z;

  Vector3D() {
    x = 0;
    y = 0;
    z = 0;
  }
  Vector3D(float _x, float _y, float _z) {
    this->x = _x;
    this->y = _y;
    this->z = _z;
  }

  bool operator==(const Vector3D& other) const {
    return (x == other.x) && (y == other.y) && (z == other.z);
  }
};

struct Matrix {
  int rows;
  int columns;
  std::vector<float> values;

  Matrix() : Matrix(0, 0) {}

  Matrix(int _rows, int _columns) {
    this->rows = _rows;
    this->columns = _columns;
    values.resize(_rows * _columns);
  }

  bool operator==(const Matrix& other) const {
    return (rows == other.rows) && (columns == other.columns) &&
           (values == other.values);
  }
};

// Phantom tag types used purely to select a template specialization; they
// carry no data of their own.
struct TagA {};
struct TagB {};

// A native type template, instantiated per-field in the schema via
// `native_type_template_arg` rather than needing one flatbuffers struct
// declaration per specialization.
template <typename T>
struct Tagged {
  int32_t value;

  Tagged() : value(0) {}
  explicit Tagged(int32_t _value) : value(_value) {}

  bool operator==(const Tagged& other) const { return value == other.value; }
};

// A native type template taking more than one argument.
template <typename T0, typename T1>
struct Pair {
  int32_t value;

  Pair() : value(0) {}
  explicit Pair(int32_t _value) : value(_value) {}

  bool operator==(const Pair& other) const { return value == other.value; }
};
}  // namespace Native

namespace Geometry {
struct Vector3D;
struct Vector3DAlt;
struct Tagged;
struct Pair;
}  // namespace Geometry

namespace flatbuffers {
Geometry::Vector3D Pack(const Native::Vector3D& obj);
const Native::Vector3D UnPack(const Geometry::Vector3D& obj);
Geometry::Vector3DAlt PackVector3DAlt(const Native::Vector3D& obj);
const Native::Vector3D UnPackVector3DAlt(const Geometry::Vector3DAlt& obj);

Geometry::Tagged PackTaggedTagA(const Native::Tagged<Native::TagA>& obj);
const Native::Tagged<Native::TagA> UnPackTaggedTagA(const Geometry::Tagged& obj);
Geometry::Tagged PackTaggedTagB(const Native::Tagged<Native::TagB>& obj);
const Native::Tagged<Native::TagB> UnPackTaggedTagB(const Geometry::Tagged& obj);

Geometry::Pair PackPairTagATagB(
    const Native::Pair<Native::TagA, Native::TagB>& obj);
const Native::Pair<Native::TagA, Native::TagB> UnPackPairTagATagB(
    const Geometry::Pair& obj);
}  // namespace flatbuffers

#endif  // VECTOR3D_PACK_H
