# 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 self.enabled:
            return
        if 'M109' in line:
            tool_match = re.match('.+T(\d+)', line)
            if tool_match:
                tool = int(tool_match.group(1)) + 1
            else:
                tool = 1
            temp_match = re.match('.+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('M109', 'M104')
        elif 'M190' in line:
            temp_match = re.match('.+S([\d\.]+)', line)
            if temp_match and float(temp_match.group(1)) > 0:
                self.__enable(line, 0)
                return line.replace('M190', 'M140')

    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
