"""
Automated STM32 Firmware Programming System

This module provides automated firmware programming functionality for STM32 devices,
including ST-Link detection, flash checking, serial verification, and voice announcements.
"""

import sys
import os
import logging
import argparse
import subprocess
import shutil
from typing import List, Optional
from .config import Config
from .file_scanner import scan_firmware_files, extract_serial_number
from .st_link_handler import STLinkHandler
from .firmware_programmer import FirmwareProgrammer
from .serial_communicator import SerialCommunicator
from .file_handler import FileHandler
from .firmware_verifier import FirmwareVerifier
from .error_handler import ErrorHandler
from .logger import ProgrammingLogger
from .performance_monitor import PerformanceMonitor, performance_monitor_decorator
from .flash_checker import FlashChecker
from .voice_announcer import VoiceAnnouncer


class STM32ProgrammingSystem:
    """
    Main STM32 programming system that integrates all modules.
    """
    
    def __init__(self, config: Config, force_programming: bool = False, read_only: bool = False):
        """
        Initialize the programming system with configuration.
        
        Args:
            config: Configuration instance
            force_programming: If True, skip flash check and force programming
            read_only: If True, only check flash without programming
        """
        self.config = config
        self.force_programming = force_programming
        self.read_only = read_only
        self.logger = ProgrammingLogger(
            log_dir=config.get('FILES', 'log_directory'),
            log_level=getattr(logging, config.get('GENERAL', 'log_level'))
        )
        self.error_handler = ErrorHandler()
        
        # Initialize voice announcer first for early notifications
        self.voice_announcer = VoiceAnnouncer()
        
        # Initialize error tracking
        self.timeout_error_count = 0
        self.max_timeout_errors = int(config.get('GENERAL', 'max_retries', '3'))  # Use config value or default to 3
        self.consecutive_errors = 0
        
        # Check if programmer CLI is installed
        self._check_programmer_installation()
        
        # Initialize performance monitor
        self.performance_monitor = PerformanceMonitor()
        
        # Initialize components with proper configuration
        self.st_link_handler = STLinkHandler()  # No config file needed
        self.firmware_programmer = FirmwareProgrammer()
        
        # Get COM port from config
        com_port = config.get('SERIAL', 'port')
        self.serial_communicator = SerialCommunicator(com_port)
        
        self.file_handler = FileHandler()
        self.firmware_verifier = FirmwareVerifier()
        
        # Initialize flash checker with voice announcer
        programmer_cli = config.get('PROGRAMMER', 'programmer_cli')
        self.flash_checker = FlashChecker(programmer_cli, self.voice_announcer)
        
        # Store ST-Link serial number from config
        self.stlink_sn = config.get('PROGRAMMER', 'stlink_sn')
        
        # Store programming offset from config
        self.programming_offset = config.get('PROGRAMMER', 'offset')
        
        # Store flash check configuration
        self.flash_check_address = config.get('FLASH_CHECK', 'check_address')
        self.flash_expected_value = int(config.get('FLASH_CHECK', 'expected_value'), 16)
        self.flash_max_retries = int(config.get('FLASH_CHECK', 'max_retry_attempts'))
        self.flash_retry_delay = int(config.get('FLASH_CHECK', 'retry_delay'))
        
        # Store voice announcement configuration
        self.voice_enabled = config.get('VOICE_ANNOUNCEMENT', 'enable_voice').lower() == 'true'
        self.announce_board_completed = config.get('VOICE_ANNOUNCEMENT', 'announce_board_completed').lower() == 'true'
        self.announce_programming_start = config.get('VOICE_ANNOUNCEMENT', 'announce_programming_start').lower() == 'true'
        self.announce_programming_complete = config.get('VOICE_ANNOUNCEMENT', 'announce_programming_complete').lower() == 'true'
        self.announce_errors = config.get('VOICE_ANNOUNCEMENT', 'announce_errors').lower() == 'true'
        
        self.logger.info("STM32 Programming System initialized")
        if self.stlink_sn:
            self.logger.info(f"ST-Link Serial Number configured: {self.stlink_sn}")
        else:
            self.logger.info("ST-Link Serial Number: Auto-detect mode")
        
        self.logger.info(f"Serial COM Port configured: {com_port}")
        self.logger.info(f"Programming offset configured: {self.programming_offset}")
        self.logger.info(f"Flash check address: {self.flash_check_address}")
        self.logger.info(f"Flash expected value: 0x{self.flash_expected_value:08X}")
        
        if self.force_programming:
            self.logger.info("Force programming mode enabled - flash check will be skipped")
        if self.read_only:
            self.logger.info("Read-only mode enabled - only flash check will be performed")
        self.logger.info(f"Flash check max retries: {self.flash_max_retries}")
        self.logger.info(f"Flash check retry delay: {self.flash_retry_delay}s")
        self.logger.info(f"Voice announcements enabled: {self.voice_enabled}")
        if self.voice_enabled:
            self.logger.info(f"  - Board completed announcement: {self.announce_board_completed}")
            self.logger.info(f"  - Programming start announcement: {self.announce_programming_start}")
            self.logger.info(f"  - Programming complete announcement: {self.announce_programming_complete}")
            self.logger.info(f"  - Error announcements: {self.announce_errors}")
    
    def _check_programmer_installation(self) -> None:
        """
        Check if the programmer CLI is installed and accessible.
        If not found, announce the error and exit.
        """
        programmer_cli = self.config.get('PROGRAMMER', 'programmer_cli')
        if not programmer_cli:
            programmer_cli = self.config.get('PROGRAMMER', 'stm32_programmer_cli')  # Try alternate key
            if not programmer_cli:
                programmer_cli = 'STM32_Programmer_CLI.exe'  # Use default
        
        self.logger.info(f"Checking for programmer CLI: {programmer_cli}")
        
        # First check if it's an absolute path and exists
        if os.path.isabs(programmer_cli) and os.path.exists(programmer_cli):
            self.logger.info(f"Programmer CLI found at absolute path: {programmer_cli}")
            return
        
        # Check if it exists in the current directory
        if os.path.exists(programmer_cli):
            self.logger.info(f"Programmer CLI found in current directory: {programmer_cli}")
            return
        
        # Try to find it in PATH
        programmer_path = shutil.which(programmer_cli)
        if programmer_path:
            self.logger.info(f"Programmer CLI found in PATH: {programmer_path}")
            return
        
        # Try to run it and check if it responds
        try:
            result = subprocess.run(
                [programmer_cli, "--version"],
                capture_output=True,
                text=True,
                timeout=5
            )
            if result.returncode == 0:
                self.logger.info(f"Programmer CLI is accessible and working")
                return
        except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
            self.logger.warning(f"Could not execute programmer CLI: {e}")
        
        # Programmer not found - announce error and exit
        error_message = f"프로그래머가 설치되어 있지 않습니다: {programmer_cli}"
        self.logger.error(f"Programmer CLI not found: {programmer_cli}")
        self.logger.error("Please install STM32_Programmer_CLI.exe or update the configuration")
        
        # Announce error in Korean
        if self.voice_announcer and self.config.get('VOICE_ANNOUNCEMENT', 'enable_voice').lower() == 'true':
            self.logger.info("Announcing programmer not installed error...")
            # Announce the error message
            self.voice_announcer.announce_error(error_message)
            # Also provide installation instructions
            installation_message = "STM32 프로그래머를 설치하거나 설정 파일을 업데이트해 주세요"
            self.voice_announcer.announce_custom(installation_message)
        
        # Exit the program
        self.logger.error("Exiting due to missing programmer CLI")
        sys.exit(1)
    
    @performance_monitor_decorator
    def scan_firmware_directory(self) -> List[str]:
        """
        Scan the firmware directory for .bin files.
        
        Returns:
            List of firmware file paths
        """
        firmware_dir = self.config.get('FILES', 'firmware_dir')
        pattern = self.config.get('FILES', 'firmware_pattern')
        
        self.logger.info(f"Scanning directory: {firmware_dir}")
        firmware_files = scan_firmware_files(firmware_dir, pattern)
        
        if not firmware_files:
            self.logger.warning("No firmware files found")
            return []
        
        self.logger.info(f"Found {len(firmware_files)} firmware files")
        return firmware_files
    
    @performance_monitor_decorator
    def process_single_file(self, firmware_file: str) -> bool:
        """
        Process a single firmware file.
        
        Args:
            firmware_file: Path to the firmware file
            
        Returns:
            True if successful, False otherwise
        """
        try:
            self.logger.info(f"Processing firmware file: {firmware_file}")
            
            # Extract serial number
            serial_number = extract_serial_number(firmware_file)
            if not serial_number:
                self.logger.error(f"Could not extract serial number from {firmware_file}")
                return False
            
            self.logger.info(f"Extracted serial number: {serial_number}")
            
            # Check if firmware file exists before proceeding
            import os
            if not os.path.exists(firmware_file):
                self.logger.error(f"Firmware file does not exist: {firmware_file}")
                return False
            
            # Check if there are any *.bin files in the firmware directory
            firmware_dir = self.config.get('FILES', 'firmware_dir')
            firmware_files = self.scan_firmware_directory()
            if not firmware_files:
                self.logger.warning("No firmware files found in directory - announcing completion")
                
                # Announce all firmware completed if enabled
                if self.voice_enabled:
                    self.logger.info("Announcing all firmware completed...")
                    success = self.voice_announcer.announce_all_firmware_completed()
                    if success:
                        self.logger.info("All firmware completed announcement successful")
                    else:
                        self.logger.warning("All firmware completed announcement failed")
                
                return True  # Return success since no files to process is normal completion
            
            # Check ST-Link connection
            self.logger.info("Checking ST-Link connection...")
            if self.stlink_sn:
                self.logger.info(f"Using ST-Link serial number: {self.stlink_sn}")
            if not self.st_link_handler.check_st_link_connection(self.stlink_sn or ""):
                self.logger.error("ST-Link connection failed")
                return False
            
            # Handle read-only mode
            if self.read_only:
                self.logger.info("Read-only mode: Performing flash check only...")
                should_program, last_value = self.flash_checker.check_flash_value(
                    self.stlink_sn or "",
                    self.flash_check_address,
                    self.flash_expected_value,
                    self.flash_max_retries,
                    self.flash_retry_delay
                )
                
                if last_value is not None:
                    if last_value == self.flash_expected_value:
                        self.logger.info(f"Flash value matches expected value: 0x{last_value:08X} - Board is programmed")
                        if self.voice_enabled:
                            self.voice_announcer.announce_board_completed_with_serial(serial_number)
                    else:
                        self.logger.warning(f"Flash value 0x{last_value:08X} does not match expected 0x{self.flash_expected_value:08X}")
                        self.logger.warning("Board is not programmed correctly")
                        if self.voice_enabled:
                            self.voice_announcer.announce_error("프로그램이 되어있지 않습니다")
                else:
                    self.logger.error("Could not read flash value")
                    if self.voice_enabled:
                        self.voice_announcer.announce_error("플래시를 읽을 수 없습니다")
                
                return True  # Always return true in read-only mode (no programming action taken)
            
            # Check flash value before programming (unless force mode is enabled)
            if not self.force_programming:
                self.logger.info("Checking flash value before programming...")
                should_program, last_value = self.flash_checker.check_flash_value(
                    self.stlink_sn or "",
                    self.flash_check_address,
                    self.flash_expected_value,
                    self.flash_max_retries,
                    self.flash_retry_delay
                )
                
                if not should_program:
                    self.logger.info("Flash check indicates no programming needed - skipping programming")
                    if last_value is not None:
                        self.logger.info(f"Flash value matches expected value: 0x{last_value:08X}")
                    
                    return True  # Return success since no programming was needed
                
                if last_value is not None:
                    self.logger.info(f"Flash check completed. Last read value: 0x{last_value:08X}")
                    if last_value == self.flash_expected_value:
                        self.logger.info("Flash value matches expected value - no programming needed")
                    else:
                        self.logger.info("Flash value differs from expected value - programming will proceed")
                else:
                    self.logger.warning("Could not read flash value, but proceeding with programming")
            else:
                self.logger.info("Force mode enabled - skipping flash check and proceeding with programming")
            
            # Verify firmware integrity
            self.logger.info("Verifying firmware integrity...")
            if not self.firmware_verifier.verify_firmware(firmware_file):
                self.logger.error("Firmware verification failed")
                return False
            
            # Announce programming start with serial number before actual programming
            if self.voice_enabled:
                self.logger.info("Announcing programming start...")
                success = self.voice_announcer.announce_programming_start(serial_number)
                if success:
                    self.logger.info("Programming start announcement successful")
                else:
                    self.logger.warning("Programming start announcement failed")
            
            # Program firmware
            self.logger.info("Programming firmware...")
            success, message = self.firmware_programmer.program_firmware(
                firmware_file, 
                self.stlink_sn or "",  # Use configured ST-Link SN or empty string
                offset=self.programming_offset,  # Use configured offset
                verify=True,
                reset=True
            )
            if not success:
                self.logger.error(f"Firmware programming failed: {message}")
                
                # Check if it's a timeout error
                if "timeout" in message.lower() or "timed out" in message.lower():
                    self.timeout_error_count += 1
                    self.consecutive_errors += 1
                    self.logger.warning(f"Timeout error count: {self.timeout_error_count}/{self.max_timeout_errors}")
                    
                    # Check if we've exceeded the maximum timeout errors
                    if self.timeout_error_count >= self.max_timeout_errors:
                        self.logger.critical(f"Maximum timeout errors ({self.max_timeout_errors}) reached. Terminating program.")
                        
                        # Announce critical error and termination
                        if self.voice_enabled:
                            self.voice_announcer.announce_error("오류가 계속 발생하여 작업을 중단합니다.")
                        
                        # Exit the program
                        sys.exit(1)
                else:
                    # Reset consecutive errors for non-timeout errors
                    self.consecutive_errors = 0
                
                # Announce error if enabled
                if self.voice_enabled and self.announce_errors:
                    self.voice_announcer.announce_error("프로그래밍 오류가 발생했습니다.")
                
                return False
            else:
                # Reset consecutive errors on success
                self.consecutive_errors = 0
            
            # Announce programming completion if enabled
            if self.voice_enabled and self.announce_programming_complete:
                self.logger.info("Announcing programming completion...")
                self.voice_announcer.announce_programming_completed()
            
            # Verify flash programming
            self.logger.info("Verifying flash programming...")
            if not self.flash_checker.verify_flash_programming(
                self.stlink_sn or "",
                self.flash_check_address,
                self.flash_expected_value
            ):
                self.logger.warning("Flash programming verification failed, but continuing...")
                # Don't fail the entire process for verification failure
            
            # Verify via serial communication
            self.logger.info("Verifying via serial communication...")
            if not self.serial_communicator.verify_firmware(serial_number):
                self.logger.warning("Serial verification failed")
                # Don't fail the entire process for serial verification failure
            
            # Change file extension
            self.logger.info("Changing file extension...")
            if not self.file_handler.change_file_extension(firmware_file):
                self.logger.warning("File extension change failed")
                # Don't fail the entire process for file extension change failure
            
            self.logger.info(f"Successfully processed {firmware_file}")
            
            # In force mode, wait for Enter key before continuing to next file
            if self.force_programming:
                self.logger.info("Force mode: Waiting for Enter key to continue...")
                
                # Announce waiting state if voice is enabled
                if self.voice_enabled:
                    self.voice_announcer.announce_custom("다음 보드를 진행하려면 엔터 키를 눌러주세요", "ko")
                
                # Wait for Enter key
                input("\nPress Enter to continue to the next board...")
                self.logger.info("Enter key pressed, continuing...")
            
            # Check if there are any remaining *.bin files after processing
            remaining_files = self.scan_firmware_directory()
            if not remaining_files:
                self.logger.info("No more firmware files remaining - all firmware work completed")
                
                # Announce all firmware work completed if enabled
                if self.voice_enabled:
                    self.logger.info("Announcing all firmware work completed...")
                    success = self.voice_announcer.announce_all_firmware_completed()
                    if success:
                        self.logger.info("All firmware work completed announcement successful")
                    else:
                        self.logger.warning("All firmware work completed announcement failed")
            
            return True
            
        except Exception as e:
            self.logger.error(f"Error processing {firmware_file}: {e}")
            
            # Check if it's a timeout exception
            error_msg = str(e).lower()
            if "timeout" in error_msg or "timed out" in error_msg:
                self.timeout_error_count += 1
                self.consecutive_errors += 1
                self.logger.warning(f"Timeout error count: {self.timeout_error_count}/{self.max_timeout_errors}")
                
                # Check if we've exceeded the maximum timeout errors
                if self.timeout_error_count >= self.max_timeout_errors:
                    self.logger.critical(f"Maximum timeout errors ({self.max_timeout_errors}) reached. Terminating program.")
                    
                    # Announce critical error and termination
                    if self.voice_enabled:
                        self.voice_announcer.announce_error("오류가 계속 발생하여 작업을 중단합니다.")
                    
                    # Exit the program
                    sys.exit(1)
            
            return False
    
    def process_batch_files(self, firmware_files: List[str]) -> dict:
        """
        Process multiple firmware files in batch.
        
        Args:
            firmware_files: List of firmware file paths
            
        Returns:
            Dictionary with processing statistics
        """
        # Check if firmware files exist at the start of processing loop
        if not firmware_files:
            self.logger.warning("No firmware files found at start of processing loop")
            
            # Announce all firmware completed if enabled
            if self.voice_enabled:
                self.logger.info("Announcing all firmware completed...")
                success = self.voice_announcer.announce_all_firmware_completed()
                if success:
                    self.logger.info("All firmware completed announcement successful")
                else:
                    self.logger.warning("All firmware completed announcement failed")
            
            # Return empty statistics
            return {
                'total_files': 0,
                'successful': 0,
                'failed': 0,
                'processing_time': 0
            }
        
        total_files = len(firmware_files)
        successful = 0
        failed = 0
        
        # Extract serial numbers and determine range
        serial_numbers = []
        for firmware_file in firmware_files:
            serial_number = extract_serial_number(firmware_file)
            if serial_number:
                serial_numbers.append(serial_number)
        
        # Sort serial numbers to get start and end
        if serial_numbers:
            serial_numbers.sort()
            start_serial = serial_numbers[0]
            end_serial = serial_numbers[-1]
            
            # Announce batch start with serial number range
            if self.voice_enabled:
                self.logger.info("Announcing batch start with serial number range...")
                success = self.voice_announcer.announce_batch_start(start_serial, end_serial, total_files)
                if success:
                    self.logger.info("Batch start announcement successful")
                else:
                    self.logger.warning("Batch start announcement failed")
        
        self.logger.info(f"Starting batch processing of {total_files} files")
        
        for i, firmware_file in enumerate(firmware_files, 1):
            self.logger.info(f"Processing file {i}/{total_files}: {firmware_file}")
            
            if self.process_single_file(firmware_file):
                successful += 1
            else:
                failed += 1
        
        stats = {
            'total': total_files,
            'successful': successful,
            'failed': failed,
            'success_rate': (successful / total_files * 100) if total_files > 0 else 0
        }
        
        self.logger.info(f"Batch processing completed: {successful}/{total_files} successful")
        return stats
    
    def run(self, args):
        """
        Run the programming system based on command line arguments.
        
        Args:
            args: Parsed command line arguments
        """
        try:
            # Start performance monitoring
            self.performance_monitor.start_monitoring()
            
            # Handle dry run mode
            if args.dry_run:
                self.logger.info("Running in dry-run mode")
                firmware_files = self.scan_firmware_directory()
                if firmware_files:
                    self.logger.info(f"Would process {len(firmware_files)} files in dry-run mode")
                else:
                    self.logger.info("No files to process in dry-run mode")
                    
                    # Announce all firmware completed if enabled (even in dry-run mode)
                    if self.voice_enabled:
                        self.logger.info("Announcing all firmware completed...")
                        success = self.voice_announcer.announce_all_firmware_completed()
                        if success:
                            self.logger.info("All firmware completed announcement successful")
                        else:
                            self.logger.warning("All firmware completed announcement failed")
                
                # Show current configuration
                self.logger.info("Current configuration:")
                self.logger.info(f"  Firmware directory: {self.config.get('FILES', 'firmware_dir')}")
                self.logger.info(f"  Programming timeout: {self.config.get('GENERAL', 'timeout')} seconds")
                self.logger.info(f"  Verify after programming: {self.config.get('PROGRAMMER', 'verify')}")
                self.logger.info(f"  Serial baudrate: {self.config.get('SERIAL', 'baudrate')}")
                self.logger.info(f"  Max retry attempts: {self.config.get('GENERAL', 'max_retries')}")
                self.logger.info(f"  Programming offset: {self.programming_offset}")
                self.logger.info(f"  Flash check address: {self.flash_check_address}")
                self.logger.info(f"  Flash expected value: 0x{self.flash_expected_value:08X}")
                self.logger.info(f"  Flash check max retries: {self.flash_max_retries}")
                self.logger.info(f"  Flash check retry delay: {self.flash_retry_delay}s")
                self.logger.info(f"  Voice announcements enabled: {self.voice_enabled}")
                if self.voice_enabled:
                    self.logger.info(f"    - Board completed announcement: {self.announce_board_completed}")
                    self.logger.info(f"    - Programming start announcement: {self.announce_programming_start}")
                    self.logger.info(f"    - Programming complete announcement: {self.announce_programming_complete}")
                    self.logger.info(f"    - Error announcements: {self.announce_errors}")
                
                # Stop monitoring and generate report
                self.performance_monitor.stop_monitoring()
                report = self.performance_monitor.generate_performance_report("dry_run_performance_report.json")
                
                # Show performance summary
                summary = self.performance_monitor.get_performance_summary()
                self.logger.info(f"Performance summary: {summary}")
                
                return 0
            
            # Handle single file mode
            if args.single_file:
                self.logger.info(f"Processing single file: {args.single_file}")
                success = self.process_single_file(args.single_file)
                
                # Stop monitoring
                self.performance_monitor.stop_monitoring()
                return 0 if success else 1
            
            # Handle batch mode
            if args.batch:
                firmware_files = self.scan_firmware_directory()
                stats = self.process_batch_files(firmware_files)
                
                # Log final statistics
                self.logger.info(f"Final statistics: {stats}")
                
                # Stop monitoring and generate report
                self.performance_monitor.stop_monitoring()
                report = self.performance_monitor.generate_performance_report("batch_performance_report.json")
                
                # Show performance summary
                summary = self.performance_monitor.get_performance_summary()
                self.logger.info(f"Performance summary: {summary}")
                
                return 0 if stats['failed'] == 0 else 1
            
            # Default: show help
            self.logger.info("No action specified. Use --batch, --single-file, or --dry-run")
            
            # Stop monitoring
            self.performance_monitor.stop_monitoring()
            return 0
            
        except KeyboardInterrupt:
            self.logger.info("Programming interrupted by user")
            self.performance_monitor.stop_monitoring()
            return 1
        except Exception as e:
            self.logger.error(f"Unexpected error: {e}")
            self.performance_monitor.stop_monitoring()
            return 1


