""" Function to convert raw modbus value """

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import datetime
import json
from . import time_delta_json


def unit(raw_table, base_index):
    """ Direct word value """
    raw_value = raw_table[base_index]
    sign = 1
    if raw_value & 0x8000:
        sign = -1
    return sign * (raw_value & 0x7FFF)


def tenth(raw_table, base_index):
    """ Word value divide by ten """
    raw_value = raw_table[base_index]
    if raw_value == 0xFFFF:
        return None
    sign = 1
    if raw_value & 0x8000:
        sign = -1
    return sign * (raw_value & 0x7FFF) / 10


def unit_and_ten(raw_table, base_index):
    """ Two word values, 0000x and xxxx0 """
    return raw_table[base_index] + 10 * raw_table[base_index + 1]

def anticipation(raw_table, base_index):
    """ 101 for None or value divide by ten """
    raw_value = raw_table[base_index]
    if raw_value == 101:
        return None
    return tenth(raw_table, base_index)

def footprint(raw_table, base_index):
    """ 150 for None or value divide by ten """
    raw_value = raw_table[base_index]
    if raw_value == 150:
        return None
    return tenth(raw_table, base_index)


def power(raw_table, base_index):
    """ Value of MWh, KWh, Wh or None if 65535 """
    if (raw_table[base_index] == 0xFFFF
            or raw_table[base_index+1] == 0xFFFF
            or raw_table[base_index+2] == 0xFFFF):
        return None
    return (raw_table[base_index] * 1000 + raw_table[base_index+1]) * 1000 + raw_table[base_index+2]

BIT_ANTIFREEZE = 1
BIT_NIGHT = 2
BIT_DAY = 4
BIT_AUTO = 8
BIT_DHW = 16
BIT_END_OF_PROGRAM = 32
BIT_DHW_END_OF_PROGRAM = 64
BIT_ALL_ZONE = 128


def derog_bit(raw_table, base_index):
    """ Convert derog bit flag to english """
    value = raw_table[base_index]
    stringvalue = ""
    if value & BIT_ANTIFREEZE:
        stringvalue += "Antifreeze "
    if value & BIT_NIGHT:
        stringvalue += "Night "
    if value & BIT_DAY:
        stringvalue += "Day "
    if value & BIT_AUTO:
        stringvalue += "Automatic "
    if value & BIT_DHW:
        stringvalue += "Water "
    if value & BIT_END_OF_PROGRAM:
        stringvalue += "until the end of the program "
    if value & BIT_DHW_END_OF_PROGRAM:
        stringvalue += "until the end of the program (water) "
    if value & BIT_ALL_ZONE:
        stringvalue += "all areas"
    return stringvalue


def derog_bit_simple(raw_table, base_index):
    """ Convert derog bit flag to english do not handle all case """
    value = raw_table[base_index]
    stringvalue = ""
    if value & BIT_ANTIFREEZE:
        stringvalue = "Holidays"
    if value & BIT_NIGHT:
        stringvalue = "Night"
    if value & BIT_DAY:
        stringvalue = "Day"
    if value & BIT_AUTO:
        stringvalue = "Automatic"
    return stringvalue


def active_mode(raw_table, base_index):
    """ Convert mode to english  """
    value = raw_table[base_index]
    if value == 0:
        return "Antifreeze"
    if value == 2:
        return "Night"
    if value == 4:
        return "Day"
    return "Unknown"


def boiler_mode(raw_table, base_index):
    """ Convert boiler mode to english  """
    value = raw_table[base_index]
    if value == 4:
        return "Summer"
    if value == 5:
        return "Winder"
    return "Unknown"


def day_schedule(raw_table, base_index):
    """ Convert schedule of present/away """
    current_mode = 0
    start_time = datetime.timedelta()
    current_time = datetime.timedelta()
    schedule = []
    interval_for_bit = datetime.timedelta(minutes=30)
    for word in raw_table[base_index:base_index + 3]:
        for _ in range(16):
            mode = word & 0x8000
            word <<= 1
            # end of period
            if mode == 0 and current_mode != 0:
                schedule.append((start_time, current_time))
                current_mode = mode

            current_time += interval_for_bit
            # before period
            if mode == 0:
                start_time = current_time

            current_mode = mode
    if current_mode != 0:
        schedule.append((start_time, current_time))

    return schedule


