#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/cmdline/include/cmdline.h>

#include <maps/wikimap/mapspro/services/autocart/libs/satellite/include/load_sat_image.h>

#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/geolib/include/bounding_box.h>

#include <opencv2/opencv.hpp>

#include <string>

namespace geolib3 = maps::geolib3;
namespace autocart = maps::wiki::autocart;

int main(int argc, char** argv)
try {
    maps::cmdline::Parser parser;

    auto minLat = parser.real("min_lat")
        .required()
        .help("Minimum latitude value of bbox");

    auto maxLat = parser.real("max_lat")
        .required()
        .help("Maximum latitude value of bbox");

    auto minLon = parser.real("min_lon")
        .required()
        .help("Minimum longitude value of bbox");

    auto maxLon = parser.real("max_lon")
        .required()
        .help("Maximum longitude value of bbox");

    auto tileSourceUrl = parser.string("tile-source")
        .defaultValue(autocart::SAT_TILES_URL)
        .help("url prefix for tile source");

    auto outputPath = parser.string("output")
        .required()
        .help("path to output image");

    auto zoom = parser.num("zoom").required();

    parser.parse(argc, argv);

    geolib3::BoundingBox geodeticBbox({minLon, minLat}, {maxLon, maxLat});
    geolib3::BoundingBox mercatorBbox
        = geolib3::convertGeodeticToMercator(geodeticBbox);

    INFO() << "Loading satellite image:\n"
           << "  geodetic: " << std::fixed << std::setprecision(12)
           << minLat << "," << minLon << "~"
           << maxLat << "," << maxLon << "\n"
           << "  mercator: " << std::fixed << std::setprecision(12)
           << mercatorBbox.minX() << "," << mercatorBbox.minY() << "~"
           << mercatorBbox.maxX() << "," << mercatorBbox.maxY();

    cv::Mat image = autocart::loadSatImage(mercatorBbox, zoom, tileSourceUrl);

    INFO() << "Save image: " << outputPath;
    cv::imwrite(outputPath, image);

    return EXIT_SUCCESS;
}
catch (const maps::Exception& e) {
    INFO() << e;
    return EXIT_FAILURE;
}
catch (const std::exception& e) {
    INFO() << e.what();
    return EXIT_FAILURE;
}
catch (...) {
    INFO() << "Caught unknown exception";
    return EXIT_FAILURE;
}