def check_git_status():
    """
    Check if the git repository has uncommitted changes.

    Returns:
        bool: True if working directory is clean, False if dirty
    """
    try:
        # Check git status in current directory
        result = subprocess.run(
            ["git", "status", "--porcelain"],
            text=True,
            capture_output=True,
            check=True
        )

        # If output is empty, working directory is clean
        if result.stdout.strip():
            return False
        return True

    except subprocess.CalledProcessError as e:
        # Not a git repository or other git error - allow to continue
        return True
    except FileNotFoundError:
        # Git not installed - allow to continue
        return True

def main():
    """Main entry point."""
    try:
        # Parse command line arguments
        parser = argparse.ArgumentParser(description="STM32 Firmware Programming System")
        parser.add_argument('--config', type=str, help='Path to configuration file')
        parser.add_argument('--batch', action='store_true', help='Process all firmware files in batch mode')
        parser.add_argument('--single-file', type=str, help='Process only the specified firmware file')
        parser.add_argument('--dry-run', action='store_true', help='Perform a dry run without actual programming')
        parser.add_argument('--verify', action='store_true', help='Verify firmware after programming')
        parser.add_argument('--no-verify', dest='verify', action='store_false', help='Do not verify firmware after programming')
        parser.add_argument('--stlink-sn', type=str, help='ST-Link serial number')
        parser.add_argument('--force', action='store_true', help='Force programming without flash check')
        parser.add_argument('--read-only', action='store_true', help='Read-only mode: only check flash without programming')

        args = parser.parse_args()

        # Check if git working directory is clean
        if not check_git_status():
            print("\n" + "="*60)
            print("ERROR: Git working directory is not clean!")
            print("="*60)
            print("The current directory has uncommitted changes.")
            print("Please commit or stash your changes before running this program.")
            print("\nYou can check the status with: git status")
            print("To stash changes temporarily: git stash")
            print("To commit changes: git add . && git commit -m 'your message'")
            print("="*60)
            return 1
        
        # Load configuration
        config = Config()
        if args.config:
            config.load_from_file(args.config)
        
        # Override with command line arguments
        if hasattr(args, 'verify') and args.verify is not None:
            config.set('PROGRAMMER', 'verify', 'True' if args.verify else 'False')
        if args.stlink_sn:
            config.set('PROGRAMMER', 'stlink_sn', args.stlink_sn)
        
        # Check for conflicting options
        if hasattr(args, 'force') and args.force and hasattr(args, 'read_only') and args.read_only:
            print("Error: --force and --read-only options cannot be used together")
            return 1
        
        # Initialize and run the system with force and read-only flags if provided
        system = STM32ProgrammingSystem(
            config, 
            force_programming=args.force if hasattr(args, 'force') else False,
            read_only=getattr(args, 'read_only', False)
        )
        return system.run(args)
        
    except Exception as e:
        print(f"Error: {e}")
        return 1


if __name__ == "__main__":
    sys.exit(main())