def json_week_schedule(raw_table, base_index):
    """ Convert week schedule to a JSON """
    schedule = {}
    for day in range(7):
        schedule[day] = day_schedule(raw_table, base_index + day * 3)
    encoder = time_delta_json.CustomDateJSONEncoder()
    return encoder.encode(schedule)


def hours_minutes_secondes(raw_table, base_index):
    """ Convert raw value to hours """
    return "%02d:%02d:%02d" % (raw_table[base_index],
                               raw_table[base_index + 1],
                               raw_table[base_index + 2])

def hours_minutes(raw_table, base_index):
    """ Convert raw value to hours """
    return "%02d:%02d" % (raw_table[base_index],
                          raw_table[base_index + 1])


def day_month_year(raw_table, base_index):
    """ Convert raw value to date """
    return "%02d/%02d/%02d" % (raw_table[base_index],
                               raw_table[base_index + 1],
                               raw_table[base_index + 2])

def decrease(raw_table, base_index):
    """ Convert decrease flag to english """
    if raw_table[base_index] == 0:
        return "stop"
    else:
        return "lowering"


def off_on(raw_table, base_index):
    """ Convert off/on flag to text """
    if raw_table[base_index] == 0:
        return "off"
    else:
        return "on"

OUTPUT1_BURNER = 3
OUTPUT1_HYDRAULIC_VALVE_OPEN = 1 << 2
OUTPUT1_HYDRAULIC_VALVE_CLOSE = 1 << 3
OUTPUT1_BOILER_PUMP = 1 << 4
# It's ON on my boiler, I want to follow it.
OUTPUT1_UNKNOW1 = 1 << 5

OUTPUT2_DHW_PUMP = 1 << 0
OUTPUT2_ZONEA_PUMP = 1 << 1
OUTPUT2_ZONEB_PUMP = 1 << 4
OUTPUT2_ZONEB_3WV_OPEN = 1 << 5
OUTPUT2_ZONEB_3WV_CLOSE = 1 << 6
OUTPUT2_ZONEC_PUMP = 1 << 7
OUTPUT2_ZONEC_3WV_OPEN = 1 << 8
OUTPUT2_ZONEC_3WV_CLOSE = 1 << 9
OUTPUT2_AUX_PUMP = 1 << 10


def output_state(raw_table, base_index):
    """ Convert output state to JSON """
    result = {}
    val = raw_table[base_index]
    result["burner"] = val & OUTPUT1_BURNER
    result["hydraulic_valve_open"] = bool(val & OUTPUT1_HYDRAULIC_VALVE_OPEN)
    result["hydraulic_valve_close"] = bool(val & OUTPUT1_HYDRAULIC_VALVE_CLOSE)
    result["hydraulic_boiler_pump"] = bool(val & OUTPUT1_BOILER_PUMP)
    result["UNKNOWN1"] = bool(val & OUTPUT1_UNKNOW1)
    val = raw_table[base_index + 1]
    result["DHW_pump"] = bool(val & OUTPUT2_DHW_PUMP)
    result["zone_A_pump"] = bool(val & OUTPUT2_ZONEA_PUMP)
    result["zone_B_pump"] = bool(val & OUTPUT2_ZONEB_PUMP)
    result["zone_B_3WV_open"] = bool(val & OUTPUT2_ZONEB_3WV_OPEN)
    result["zone_B_3WV_close"] = bool(val & OUTPUT2_ZONEB_3WV_CLOSE)
    result["zone_C_pump"] = bool(val & OUTPUT2_ZONEC_PUMP)
    result["zone_C_3WV_open"] = bool(val & OUTPUT2_ZONEC_3WV_OPEN)
    result["zone_C_3WV_close"] = bool(val & OUTPUT2_ZONEC_3WV_CLOSE)
    result["AUX_pump"] = bool(val & OUTPUT2_AUX_PUMP)
    return json.dumps(result)

