# Copyright 3D Control Systems, Inc. All Rights Reserved 2017-2019.
# Built in San Francisco.

# This software is distributed under a commercial license for personal,
# educational, corporate or any other use.
# The software as a whole or any parts of it is prohibited for distribution or
# use without obtaining a license from 3D Control Systems, Inc.

# All software licenses are subject to the 3DPrinterOS terms of use
# (available at https://www.3dprinteros.com/terms-and-conditions/),
# and privacy policy (available at https://www.3dprinteros.com/privacy-policy/)

import re
import time

import logging


class BlockingHeatingEmulator:

    MAX_TIME_LIMIT = 30
    MAX_DELTA_TIME_LIMIT = 15
    DELTA_TEMP = 0.1

    def __init__(self, sender):
        self.sender = sender
        self.enabled = False
        self.original_gcode = None
        self.tool = None
        self.last_change_time = None
        self.max_temp = 0
        self.logger = logging.getLogger(self.__class__.__name__)

    def start(self, line):
        if not self.enabled:
            if b'M109' in line:
                tool_match = re.match(b'.+T(\d+)', line)
                if tool_match:
                    tool = int(tool_match.group(1)) + 1
                else:
                    tool = 1
                temp_match = re.match(b'.+S([\d\.]+)', line)
                if temp_match:
                    if tool < len(self.sender.target_temps) and float(temp_match.group(1)) > 0:
                        self.__enable(line, tool)
                        return line.replace(b'M109', b'M104')
            elif b'M190' in line:
                temp_match = re.match(b'.+S([\d\.]+)', line)
                if temp_match and float(temp_match.group(1)) > 0:
                    self.__enable(line, 0)
                    return line.replace(b'M190', b'M140')
        return line

    def __enable(self, line, tool):
        self.original_gcode = line
        self.tool = tool
        self.last_change_time = time.monotonic()
        self.max_temp = 0
        self.enabled = True

    def disable(self, send_original_gcode=True):
        if self.original_gcode and send_original_gcode:
            self.sender._send_now(self.original_gcode)
        self.original_gcode = None
        self.enabled = False

    def check_temperatures(self):
        if not self.enabled:
            return
        t = time.monotonic()
        if t - self.last_change_time > self.MAX_TIME_LIMIT:
            return self.disable()
        target = self.sender.target_temps[self.tool]
        if target <= 0:
            return
        temp = self.sender.temps[self.tool]
        delta = target*self.DELTA_TEMP
        if temp >= target or (temp >= target-delta and t-self.last_change_time > self.MAX_DELTA_TIME_LIMIT):
            return self.disable()
        if temp > self.max_temp:
            self.max_temp = temp
            self.last_change_time = t
