#include "dpkgparser.h"

#include <sstream>

#include "packagename.h"
#include "runcommandforparsing.h"

namespace NApt {



/** parse a line of dpkg -l and return a pair(packageName, version)
 */
const DpkgParser::PackageInformation parseDpkgLine(const string& line) {

    std::istringstream stream(line);
    std::string word;

    std::string package;
    std::string version;
    std::string architecture;
    std::string description;

    for (int i = 0; i < 5 && stream; ++i) {
        // should not happen, but to improve robustness provide some fallback
        if (!(stream >> word))
            break;
        switch (i) {
        case 0:
            // ignore, should be "ii"
            break;
        case 1: {
            string packageName = PackageName::extractArchitecture(word).name;
            package = packageName;
            break;
        }
        case 2:
            version = word;
            break;
        case 3:
            architecture = word;
            // skip whitespace and put the rest into description
            std::getline(stream >> std::ws, description);
            break;
        }
    }
    return DpkgParser::PackageInformation {
        .name = package,
        .version = version,
        .architecture = architecture,
        .description = description
    };
}

void mergeIntoPackages(
    const DpkgParser::PackageInformation& package,
    map<string, const DpkgParser::PackageInformation>& target
) {
    auto packageIterator = target.find(package.name);
    if (packageIterator == target.end()) {
        target.insert({package.name, package});
    } else if (package.architecture == PackageName::defaultArchitecture()) {
        // replace only, if we have the target architecture

        // c++ speciality: we have to first remove the old, then add the
        // new entry, if the value type is const
        target.erase(packageIterator);
        target.insert({package.name, package});
    }
}

map<string, const DpkgParser::PackageInformation> DpkgParser::getInstalledPackages() {
    map<string, const PackageInformation> packages;

    auto lineProcessor = [&packages](const string& line) {
        auto package = parseDpkgLine(line);
        if (!package.name.empty()) {
            mergeIntoPackages(package, packages);
        }
    };

    NApplication::runCommandForParsing("dpkg -l |grep -E \"^ii\"", lineProcessor);

    return packages;
}

}   // NApt