BASEECS_AUX_PUMP = 1
BASEECS_ZONEA_PUMP_BOILER = 1 << 1
BASEECS_BURNER_1_2 = 1 << 2
BASEECS_BURNER_1_1 = 1 << 3
BASEECS_ZONEA_PUMP = 1 << 4
BASEECS_DHW_PUMP = 1 << 5
BASEECS_ALARM_BURNER = 1 << 6
# BASEECS_ = 1 << 7
BASEECS_VALVE = 1 << 8

def base_ecs(raw_table, base_index):
    """ Convert base_ecs state to JSON """
    result = {}
    val = raw_table[base_index]
    result["AUX_pump"] = bool(val & BASEECS_AUX_PUMP)
    result["zone_A_pump_boiler"] = bool(val & BASEECS_ZONEA_PUMP_BOILER)
    result["burner_1_2"] = bool(val & BASEECS_BURNER_1_2)
    result["burner_1_1"] = bool(val & BASEECS_BURNER_1_1)
    result["zone_A_pump"] = bool(val & BASEECS_ZONEA_PUMP)
    result["DHW_pump"] = bool(val & BASEECS_DHW_PUMP)
    result["Alarm_burner"] = bool(val & BASEECS_ALARM_BURNER)
    result["valve"] = bool(val & BASEECS_VALVE)

    return json.dumps(result)

def fan(raw_table, base_index):
    """ Convert for fan speed """
    val = raw_table[base_index]
    return val & 0x007F

def texte14(raw_table, base_index):
    """ Convert 14 char of text """
    result = ''
    for word in raw_table[base_index:base_index + 7]:
        result = result + chr(word >> 8) + chr(word & 0x00FF)
    return result

def write_unit(value):
    """ Convert unit value to modbus value """
    return [int(value)]


def write_tenth(value):
    """ Convert tenth value to modbus value """
    int_value = int(float(value) * 10)
    if int_value < 0:
        int_value = abs(int_value) | 0x8000
    return [int_value]

DEROG_NAME_TO_VALUE = {
    "Holidays": BIT_ANTIFREEZE | BIT_END_OF_PROGRAM,
    "Night": BIT_NIGHT | BIT_END_OF_PROGRAM,
    "Day": BIT_DAY | BIT_END_OF_PROGRAM,
    "Automatic": BIT_AUTO
}


def write_derog_bit_simple(value):
    """ Convert english Mode to bit value """
    if value not in DEROG_NAME_TO_VALUE:
        return None
    return [DEROG_NAME_TO_VALUE[value]]

def bcd_ten(raw_table, base_index):
    """ Direct word value """
    return int(hex(raw_table[base_index])[2:]+"0")

def readbit(val, bitidx):
    """ Direct word value """
    return int((val & (1<<bitidx))!=0)

def readbit0(raw_table, base_index):
    """ Direct word value """
    return int(readbit(raw_table[base_index], 0 ))

def readbit1(raw_table, base_index):
    """ Direct word value """
    return (readbit(raw_table[base_index], 1 ))

def readbit2(raw_table, base_index):
    return (readbit(raw_table[base_index], 2 ))

def readbit3(raw_table, base_index):
    return (readbit(raw_table[base_index], 3 ))

def readbit4(raw_table, base_index):
    return (readbit(raw_table[base_index], 4 ))

def readbit5(raw_table, base_index):
    return (readbit(raw_table[base_index], 5 ))

def readbit6(raw_table, base_index):
    return (readbit(raw_table[base_index], 6 ))


""" Function to convert raw modbus value """

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import datetime
import json
from . import time_delta_json


def unit(raw_table, base_index):
    """ Direct word value """
    raw_value = raw_table[base_index]
    sign = 1
    if raw_value & 0x8000:
        sign = -1
    return sign * (raw_value & 0x7FFF)


