"""
example::

    send data
    namespace    communication
    author       william.ballenthin@fireeye.com
    description  all known techniques for sending data to a potential C2 server
    scope        function
    examples     BFB9B5391A13D0AFD787E87AB90F14F5:0x13145D60
    matches      0x10004363
                 0x100046c9
                 0x1000454e
                 0x10003a13
                 0x10003415
                 0x10003797

Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
"""
import tabulate

import capa.rules
import capa.render.utils as rutils


def render_meta(ostream, doc):
    """
    like:

        md5                  84882c9d43e23d63b82004fae74ebb61
        sha1                 c6fb3b50d946bec6f391aefa4e54478cf8607211
        sha256               5eced7367ed63354b4ed5c556e2363514293f614c2c2eb187273381b2ef5f0f9
        path                 /tmp/suspicious.dll_
        timestamp            2020-07-03T10:17:05.796933
        capa version         0.0.0
        format               auto
        extractor            VivisectFeatureExtractor
        base address         0x10000000
        rules                (embedded rules)
        function count       42
        total feature count  1918
    """
    rows = [
        ("md5", doc["meta"]["sample"]["md5"]),
        ("sha1", doc["meta"]["sample"]["sha1"]),
        ("sha256", doc["meta"]["sample"]["sha256"]),
        ("path", doc["meta"]["sample"]["path"]),
        ("timestamp", doc["meta"]["timestamp"]),
        ("capa version", doc["meta"]["version"]),
        ("format", doc["meta"]["analysis"]["format"]),
        ("extractor", doc["meta"]["analysis"]["extractor"]),
        ("base address", hex(doc["meta"]["analysis"]["base_address"])),
        ("rules", doc["meta"]["analysis"]["rules"]),
        ("function count", len(doc["meta"]["analysis"]["feature_counts"]["functions"])),
        (
            "total feature count",
            doc["meta"]["analysis"]["feature_counts"]["file"]
            + sum(doc["meta"]["analysis"]["feature_counts"]["functions"].values()),
        ),
    ]
    ostream.writeln(tabulate.tabulate(rows, tablefmt="plain"))


def render_rules(ostream, doc):
    """
    like:

        receive data (2 matches)
        namespace    communication
        description  all known techniques for receiving data from a potential C2 server
        scope        function
        matches      0x10003A13
                     0x10003797
    """
    had_match = False
    for rule in rutils.capability_rules(doc):
        count = len(rule["matches"])
        if count == 1:
            capability = rutils.bold(rule["meta"]["name"])
        else:
            capability = "%s (%d matches)" % (rutils.bold(rule["meta"]["name"]), count)

        ostream.writeln(capability)
        had_match = True

        rows = []
        for key in ("namespace", "description", "scope"):
            if key == "name" or key not in rule["meta"]:
                continue

            v = rule["meta"][key]
            if isinstance(v, list) and len(v) == 1:
                v = v[0]
            rows.append((key, v))

        if rule["meta"]["scope"] != capa.rules.FILE_SCOPE:
            locations = doc["rules"][rule["meta"]["name"]]["matches"].keys()
            rows.append(("matches", "\n".join(map(rutils.hex, locations))))

        ostream.writeln(tabulate.tabulate(rows, tablefmt="plain"))
        ostream.write("\n")

    if not had_match:
        ostream.writeln(rutils.bold("no capabilities found"))


def render_verbose(doc):
    ostream = rutils.StringIO()

    render_meta(ostream, doc)
    ostream.write("\n")

    render_rules(ostream, doc)
    ostream.write("\n")

    return ostream.getvalue()
