#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

# <<<lnx_thermal>>>
# thermal_zone0 enabled acpitz 57000 127000 critical
# thermal_zone1 enabled acpitz 65000 100000 critical 95500 passive

# <<<lnx_thermal>>>
# thermal_zone0 enabled acpitz 47000 90000 critical 79000 passive

# <<<lnx_thermal>>>
# thermal_zone0 enabled acpitz 38000 98000 critical
# thermal_zone1 pkg-temp-0  44000 0 passive 0 passive

factory_settings["lnx_thermal_default_levels"] = {
    "levels": (70.0, 80.0),
    "device_levels_handling": "devdefault",
}


def parse_lnx_thermal(info):
    """
    Supported format:
    - Temperature is either the 3rd or 4th element in an info row.
    - After temperature follows pairwise trip point temperature and trip point type.
    - Considered trip points are critical, passive, hot.
    - A known, not considered trip point is active.
    - In case trip point values are 0 or negative (known default values) they are ignored.
    """
    if not info:
        return {}
    parsed = {}
    for line in info:
        temperature, trip_points = _parse_temp_value_str(line)
        if temperature is None and trip_points is None:
            continue

        zone_identifier = 0
        zone_raw = line[zone_identifier]
        zone_formatted = zone_raw.replace('thermal_zone', 'Zone ')
        parsed[zone_formatted] = {}
        temperature_and_trip_points = ('temperature', 'passive', 'critical', 'hot')
        for key in temperature_and_trip_points:
            parsed[zone_formatted].setdefault(key, None)
        parsed[zone_formatted]['temperature'] = temperature
        for tp_name, tp_value in trip_points.items():
            if tp_name in trip_points and tp_value > 0:
                parsed[zone_formatted][tp_name] = tp_value
            else:
                # ignore trip point types not supported yet
                pass
    return parsed


def _parse_temp_value_str(line):
    temperature_factor = 1000.0
    for temp_idx, trip_point_key_idx, trip_point_value_idx in [
        (2, 4, 3),
        (3, 5, 4),
    ]:
        try:
            temp = int(line[temp_idx]) / temperature_factor
            trip_point_keys = line[trip_point_key_idx::2]
            trip_point_values = [int(x) / temperature_factor for x in line[trip_point_value_idx::2]]
            return temp, dict(zip(trip_point_keys, trip_point_values))
        except (IndexError, ValueError):
            pass
    return None, None


def inventory_lnx_thermal(parsed):
    for item in parsed.keys():
        yield item, {}


@get_parsed_item_data
def check_lnx_thermal(item, params, data):
    """
    - Trip points hot and critical are considered for the device crit level. In case both trip
      points are given the lower value is considered for the device crit level.
    - Trip point passive is considered for the device warn level.
    - In case both hot and critical trip points are given the lower trip point value
      is considered for the device crit level.
    - Trip point temperatures are provided via performance data.
    """
    temperature = data.get('temperature')
    warn_level = data.get('passive')
    hot_value = data.get('hot')
    critical_value = data.get('critical')

    if temperature is None:
        return

    if hot_value and critical_value:
        crit_level = min(hot_value, critical_value)
    else:
        crit_level = hot_value or critical_value

    if not crit_level or not warn_level:
        crit_level = crit_level or warn_level
        warn_level = warn_level or crit_level

    if warn_level and crit_level:
        levels = (warn_level, crit_level)
    else:
        levels = None

    return check_temperature(temperature, params, "lnx_thermal_%s" % item, dev_levels=levels)


check_info['lnx_thermal'] = {
    "parse_function": parse_lnx_thermal,
    "inventory_function": inventory_lnx_thermal,
    "check_function": check_lnx_thermal,
    "service_description": "Temperature %s",
    "has_perfdata": True,
    "group": "temperature",
    "default_levels_variable": "lnx_thermal_default_levels",
    "includes": ["temperature.include"],
}
