#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
# problems because it might remove variables from the check-context which are necessary for
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
# it might also remove variables needed for accessing discovery rulesets.
from cmk.base.check_legacy_includes.jolokia import *  # pylint: disable=wildcard-import,unused-wildcard-import

factory_settings.update(
    {
        "jolokia_jvm_memory": {
            "perc_heap": (80.0, 90.0),
            "perc_nonheap": (80.0, 90.0),
            "perc_total": (80.0, 90.0),
        },
        "jolokia_jvm_memory_pools": {
            "perc_used": (80.0, 90.0),
        },
    }
)

MB = 1024 * 1024


def parse_jolokia_jvm_memory(info):
    parsed = {}
    for instance, mbean, data in parse_jolokia_json_output(info):

        type_ = jolokia_mbean_attribute("type", mbean)
        parsed_data = parsed.setdefault(instance, {}).setdefault(type_, {})
        parsed_data.update(data)

    return parsed


def _jolokia_check_abs_and_perc(mem_type, value, value_max, params):
    perf_name = ("mem_%s" % mem_type) if mem_type != "total" else None

    yield check_levels(
        value,
        perf_name,
        params.get("abs_%s" % mem_type),
        infoname=mem_type.title(),
        human_readable_func=get_bytes_human_readable,
        boundaries=(None, value_max),
    )

    if value_max is None:
        return

    perc_val = float(value) / float(value_max) * 100.0
    yield check_levels(
        perc_val,
        None,
        params.get("perc_%s" % mem_type),
        human_readable_func=get_percent_human_readable,
        boundaries=(0, 100),
    )


@discover
def discover_jolokia_jvm_memory(_instance, data):
    return bool(data.get("Memory"))


def _iter_type_value_max(mem_data):
    heap_data = mem_data.get("HeapMemoryUsage", {})
    nonheap_data = mem_data.get("NonHeapMemoryUsage", {})
    heap = heap_data.get("used")
    nonheap = nonheap_data.get("used")
    heapmax = heap_data.get("max", -1)
    nonheapmax = nonheap_data.get("max", -1)

    totalmax = heapmax + nonheapmax
    if heapmax <= 0:
        heapmax = None
        totalmax = None
    if nonheapmax <= 0:
        nonheapmax = None
        totalmax = None

    if heap is not None:
        yield "heap", heap, heapmax
    if nonheap is not None:
        yield "nonheap", nonheap, nonheapmax
    if heap is not None and nonheap is not None:
        yield "total", heap + nonheap, totalmax


@get_parsed_item_data
def check_jolokia_jvm_memory(_item, params, instance_data):
    mem_data = instance_data.get("Memory", {})

    for mem_type, value, value_max in _iter_type_value_max(mem_data):
        for subresult in _jolokia_check_abs_and_perc(mem_type, value, value_max, params):
            yield subresult


check_info["jolokia_jvm_memory"] = {
    "parse_function": parse_jolokia_jvm_memory,
    "inventory_function": discover_jolokia_jvm_memory,
    "check_function": check_jolokia_jvm_memory,
    "service_description": "JVM %s Memory",
    "has_perfdata": True,
    "default_levels_variable": "jolokia_jvm_memory",
    "group": "jvm_memory",
}

# .
#   .--Memory Pools--------------------------------------------------------.
#   |   __  __                                   ____             _        |
#   |  |  \/  | ___ _ __ ___   ___  _ __ _   _  |  _ \ ___   ___ | |___    |
#   |  | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | | |_) / _ \ / _ \| / __|   |
#   |  | |  | |  __/ | | | | | (_) | |  | |_| | |  __/ (_) | (_) | \__ \   |
#   |  |_|  |_|\___|_| |_| |_|\___/|_|   \__, | |_|   \___/ \___/|_|___/   |
#   |                                    |___/                             |
#   '----------------------------------------------------------------------'


def discover_jolokia_jvm_memory_pools(parsed):
    for instance, instance_info in parsed.items():
        for data in instance_info.get("MemoryPool", {}).values():
            pool = data.get("Name")
            if pool:
                yield "%s Memory Pool %s" % (instance, pool), {}


def _get_jolokia_jvm_mempool_data(item, parsed):
    instance, pool_name = item.split(" Memory Pool ", 1)
    data = parsed.get(instance, {}).get("MemoryPool", {})
    pools = [pool for pool in data.values() if pool.get("Name") == pool_name]
    return pools[0] if pools else {}


def check_jolokia_jvm_memory_pools(item, params, parsed):
    data = _get_jolokia_jvm_mempool_data(item, parsed)
    usage = data.get("Usage")
    if not usage:
        return

    value_max = usage.get("max", -1)
    value_max = value_max if value_max > 0 else None
    for subresult in _jolokia_check_abs_and_perc("used", usage["used"], value_max, params):
        yield subresult

    init = usage.get("init")
    if init is not None:
        yield 0, "Initially: %s" % get_bytes_human_readable(init)

    committed = usage.get("committed")
    if committed is not None:
        yield 0, "Committed: %s" % get_bytes_human_readable(committed)


check_info["jolokia_jvm_memory.pools"] = {
    "inventory_function": discover_jolokia_jvm_memory_pools,
    "check_function": check_jolokia_jvm_memory_pools,
    "service_description": "JVM %s",
    "has_perfdata": True,
    "default_levels_variable": "jolokia_jvm_memory_pools",
    "group": "jvm_memory_pools",
}
