#!/usr/bin/env python3
"""
Button-controlled screen mode cycling for ModuleAir Pro 4G
Connected to GPIO6 (GND), cycles through 4 screen modes:
1. Network Status
2. All 4 Sensors (displayAll4_v2)
3. CO2 + PM2.5 + PM10 + Network Status
4. Blank Screen (black)

Usage:
sudo python3 /var/www/moduleair_pro_4g/matrix/button_screen_controller.py
"""

import RPi.GPIO as GPIO
import subprocess
import time
import signal
import sys
import os

# GPIO pin configuration
BUTTON_PIN = 6  # GPIO6, connected to GND when pressed

# Screen mode configuration
SCREEN_MODES = {
    0: {
        'name': 'Network Status',
        'program': '/var/www/moduleair_pro_4g/matrix/screenNetwork/network_status',
        'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
    },
    1: {
        'name': 'All 4 Sensors',
        'program': '/var/www/moduleair_pro_4g/matrix/screenSensors/displayAll4_v2',
        'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
    },
    2: {
        'name': 'CO2 + PM + Network',
        'program': '/var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network',
        'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
    },
    3: {
        'name': 'Blank Screen',
        'program': '/var/www/moduleair_pro_4g/matrix/screenSensors/blank_screen',
        'args': ['--led-no-hardware-pulse', '--led-row-addr-type=3', '--led-panel-type=FM6126A']
    }
}

class ScreenController:
    def __init__(self):
        self.current_mode = 0
        self.current_process = None
        self.running = True
        self.last_button_press = 0
        self.debounce_time = 0.5  # 500ms debounce
        self.last_button_state = True  # Button starts as HIGH (pulled up)
        
        # Set up GPIO - clean initialization
        try:
            # Only cleanup if GPIO was previously initialized
            if GPIO.getmode() is not None:
                GPIO.cleanup()
        except:
            pass
            
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        
        # Set up signal handlers
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        
        print(f"Screen controller initialized. Current mode: {SCREEN_MODES[self.current_mode]['name']}")
        print("Using polling method for button detection (more reliable)")
        
    def check_button_pressed(self):
        """Check for button press using polling method"""
        current_button_state = GPIO.input(BUTTON_PIN)
        
        # Detect falling edge (button pressed)
        if self.last_button_state == True and current_button_state == False:
            current_time = time.time()
            if current_time - self.last_button_press >= self.debounce_time:
                self.last_button_press = current_time
                self.cycle_screen_mode()
        
        self.last_button_state = current_button_state
        
    def cycle_screen_mode(self):
        """Cycle to next screen mode"""
        print(f"Button pressed! Cycling from mode {self.current_mode}")
        
        # Stop current program
        self.stop_current_program()
        
        # Move to next mode
        old_mode = self.current_mode
        self.current_mode = (self.current_mode + 1) % len(SCREEN_MODES)
        
        print(f"Switching from mode {old_mode} to mode {self.current_mode}: {SCREEN_MODES[self.current_mode]['name']}")
        
        # Start new program
        self.start_current_program()
        print(f"Mode switch completed successfully")
        
    def stop_current_program(self):
        """Stop the currently running display program"""
        if self.current_process:
            try:
                self.current_process.terminate()
                self.current_process.wait(timeout=3)
                print("Previous display program terminated")
            except subprocess.TimeoutExpired:
                self.current_process.kill()
                print("Previous display program killed (timeout)")
            except Exception as e:
                print(f"Error stopping previous program: {e}")
            finally:
                self.current_process = None
                
        # Kill any remaining matrix display processes (be very specific to avoid killing ourselves)
        try:
            subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenNetwork/network_status'], check=False)
            subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenSensors/displayAll4'], check=False)
            subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2'], check=False)
            subprocess.run(['pkill', '-f', '/var/www/moduleair_pro_4g/matrix/screenSensors/blank_screen'], check=False)
            time.sleep(0.5)  # Give processes time to exit
        except Exception as e:
            print(f"Error killing matrix processes: {e}")
            
    def start_current_program(self):
        """Start the display program for current mode"""
        mode_config = SCREEN_MODES[self.current_mode]
        program_path = mode_config['program']
        
        if not os.path.exists(program_path):
            print(f"Warning: Program {program_path} does not exist")
            return
            
        try:
            # Build command with arguments
            cmd = [program_path] + mode_config['args']
            
            print(f"Starting: {' '.join(cmd)}")
            
            # Start the new program
            self.current_process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                preexec_fn=os.setsid
            )
            
            print(f"Started {mode_config['name']} (PID: {self.current_process.pid})")
            
        except Exception as e:
            print(f"Error starting program {program_path}: {e}")
            self.current_process = None
            
    def signal_handler(self, signum, frame):
        """Handle shutdown signals"""
        print(f"Received signal {signum}, shutting down...")
        print(f"Signal received at frame: {frame}")
        self.running = False
        self.cleanup()
        sys.exit(0)
        
    def cleanup(self):
        """Clean up resources"""
        print("Cleaning up...")
        self.stop_current_program()
        GPIO.cleanup()
        
    def run(self):
        """Main loop"""
        # Start with initial screen mode
        self.start_current_program()
        
        try:
            while self.running:
                # Check for button press
                self.check_button_pressed()
                
                # Check if current process is still running
                if self.current_process and self.current_process.poll() is not None:
                    print(f"Display program ended unexpectedly (exit code: {self.current_process.returncode})")
                    self.current_process = None
                    # Restart the current program
                    time.sleep(1)
                    self.start_current_program()
                
                time.sleep(0.1)  # Check 10 times per second for responsive button
                
        except KeyboardInterrupt:
            print("Keyboard interrupt received")
        finally:
            self.cleanup()

def main():
    """Main function"""
    print("ModuleAir Pro 4G - Button Screen Controller")
    print("Press button on GPIO6 to cycle through screen modes")
    print("Modes: Network Status -> All 4 Sensors -> CO2+PM+Network -> Blank Screen")
    
    controller = ScreenController()
    controller.run()

if __name__ == "__main__":
    main()