#pragma once

#include <glm/glm.hpp>

namespace glm {
    typedef vec<3, long long> llvec3;
    typedef vec<3, long> lvec3;
    typedef vec<3, uint16_t> usvec3;
    typedef vec<3, unsigned char> ucvec3;

    const auto IDX_LENGTH = 32;
    const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH;
    const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH;
    using idx = glm::u16;

    /// Combination of ivec3 and vec3 as a big and high precision
    /// Scale to IDX_LENGTH2
    struct ifvec3 {
        using raw_t = glm::ivec3;
        using offset_t = glm::vec3;

        ifvec3(): raw(0), offset(0) { }
        ifvec3(const raw_t &raw, const offset_t &offset, bool recenter = true) : raw(raw), offset(offset) {
            if(recenter) center();
        }
        ifvec3(const glm::llvec3 &pos, glm::u32 density = 1);

        raw_t raw;
        offset_t offset;

        glm::llvec3 as_voxel(int density = 1) const;
        void center();
        glm::llvec3 raw_as_long() const;
        double dist(const ifvec3 &p) const;

        ifvec3 divide(glm::u32 m = IDX_LENGTH) const;

        inline const ifvec3 &operator+=(const offset_t &v) {
            offset += v;
            center();
            return *this;
        }
        inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
        inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); }
        inline ifvec3 operator-(const ifvec3 &v) const { return ifvec3(raw - v.raw, offset - v.offset); }
        inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
        inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
    };
}
