# coding: utf-8
# python 2 only

# Copyright (c) 2026 TormachTips.com. All rights reserved.
# Licensed under the TormachTips Personal Use License.
# Permission is granted only for private personal use and private personal modification.
# No sharing, publication, distribution, resale, sublicensing, screenshots, code excerpts,
# benchmarks, or videos are permitted without prior written permission.
# Requests:         tormach.1100m@gmail.com
# Information page: https://tormachtips.com/plugins.htm

############################################
##                                        ##
##     Cycle Start Popup Button 0.03      ##
##          www.tormachtips.com           ##
##                                        ##
############################################

# 0.03 - public beta - 4/24/2026

import os
import gtk
import glib
import constants
import singletons
from ui_hooks import plugin

CURRENT_VER      = "0.03"
SCRIPT_NAME      = "Cycle Start Popup Test"
DESCRIPTION      = "This is a separate, external Cycle Start button, utiliziing all OEM built-in safety checks. This is test code for future projects."
ENABLED          = 1
DEV_MACHINE      = 0
DEV_MACHINE_FLAG = "/home/operator/gcode/python/dev_machine.txt"

class UserPlugin(plugin):
    def __init__(self):
        plugin.__init__(self, SCRIPT_NAME)
        self.dialog = None
        self.button = None
        dev_machine_found = os.path.exists(DEV_MACHINE_FLAG)
        if dev_machine_found:
            plugin_enabled = DEV_MACHINE
        else:
            plugin_enabled = ENABLED
        if plugin_enabled:
            glib.timeout_add(3000, self.start_process)
            return
        else:
            if dev_machine_found:
                self.error_handler.write("[%s] Dev machine found. Plugin loaded, but disabled by DEV_MACHINE." % SCRIPT_NAME, constants.ALARM_LEVEL_QUIET)
            else:
                self.error_handler.write("[%s] Plugin loaded, but disabled." % SCRIPT_NAME, constants.ALARM_LEVEL_QUIET)
                self.error_handler.write("[%s] To enable, open script, find ENABLED = 0 and change to ENABLED = 1" % SCRIPT_NAME, constants.ALARM_LEVEL_QUIET)
            return
    
    def start_process(self):
        try:
            ui = singletons.g_Machine
            if not ui:
                self.error_handler.write("[%s] singletons.g_Machine not ready." % SCRIPT_NAME, constants.ALARM_LEVEL_LOW)
                return True
            self.make_popup(ui)
            self.error_handler.write("[%s] v%s Ready." % (SCRIPT_NAME, CURRENT_VER),constants.ALARM_LEVEL_QUIET)
            return False
        except Exception as e:
            self.error_handler.write("[%s] Startup error: %s" % (SCRIPT_NAME, str(e)),constants.ALARM_LEVEL_LOW)
            return False
    
    def make_popup(self, ui):
        self.dialog = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.dialog.set_title("Cycle Start Test")
        self.dialog.set_default_size(260, 120)
        self.dialog.set_decorated(True)
        self.dialog.set_keep_above(True)
        self.dialog.set_accept_focus(True)
        self.dialog.set_focus_on_map(True)
        self.dialog.set_skip_taskbar_hint(False)
        self.dialog.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
        self.dialog.set_position(gtk.WIN_POS_CENTER)
        box = gtk.VBox(False, 8)
        box.set_border_width(10)
        label = gtk.Label("Presses PathPilot's real Cycle Start button.")
        label.set_line_wrap(True)
        box.pack_start(label, False, False, 0)
        self.button = gtk.Button("CYCLE START")
        self.button.set_size_request(220, 60)
        self.button.connect("clicked", self.on_cycle_start_clicked)
        box.pack_start(self.button, False, False, 0)
        self.dialog.add(box)
        self.dialog.connect("map-event", self.on_dialog_mapped)
        self.dialog.connect("delete-event", self.on_dialog_delete)
        self.dialog.show_all()
        glib.idle_add(self.force_to_front)
        glib.timeout_add(250, self.force_to_front)
        glib.timeout_add(1000, self.force_to_front)
    
    def on_dialog_mapped(self, widget, event):
        self.force_to_front()
        return False
    
    def on_dialog_delete(self, widget, event):
        widget.hide()
        return True
    
    def force_to_front(self):
        try:
            if not self.dialog:
                return False
            self.dialog.show_all()
            self.dialog.set_keep_above(True)
            self.dialog.present()
            gdk_window = self.dialog.get_window()
            if gdk_window:
                gdk_window.raise_()
        except Exception as e:
            self.error_handler.write("[%s] raise failed: %s" % (SCRIPT_NAME, str(e)),constants.ALARM_LEVEL_LOW)
        return False
    
    def request_cycle_start(self):
        ui = singletons.g_Machine
        if not ui:
            self.error_handler.write("[%s] UI object not available." % SCRIPT_NAME,constants.ALARM_LEVEL_LOW)
            return False
        try:
            cycle_widget = ui.button_list.get("cycle_start", None)
            if not cycle_widget:
                self.error_handler.write("[%s] cycle_start widget not found." % SCRIPT_NAME,constants.ALARM_LEVEL_LOW)
                return False
            if not hasattr(ui, "enqueue_button_press_release"):
                self.error_handler.write("[%s] enqueue_button_press_release not found." % SCRIPT_NAME,constants.ALARM_LEVEL_LOW)
                return False
            ui.enqueue_button_press_release(cycle_widget)
        except Exception as e:
            self.error_handler.write("[%s] Cycle Start request failed: %s" % (SCRIPT_NAME, str(e)),constants.ALARM_LEVEL_LOW)
        return False
    
    def on_cycle_start_clicked(self, widget):
        self.error_handler.write("[%s] Requesting OEM Cycle Start through FIFO." % SCRIPT_NAME,constants.ALARM_LEVEL_DEBUG)
        self.request_cycle_start()
        
DESCRIPTION_LONG = """PathPilot will show the ETA of a running 
    program, but only after it has been run once and it can log an approximate 
    elapsed time. And, if you rename or edit the file, that estimate disappears.
    </font></p>
    <p>
    <font face="Verdana" size="2">This can be frustrating, especially for longer 
    jobs.</font></p>
    <p><font face="Verdana" size="2">This plugin calculates an estimated runtime 
    every time a file is loaded and displays it in the Status window. No need to 
    run the file first.</font></p>
    <p><font face="Verdana" size="2">Default rapid speed for G0 moves is set to 
    100 IPM. You can adjust this in the script if your machine differs (e.g. MX, 
    etc.).</font></p>
    <p><font face="Verdana" size="2">For more advanced estimators with 
    line-by-line analysis, see my scripts <a href="cgi-bin/time.cgi">here</a> and
    <a href="cgi-bin/time2.cgi">here</a>.</font></p>
    <p><a href="images/estimator.png">
    <img border="2" src="images/estimator_small.png" xthumbnail-orig-image="images/estimator.png" width="200" height="150"></a><br>
    <font face="Verdana" size="1">(There are other plugins in this screenshot, 
    but the Estimated Cycle Time is the important part here...)</font></p>"""        