def tenth(raw_table, base_index):
    """ Word value divide by ten """
    raw_value = raw_table[base_index]
    if raw_value == 0xFFFF:
        return None
    sign = 1
    if raw_value & 0x8000:
        sign = -1
    return sign * (raw_value & 0x7FFF) / 10


def unit_and_ten(raw_table, base_index):
    """ Two word values, 0000x and xxxx0 """
    return raw_table[base_index] + 10 * raw_table[base_index + 1]

def anticipation(raw_table, base_index):
    """ 101 for None or value divide by ten """
    raw_value = raw_table[base_index]
    if raw_value == 101:
        return None
    return tenth(raw_table, base_index)

def footprint(raw_table, base_index):
    """ 150 for None or value divide by ten """
    raw_value = raw_table[base_index]
    if raw_value == 150:
        return None
    return tenth(raw_table, base_index)


def power(raw_table, base_index):
    """ Value of MWh, KWh, Wh or None if 65535 """
    if (raw_table[base_index] == 0xFFFF
            or raw_table[base_index+1] == 0xFFFF
            or raw_table[base_index+2] == 0xFFFF):
        return None
    return (raw_table[base_index] * 1000 + raw_table[base_index+1]) * 1000 + raw_table[base_index+2]

BIT_ANTIFREEZE = 1
BIT_NIGHT = 2
BIT_DAY = 4
BIT_AUTO = 8
BIT_DHW = 16
BIT_END_OF_PROGRAM = 32
BIT_DHW_END_OF_PROGRAM = 64
BIT_ALL_ZONE = 128


def derog_bit(raw_table, base_index):
    """ Convert derog bit flag to english """
    value = raw_table[base_index]
    stringvalue = ""
    if value & BIT_ANTIFREEZE:
        stringvalue += "Antifreeze "
    if value & BIT_NIGHT:
        stringvalue += "Night "
    if value & BIT_DAY:
        stringvalue += "Day "
    if value & BIT_AUTO:
        stringvalue += "Automatic "
    if value & BIT_DHW:
        stringvalue += "Water "
    if value & BIT_END_OF_PROGRAM:
        stringvalue += "until the end of the program "
    if value & BIT_DHW_END_OF_PROGRAM:
        stringvalue += "until the end of the program (water) "
    if value & BIT_ALL_ZONE:
        stringvalue += "all areas"
    return stringvalue


def derog_bit_simple(raw_table, base_index):
    """ Convert derog bit flag to english do not handle all case """
    value = raw_table[base_index]
    stringvalue = ""
    if value & BIT_ANTIFREEZE:
        stringvalue = "Holidays"
    if value & BIT_NIGHT:
        stringvalue = "Night"
    if value & BIT_DAY:
        stringvalue = "Day"
    if value & BIT_AUTO:
        stringvalue = "Automatic"
    return stringvalue


def active_mode(raw_table, base_index):
    """ Convert mode to english  """
    value = raw_table[base_index]
    if value == 0:
        return "Antifreeze"
    if value == 2:
        return "Night"
    if value == 4:
        return "Day"
    return "Unknown"


def boiler_mode(raw_table, base_index):
    """ Convert boiler mode to english  """
    value = raw_table[base_index]
    if value == 4:
        return "Summer"
    if value == 5:
        return "Winder"
    return "Unknown"


def day_schedule(raw_table, base_index):
    """ Convert schedule of present/away """
    current_mode = 0
    start_time = datetime.timedelta()
    current_time = datetime.timedelta()
    schedule = []
    interval_for_bit = datetime.timedelta(minutes=30)
    for word in raw_table[base_index:base_index + 3]:
        for _ in range(16):
            mode = word & 0x8000
            word <<= 1
            # end of period
            if mode == 0 and current_mode != 0:
                schedule.append((start_time, current_time))
                current_mode = mode

            current_time += interval_for_bit
            # before period
            if mode == 0:
                start_time = current_time

            current_mode = mode
    if current_mode != 0:
        schedule.append((start_time, current_time))

    return schedule


