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

#include <maps/libs/json/include/value.h>
#include <maps/libs/json/include/builder.h>

#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/objects/include/building.h>

#include <string>
#include <fstream>
#include <iomanip>

using namespace maps::wiki::autocart::pipeline;

namespace json = maps::json;

namespace {

std::string getFTTypeIdToRussianNameJSMap() {
    std::stringstream ss;
    ss << "{";
    for (size_t i = 0; i < ALL_FT_TYPE_ID.size(); i++) {
        const FTTypeId& ftTypeId = ALL_FT_TYPE_ID[i];
        ss << encodeFTTypeId(ftTypeId) << " : '" + ruNameFTTypeId(ftTypeId) + "'";
        if (i != ALL_FT_TYPE_ID.size() - 1) {
            ss << ", ";
        };
    }
    ss << "}";
    return ss.str();
}

const std::string HTML_TEMPLATE_BEGIN = R"(
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
    <title>Building viewer</title>
    <script type="text/javascript">
      ft_type_id_to_ru_name = )" + getFTTypeIdToRussianNameJSMap()
      + R"(
      blds = )";

const std::string HTML_TEMPLATE_END = R"(

      function displayContents(pageNum) {
        maxCount = 10
        SAT_URL = 'https://static-maps.yandex.ru/1.x/?l=sat&pl=c:7FFF00,w:4,'
        MAP_URL = 'https://static-maps.yandex.ru/1.x/?l=map&pl=c:7FFF00,w:4,'
        pageDataHTML = '';
        pageDataHTML += '<table border=1>';
        for (i = pageNum * maxCount; i < Math.min((pageNum + 1) * maxCount, blds.length); i++) {
          polygon = blds[i]['shape']
          polygonStr = ''
          for (j = 0; j < polygon.length; j++) {
            polygonStr += polygon[j] + ",";
          }
          polygonStr += polygon[0];
          pageDataHTML += '<tr>';
          pageDataHTML += '  <th>';
          pageDataHTML += '    <span>' + (i + 1) + '</span>';
          pageDataHTML += '    <br>';
          if ('bld_id' in blds[i]) {
              bld_id = blds[i]['bld_id'];
              pageDataHTML += '    <span>bld_id: ' + bld_id + '</span>';
              pageDataHTML += '    <br>'
          }
          if ('height' in blds[i]) {
              height = blds[i]['height'];
              pageDataHTML += '    <span>height: ' + height + '</span>';
              pageDataHTML += '    <br>'
          }
          if ('ft_type_id' in blds[i]) {
              ft_type_id = blds[i]['ft_type_id'];
              ft_type_ru_name = 'unknown';
              if (ft_type_id in ft_type_id_to_ru_name) {
                  ft_type_ru_name = ft_type_id_to_ru_name[ft_type_id];
              }
              pageDataHTML += '    <span>ft_type_id: ' + ft_type_ru_name + ' (' + ft_type_id + ')' + '</span>';
              pageDataHTML += '    <br>'
          }
          pageDataHTML += '    <img src=' + SAT_URL + polygonStr + '>';
          pageDataHTML += '    <img src=' + MAP_URL + polygonStr + '>';
          pageDataHTML += '  </th>';
          pageDataHTML += '</tr>';
        }
        pageDataHTML += '</table>';
        document.getElementById('data').innerHTML = pageDataHTML;

        pageControlsHTML = '';
        if (pageNum > 0) {
          prevPageNum = pageNum - 1;
          pageControlsHTML += '<input type="button" value="Prev" onclick=displayContents(' + (prevPageNum).toString() + ')>';
        }
        pageControlsHTML += '<span>Page:' + (pageNum).toString() + '</span>'
        if ((pageNum + 1) * maxCount < blds.length) {
          nextPageNum = pageNum + 1;
          pageControlsHTML += '<input type="button" value="Next" onclick=displayContents(' + (nextPageNum).toString() + ')>';
        }
        document.getElementById('top-controls').innerHTML = pageControlsHTML;
        document.getElementById('bottom-controls').innerHTML = pageControlsHTML;
      }
    </script>
  </head>
  <body onload='displayContents(0)'>
    <div id='top-controls'></div>
    <br>
    <div id='data'></div>
    <br>
    <div id='bottom-controls'></div>
  </body>
</html>
)";

std::string makeBldsJSArray(const std::string& inputJsonPath) {
    std::ifstream ifs(inputJsonPath);
    REQUIRE(ifs.is_open(), "Failed to open file: " + inputJsonPath);
    json::Value inputJson = json::Value::fromStream(ifs);

    std::stringstream ss;
    json::Builder builder(ss);
    builder.setDoublePrecision(6);
    builder << [&](json::ArrayBuilder b) {
        for (size_t i = 0; i < inputJson.size(); i++) {
            Building bld = Building::fromJson(inputJson[i]);
            b << [&](json::ObjectBuilder b) {
                bld.toJson(b);
            };
        }
    };
    return ss.str();
}

} // namespace

int main(int argc, const char** argv)
try {
    maps::cmdline::Parser parser("Generate building viewer");

    auto inputJsonPath = parser.string("input_json")
        .required()
        .help("Path to input json file");

    auto outputHTMLViewer = parser.string("output_html")
        .required()
        .help("Path to output HTML viewer");

    parser.parse(argc, const_cast<char**>(argv));

    INFO() << "Loading data from json: " << inputJsonPath;
    std::string bldsJSArray = makeBldsJSArray(inputJsonPath);

    std::ofstream ofs(outputHTMLViewer);
    REQUIRE(ofs.is_open(), "Failed to open file: " + outputHTMLViewer);
    ofs << HTML_TEMPLATE_BEGIN + bldsJSArray + HTML_TEMPLATE_END;
    ofs.close();

    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;
}
