/*
 * Souffle - A Datalog Compiler
 * Copyright (c) 2024, The Souffle Developers. All rights reserved
 * Licensed under the Universal Permissive License v 1.0 as shown at:
 * - https://opensource.org/licenses/UPL
 * - <souffle root>/licenses/SOUFFLE-UPL.txt
 */
#include "GraphUtils.h"
#include "souffle/utility/FileUtil.h"

#include <iostream>
#include <string>
#include <vector>

namespace souffle {

std::string toBase64(const std::string& data) {
    static const std::vector<char> table = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
            'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
    std::string result;
    std::string tmp = data;
    unsigned int padding = 0;
    if (data.size() % 3 == 2) {
        padding = 1;
    } else if (data.size() % 3 == 1) {
        padding = 2;
    }

    for (unsigned int i = 0; i < padding; i++) {
        tmp.push_back(0);
    }
    for (unsigned int i = 0; i < tmp.size(); i += 3) {
        auto c1 = static_cast<unsigned char>(tmp[i]);
        auto c2 = static_cast<unsigned char>(tmp[i + 1]);
        auto c3 = static_cast<unsigned char>(tmp[i + 2]);
        unsigned char index1 = c1 >> 2;
        unsigned char index2 = ((c1 & 0x03) << 4) | (c2 >> 4);
        unsigned char index3 = ((c2 & 0x0F) << 2) | (c3 >> 6);
        unsigned char index4 = c3 & 0x3F;

        result.push_back(table[index1]);
        result.push_back(table[index2]);
        result.push_back(table[index3]);
        result.push_back(table[index4]);
    }
    if (padding == 1) {
        result[result.size() - 1] = '=';
    } else if (padding == 2) {
        result[result.size() - 1] = '=';
        result[result.size() - 2] = '=';
    }
    return result;
}

std::string convertDotToSVG(const std::string& dotSpec) {
    // Check if dot is present
    std::string cmd = which("dot");
    if (!isExecutable(cmd)) {
        return "";
    }

    if (dotSpec.size() > 50000) {
        std::cerr << "skip graph with DOT spec length: " << dotSpec.size() << "\n";
        return "";
    }

    TempFileStream dotFile;
    dotFile << dotSpec;
    dotFile.flush();
    return execStdOut("dot -Tsvg < " + dotFile.getFileName()).str();
}

void printHTMLGraph(std::ostream& out, const std::string& dotSpec, const std::string& id) {
    std::string data = convertDotToSVG(dotSpec);

    if (data.find("<svg") != std::string::npos) {
        out << "<img alt='graph image' src='data:image/svg+xml;base64," << toBase64(data) << "'><br/>\n";
    } else {
        out << "<div class='" << id << "-source"
            << "'>\n<pre>" << dotSpec << "</pre>\n";
        out << "</div>\n";
    }
}

}  // namespace souffle