def json_week_schedule(raw_table, base_index):
    """ Convert week schedule to a JSON """
    schedule = {}
    for day in range(7):
        schedule[day] = day_schedule(raw_table, base_index + day * 3)
    encoder = time_delta_json.CustomDateJSONEncoder()
    return encoder.encode(schedule)


def hours_minutes_secondes(raw_table, base_index):
    """ Convert raw value to hours """
    return "%02d:%02d:%02d" % (raw_table[base_index],
                               raw_table[base_index + 1],
                               raw_table[base_index + 2])

def hours_minutes(raw_table, base_index):
    """ Convert raw value to hours """
    return "%02d:%02d" % (raw_table[base_index],
                          raw_table[base_index + 1])


def day_month_year(raw_table, base_index):
    """ Convert raw value to date """
    return "%02d/%02d/%02d" % (raw_table[base_index],
                               raw_table[base_index + 1],
                               raw_table[base_index + 2])

def decrease(raw_table, base_index):
    """ Convert decrease flag to english """
    if raw_table[base_index] == 0:
        return "stop"
    else:
        return "lowering"


def off_on(raw_table, base_index):
    """ Convert off/on flag to text """
    if raw_table[base_index] == 0:
        return "off"
    else:
        return "on"

OUTPUT1_BURNER = 3
OUTPUT1_HYDRAULIC_VALVE_OPEN = 1 << 2
OUTPUT1_HYDRAULIC_VALVE_CLOSE = 1 << 3
OUTPUT1_BOILER_PUMP = 1 << 4
# It's ON on my boiler, I want to follow it.
OUTPUT1_UNKNOW1 = 1 << 5

OUTPUT2_DHW_PUMP = 1 << 0
OUTPUT2_ZONEA_PUMP = 1 << 1
OUTPUT2_ZONEB_PUMP = 1 << 4
OUTPUT2_ZONEB_3WV_OPEN = 1 << 5
OUTPUT2_ZONEB_3WV_CLOSE = 1 << 6
OUTPUT2_ZONEC_PUMP = 1 << 7
OUTPUT2_ZONEC_3WV_OPEN = 1 << 8
OUTPUT2_ZONEC_3WV_CLOSE = 1 << 9
OUTPUT2_AUX_PUMP = 1 << 10


def output_state(raw_table, base_index):
    """ Convert output state to JSON """
    result = {}
    val = raw_table[base_index]
    result["burner"] = val & OUTPUT1_BURNER
    result["hydraulic_valve_open"] = bool(val & OUTPUT1_HYDRAULIC_VALVE_OPEN)
    result["hydraulic_valve_close"] = bool(val & OUTPUT1_HYDRAULIC_VALVE_CLOSE)
    result["hydraulic_boiler_pump"] = bool(val & OUTPUT1_BOILER_PUMP)
    result["UNKNOWN1"] = bool(val & OUTPUT1_UNKNOW1)
    val = raw_table[base_index + 1]
    result["DHW_pump"] = bool(val & OUTPUT2_DHW_PUMP)
    result["zone_A_pump"] = bool(val & OUTPUT2_ZONEA_PUMP)
    result["zone_B_pump"] = bool(val & OUTPUT2_ZONEB_PUMP)
    result["zone_B_3WV_open"] = bool(val & OUTPUT2_ZONEB_3WV_OPEN)
    result["zone_B_3WV_close"] = bool(val & OUTPUT2_ZONEB_3WV_CLOSE)
    result["zone_C_pump"] = bool(val & OUTPUT2_ZONEC_PUMP)
    result["zone_C_3WV_open"] = bool(val & OUTPUT2_ZONEC_3WV_OPEN)
    result["zone_C_3WV_close"] = bool(val & OUTPUT2_ZONEC_3WV_CLOSE)
    result["AUX_pump"] = bool(val & OUTPUT2_AUX_PUMP)
    return json.dumps(result)

BASEECS_AUX_PUMP = 1
BASEECS_ZONEA_PUMP_BOILER = 1 << 1
BASEECS_BURNER_1_2 = 1 << 2
BASEECS_BURNER_1_1 = 1 << 3
BASEECS_ZONEA_PUMP = 1 << 4
BASEECS_DHW_PUMP = 1 << 5
BASEECS_ALARM_BURNER = 1 << 6
# BASEECS_ = 1 << 7
BASEECS_VALVE = 1 << 8

