#pragma once

#include <algorithm>
#include <cmath>

namespace cb {
  class Vector3D {
    double values[3];

  public:
    Vector3D(double value = 0) : values{value, value, value} {}
    Vector3D(double x, double y, double z) : values{x, y, z} {}

    double &operator[](unsigned index) {return values[index];}
    double operator[](unsigned index) const {return values[index];}
    double x() const {return values[0];}
    double y() const {return values[1];}
    double z() const {return values[2];}

    Vector3D operator+(const Vector3D &other) const {
      return Vector3D(x() + other.x(), y() + other.y(), z() + other.z());
    }
    Vector3D operator-(const Vector3D &other) const {
      return Vector3D(x() - other.x(), y() - other.y(), z() - other.z());
    }
    Vector3D operator*(double scalar) const {
      return Vector3D(x() * scalar, y() * scalar, z() * scalar);
    }
    Vector3D &operator*=(const Vector3D &other) {
      values[0] *= other.x();
      values[1] *= other.y();
      values[2] *= other.z();
      return *this;
    }
    double dot(const Vector3D &other) const {
      return x() * other.x() + y() * other.y() + z() * other.z();
    }
    double distance(const Vector3D &other) const {
      const Vector3D delta = *this - other;
      return std::sqrt(delta.dot(delta));
    }
  };

  class Rectangle3D {
    Vector3D minimum;
    Vector3D maximum;

  public:
    Rectangle3D() = default;
    Rectangle3D(const Vector3D &minimum, const Vector3D &maximum) :
      minimum(minimum), maximum(maximum) {}

    const Vector3D &getMin() const {return minimum;}
    const Vector3D &getMax() const {return maximum;}
  };
}
