#pragma once

#include "common.h"

#include <maps/libs/chrono/include/time_point.h>
#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/sql_chemistry/include/gateway_access.h>

#include <maps/libs/geolib/include/point.h>
#include <maps/libs/geolib/include/units.h>

#include <string>

namespace maps::mrc::db {

class Panorama {
public:
    Panorama(const std::string& mdsKey,
             const std::string& mdsSrcKey,
             chrono::TimePoint date,
             std::uint64_t sessionId,
             std::uint32_t orderNum,
             geolib3::Point2 geodeticPos,
             float vehicleCourseDeg,
             float horizontalAngleDeg,
             float verticalAngleDeg,
             std::uint32_t tileWidth,
             std::uint32_t tileHeight,
             std::uint32_t totalWidth,
             std::uint32_t totalHeight,
             std::uint16_t zoomLevel);

    TId panoramaId() const { return panoramaId_; }

    const std::string& mdsKey() const { return mdsKey_; }

    const std::string& mdsSrcKey() const { return mdsSrcKey_; }

    chrono::TimePoint date() const { return date_; }

    // ID of a shooting session a panorama was taken in
    std::uint64_t sessionId() const { return sessionId_; }

    // A panorama order number within its shooting session
    std::uint32_t orderNum() const { return orderNum_; }

    const geolib3::Point2& geodeticPos() const { return geodeticPos_; }

    geolib3::Point2 mercatorPos() const
    {
        return geolib3::convertGeodeticToMercator(geodeticPos());
    }

    // Azimuth a shooting vehicle is directed to
    float vehicleCourseDeg() const { return vehicleCourseDeg_; }

    // Azimuth of a panorama projection's left edge
    float horizontalAngleDeg() const { return horizontalAngleDeg_; }

    // Angle from a central line splitting panorama projection in half and the
    // true horizon. Positive direction is upward and negative is downward.
    float verticalAngleDeg() const { return verticalAngleDeg_; }

    std::uint32_t tileWidth() const { return tileWidth_; }

    std::uint32_t tileHeight() const { return tileHeight_; }

    std::uint32_t totalWidth() const { return totalWidth_; }

    std::uint32_t totalHeight() const { return totalHeight_; }

    std::uint16_t zoomLevel() const { return zoomLevel_; }

private:
    friend class sql_chemistry::GatewayAccess<Panorama>;

    Panorama() = default;

    template <typename T>
    static auto introspect(T& t)
    {
        return std::tie(t.panoramaId_, t.mdsKey_, t.mdsSrcKey_, t.date_,
            t.sessionId_, t.orderNum_, t.geodeticPos_, t.vehicleCourseDeg_,
            t.horizontalAngleDeg_, t.verticalAngleDeg_, t.tileWidth_,
            t.tileHeight_, t.totalWidth_, t.totalHeight_, t.zoomLevel_);
    }

    TId panoramaId_{0};
    std::string mdsKey_{};
    std::string mdsSrcKey_{};
    chrono::TimePoint date_{};
    std::uint64_t sessionId_{};
    std::uint32_t orderNum_{};
    geolib3::Point2 geodeticPos_{};
    float vehicleCourseDeg_{};
    float horizontalAngleDeg_{};
    float verticalAngleDeg_{};
    std::uint32_t tileWidth_{};
    std::uint32_t tileHeight_{};
    std::uint32_t totalWidth_{};
    std::uint32_t totalHeight_{};
    std::uint16_t zoomLevel_{};

public:
    auto introspect() const { return introspect(*this); }
};

using Panoramas = std::vector<Panorama>;

} // namespace maps::mrc::db