def base_ecs(raw_table, base_index):
    """ Convert base_ecs state to JSON """
    result = {}
    val = raw_table[base_index]
    result["AUX_pump"] = bool(val & BASEECS_AUX_PUMP)
    result["zone_A_pump_boiler"] = bool(val & BASEECS_ZONEA_PUMP_BOILER)
    result["burner_1_2"] = bool(val & BASEECS_BURNER_1_2)
    result["burner_1_1"] = bool(val & BASEECS_BURNER_1_1)
    result["zone_A_pump"] = bool(val & BASEECS_ZONEA_PUMP)
    result["DHW_pump"] = bool(val & BASEECS_DHW_PUMP)
    result["Alarm_burner"] = bool(val & BASEECS_ALARM_BURNER)
    result["valve"] = bool(val & BASEECS_VALVE)

    return json.dumps(result)

def fan(raw_table, base_index):
    """ Convert for fan speed """
    val = raw_table[base_index]
    return val & 0x007F

def texte14(raw_table, base_index):
    """ Convert 14 char of text """
    result = ''
    for word in raw_table[base_index:base_index + 7]:
        result = result + chr(word >> 8) + chr(word & 0x00FF)
    return result

def write_unit(value):
    """ Convert unit value to modbus value """
    return [int(value)]


def write_tenth(value):
    """ Convert tenth value to modbus value """
    int_value = int(float(value) * 10)
    if int_value < 0:
        int_value = abs(int_value) | 0x8000
    return [int_value]

DEROG_NAME_TO_VALUE = {
    "Holidays": BIT_ANTIFREEZE | BIT_END_OF_PROGRAM,
    "Night": BIT_NIGHT | BIT_END_OF_PROGRAM,
    "Day": BIT_DAY | BIT_END_OF_PROGRAM,
    "Automatic": BIT_AUTO
}


def write_derog_bit_simple(value):
    """ Convert english Mode to bit value """
    if value not in DEROG_NAME_TO_VALUE:
        return None
    return [DEROG_NAME_TO_VALUE[value]]

def bcd_ten(raw_table, base_index):
    """ Direct word value """
    return int(hex(raw_table[base_index])[2:]+"0")

def readbit(val, bitidx):
    """ Direct word value """
    return int((val & (1<<bitidx))!=0)

def readbit0(raw_table, base_index):
    """ Direct word value """
    return int(readbit(raw_table[base_index], 0 ))

def readbit1(raw_table, base_index):
    """ Direct word value """
    return (readbit(raw_table[base_index], 1 ))

def readbit2(raw_table, base_index):
    return (readbit(raw_table[base_index], 2 ))

def readbit3(raw_table, base_index):
    return (readbit(raw_table[base_index], 3 ))

def readbit4(raw_table, base_index):
    return (readbit(raw_table[base_index], 4 ))

def readbit5(raw_table, base_index):
    return (readbit(raw_table[base_index], 5 ))

def readbit6(raw_table, base_index):
    return (readbit(raw_table[base_index], 6 ))


Function to convert raw modbus value Direct word value Word value divide by ten Two word values, 0000x and xxxx0 101 for None or value divide by ten 150 for None or value divide by ten Value of MWh, KWh, Wh or None if 65535 Convert derog bit flag to english Convert derog bit flag to english do not handle all case Convert mode to english Convert boiler mode to english Convert schedule of present/away # end of period # before period Convert week schedule to a JSON Convert raw value to hours Convert raw value to hours Convert raw value to date Convert decrease flag to english Convert off/on flag to text # It's ON on my boiler, I want to follow it. Convert output state to JSON # BASEECS_ = 1 << 7 Convert base_ecs state to JSON Convert for fan speed Convert 14 char of text Convert unit value to modbus value Convert tenth value to modbus value Convert english Mode to bit value Direct word value Direct word value Direct word value Direct word value