import os
import threading
from datetime import datetime

class DataLogger:
    """Main application data logger for CSEM Triggerbox operations (NOT GPS positions)"""
    
    def __init__(self, log_directory="/home/morepork/Documents/CSEM/logs"):
        self.log_directory = log_directory
        self.main_log_file = None
        self.gps_log_path = None  # Set externally by GPS reader
        self.is_logging = False
        self.log_lock = threading.RLock()  # Use RLock instead of Lock
        self.gps_reader = None  # Reference to GPS reader for final coordinates
        
        # Create log directory if it doesn't exist
        os.makedirs(self.log_directory, exist_ok=True)
        
        # Session information for logging
        self.session_info = {}
    
    def set_gps_reader(self, gps_reader):
        """Set reference to GPS reader for accessing final coordinates"""
        self.gps_reader = gps_reader
    
    def set_gps_log_path(self, path):
        """Set the GPS log file path (from GPSReader) - GPS logs are handled separately"""
        self.gps_log_path = path

    def get_current_gps_log_file(self):
        """Return the current GPS log file path"""
        return self.gps_log_path
    
    def start_logging(self, frequency, transmit_time, pause_time, rpi_time=None, gps_time=None, rtc_time=None, lat=None, lon=None, sats=None):
        """
        Start a new MAIN log file (application events, NOT GPS positions)
        
        Args:
            frequency: Transmission frequency in Hz
            transmit_time: Transmit duration in seconds
            pause_time: Pause duration in seconds
            rpi_time: Current RPI time
            gps_time: Current GPS time
            rtc_time: Current RTC time
            lat: GPS latitude
            lon: GPS longitude
            sats: Number of GPS satellites
        """
        # Check if already logging without holding lock
        if self.is_logging:
            self.stop_logging()  # This will handle its own locking
        
        # Prepare session info outside of lock
        session_info = {
            'frequency': frequency,
            'transmit_time': transmit_time,
            'pause_time': pause_time,
            'rpi_time': rpi_time,
            'gps_time': gps_time,
            'rtc_time': rtc_time,
            'lat': lat,
            'lon': lon,
            'sats': sats,
            'start_time': datetime.utcnow()
        }
        
        # Create timestamped filename for MAIN log
        now = datetime.utcnow() if rpi_time is None else rpi_time
        filename = f"main_log_{now.strftime('%Y%m%d_%H%M%S')}.txt"
        filepath = os.path.join(self.log_directory, filename)
        
        # Open file outside of lock to avoid file I/O while holding lock
        try:
            log_file = open(filepath, 'w')
        except Exception as e:
            print(f"[LOG ERROR] Failed to create main log file: {e}")
            return False
        
        # Now acquire lock only for the critical section
        with self.log_lock:
            # Store session information
            self.session_info = session_info
            self.main_log_file = log_file
            self.is_logging = True
        
        # Write header outside of lock (but after setting is_logging=True)
        try:
            self._write_header()
            print(f"[LOG] Started MAIN logging to: {filepath}")
            # Use non-blocking log call for initial event
            self._log_event_immediate("SESSION_START", "Main application logging started")
            return True
        except Exception as e:
            print(f"[LOG ERROR] Failed to write header: {e}")
            # Clean up on failure
            with self.log_lock:
                self.is_logging = False
                if self.main_log_file:
                    try:
                        self.main_log_file.close()
                    except:
                        pass
                    self.main_log_file = None
            return False
    
    def stop_logging(self):
        """Stop logging and close the current main log file"""
        # Get references outside of lock
        log_file = None
        was_logging = False
        
        with self.log_lock:
            if self.is_logging and self.main_log_file:
                was_logging = True
                log_file = self.main_log_file
                self.main_log_file = None
                self.is_logging = False
        
        # Close file outside of lock
        if was_logging and log_file:
            try:
                # Write final GPS coordinates if available
                self._write_final_gps_coordinates(log_file)
                
                # Write final message
                timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
                log_file.write(f"{timestamp} UTC - SESSION_STOP: Main application logging stopped\n")
                log_file.close()
                print(f"[LOG] Stopped main logging")
            except Exception as e:
                print(f"[LOG ERROR] Error closing main log file: {e}")
    
    def _write_final_gps_coordinates(self, log_file):
        """Write final GPS coordinates to log before closing"""
        try:
            if self.gps_reader and hasattr(self.gps_reader, 'current_gps_data'):
                gps_data = self.gps_reader.current_gps_data
                timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
                
                log_file.write("\n" + "=" * 40 + "\n")
                log_file.write("FINAL GPS POSITION:\n")
                log_file.write("=" * 40 + "\n")
                log_file.write(f"{timestamp} UTC - FINAL_GPS_POSITION:\n")
                log_file.write(f"  Latitude: {gps_data.get('lat', 'N/A')}\n")
                log_file.write(f"  Longitude: {gps_data.get('lon', 'N/A')}\n")
                log_file.write(f"  Satellites: {gps_data.get('sats', 'N/A')}\n")
                if gps_data.get('gps_datetime'):
                    log_file.write(f"  GPS Time: {gps_data['gps_datetime'].strftime('%Y-%m-%d %H:%M:%S')} UTC\n")
                log_file.write("=" * 40 + "\n")
            else:
                timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
                log_file.write(f"{timestamp} UTC - FINAL_GPS_POSITION: GPS data not available\n")
        except Exception as e:
            timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
            log_file.write(f"{timestamp} UTC - ERROR retrieving final GPS position: {e}\n")
    
    def log_event(self, event_type, message=""):
        """
        Log an application event with timestamp to MAIN log (NOT GPS log)
        
        Args:
            event_type: Type of event ('COUNTDOWN', 'TRANSMIT_START', 'TRANSMIT_STOP', etc.)
            message: Additional message or data
        """
        # Quick check without lock first
        if not self.is_logging:
            return
        
        # Use a very short timeout to prevent deadlocks
        if self.log_lock.acquire(timeout=0.1):
            try:
                if self.is_logging and self.main_log_file:
                    timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
                    log_line = f"{timestamp} UTC - {event_type}"
                    if message:
                        log_line += f": {message}"
                    log_line += "\n"
                    
                    self.main_log_file.write(log_line)
                    self.main_log_file.flush()  # Ensure data is written immediately
            except Exception as e:
                print(f"[LOG ERROR] Failed to write log entry: {e}")
            finally:
                self.log_lock.release()
        else:
            # If we can't acquire lock quickly, skip this log entry to prevent deadlock
            print(f"[LOG WARNING] Skipped log entry due to lock timeout: {event_type}")
    
    def _log_event_immediate(self, event_type, message=""):
        """Log event immediately without timeout (use only when you know it's safe)"""
        if not self.is_logging or not self.main_log_file:
            return
        
        try:
            timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
            log_line = f"{timestamp} UTC - {event_type}"
            if message:
                log_line += f": {message}"
            log_line += "\n"
            
            self.main_log_file.write(log_line)
            self.main_log_file.flush()
        except Exception as e:
            print(f"[LOG ERROR] Failed to write immediate log entry: {e}")
    
    def log_countdown(self, countdown_text):
        """Log countdown events to MAIN log"""
        if "00:00" in countdown_text:
            self.log_event("COUNTDOWN_COMPLETE", "Countdown reached zero")
        else:
            # Extract just the time part for cleaner logs
            time_part = countdown_text.replace("Countdown: ", "")
            self.log_event("COUNTDOWN", time_part)
    
    def log_transmission_start(self, frequency):
        """Log transmission start to MAIN log"""
        self.log_event("TRANSMIT_START", f"Started transmission at {frequency} Hz")
    
    def log_transmission_stop(self):
        """Log transmission stop to MAIN log"""
        self.log_event("TRANSMIT_STOP", "Stopped transmission")
    
    def log_pause_start(self, duration):
        """Log pause start to MAIN log"""
        self.log_event("PAUSE_START", f"Started {duration}s pause")
    
    def log_pause_complete(self):
        """Log pause completion to MAIN log"""
        self.log_event("PAUSE_COMPLETE", "Pause period ended")
    
    def log_cycle_complete(self):
        """Log complete cycle to MAIN log"""
        self.log_event("CYCLE_COMPLETE", "Transmission cycle completed")
    
    def log_manual_stop(self):
        """Log manual stop to MAIN log"""
        self.log_event("MANUAL_STOP", "Transmission manually stopped")
    
    def log_sync_event(self, event_type, message=""):
        """Log synchronization events to MAIN log"""
        self.log_event(f"SYNC_{event_type}", message)
    
    def log_gpio_event(self, event_type, message=""):
        """Log GPIO-related events to MAIN log"""
        self.log_event(f"GPIO_{event_type}", message)
    
    def log_timing_event(self, event_type, message=""):
        """Log timing-related events to MAIN log"""
        self.log_event(f"TIMING_{event_type}", message)
    
    def log_frequency_change(self, old_freq, new_freq):
        """Log frequency changes to MAIN log"""
        self.log_event("FREQUENCY_CHANGE", f"Changed from {old_freq:.3f} Hz to {new_freq:.3f} Hz")
    
    def log_system_event(self, event_type, message=""):
        """Log system-level events to MAIN log"""
        self.log_event(f"SYSTEM_{event_type}", message)
    
    def is_logging_active(self):
        """Check if main logging is currently active (thread-safe check)"""
        return self.is_logging  # This is atomic in Python
    
    def get_current_log_file(self):
        """Get the current main log file path"""
        with self.log_lock:
            if self.main_log_file:
                return self.main_log_file.name
        return None
    
    def get_session_info(self):
        """Get current session information"""
        with self.log_lock:
            return self.session_info.copy()
    
    def _write_header(self):
        """Write main log file header with session information"""
        # This should only be called when we know it's safe (right after opening file)
        if not self.main_log_file or not self.session_info:
            return
            
        info = self.session_info
        header = []
        header.append("=" * 60)
        header.append("MBARI CSEM TRIGGERBOX - MAIN APPLICATION LOG")
        header.append("=" * 60)
        header.append(f"Log created: {info['start_time'].strftime('%Y-%m-%d %H:%M:%S')} UTC")
        header.append("")
        header.append("NOTE: This is the MAIN APPLICATION log for events, timing, and system status.")
        header.append("GPS position data is logged separately in the GPS log file.")
        header.append("")
        
        # Time information
        header.append("TIME SYNCHRONIZATION:")
        if info['rpi_time']:
            header.append(f"  RPI Time (UTC): {info['rpi_time'].strftime('%Y-%m-%d %H:%M:%S')}")
        else:
            header.append("  RPI Time (UTC): Not available")
        
        if info['gps_time']:
            header.append(f"  GPS Time (UTC): {info['gps_time'].strftime('%Y-%m-%d %H:%M:%S')}")
        else:
            header.append("  GPS Time (UTC): Not available")
        
        if info['rtc_time']:
            header.append(f"  RTC Time (UTC): {info['rtc_time'].strftime('%Y-%m-%d %H:%M:%S')}")
        else:
            header.append("  RTC Time (UTC): Not available")
        
        header.append("")
        
        # GPS Position (snapshot at start)
        header.append("GPS POSITION (at session start):")
        header.append(f"  Latitude: {info['lat'] if info['lat'] and info['lat'] != 'N/A' else 'Not available'}")
        header.append(f"  Longitude: {info['lon'] if info['lon'] and info['lon'] != 'N/A' else 'Not available'}")
        header.append(f"  Satellites: {info['sats'] if info['sats'] and info['sats'] != 'N/A' else 'Not available'}")
        header.append("")
        
        # Transmission Settings
        header.append("TRANSMISSION SETTINGS:")
        header.append(f"  Frequency: {info['frequency']:.3f} Hz")
        header.append(f"  Period: {1.0/info['frequency']:.3f} seconds")
        header.append(f"  Transmit Time: {info['transmit_time']} seconds")
        header.append(f"  Pause Time: {info['pause_time']} seconds")
        header.append("")
        
        # GPIO Configuration
        header.append("GPIO CONFIGURATION:")
        header.append("  TX Status: GPIO 13 (Pin 33) - HIGH when transmitting")
        header.append("  Duty Signal: GPIO 6 (Pin 31) - toggles at 2x frequency")
        header.append("  Polarity Signal: GPIO 26 (Pin 37) - toggles at frequency")
        header.append("  PPS Input: GPIO 17 (Pin 11) - 1 pulse per second input")
        header.append("")
        
        # Log file information
        header.append("LOG FILES:")
        header.append(f"  Main Log: {self.main_log_file.name}")
        if self.gps_log_path:
            header.append(f"  GPS Log: {self.gps_log_path}")
        else:
            header.append("  GPS Log: Will be created when GPS reader starts")
        header.append("")
        
        header.append("=" * 60)
        header.append("APPLICATION EVENT LOG:")
        header.append("=" * 60)
        header.append("")
        
        # Write header to file
        for line in header:
            self.main_log_file.write(line + "\n")
        
        self.main_log_file.flush()


class LogManager:
    """Manager class for handling both main and GPS log coordination"""
    
    def __init__(self, log_directory="/home/morepork/Documents/CSEM/logs"):
        self.data_logger = DataLogger(log_directory)
        self.log_directory = log_directory
        
    def start_session_logging(self, session_params):
        """Start main application logging for a complete session"""
        return self.data_logger.start_logging(**session_params)
    
    def stop_session_logging(self):
        """Stop main application logging"""
        self.data_logger.stop_logging()
    
    def log_application_event(self, event_type, details=""):
        """Log application-level events to main log"""
        self.data_logger.log_event(f"APP_{event_type}", details)
    
    def set_gps_log_path(self, path):
        """Set GPS log file path (called by GPS reader)"""
        self.data_logger.set_gps_log_path(path)
    
    def set_gps_reader(self, gps_reader):
        """Set GPS reader reference for final coordinates"""
        self.data_logger.set_gps_reader(gps_reader)
    
    def get_log_status(self):
        """Get status of all logging activities"""
        return {
            'main_logging_active': self.data_logger.is_logging_active(),
            'main_log_file': self.data_logger.get_current_log_file(),
            'gps_log_file': self.data_logger.get_current_gps_log_file(),
            'session_info': self.data_logger.get_session_info()
        }
    
    def get_log_sizes(self):
        """Get sizes of all log files"""
        sizes = {'main_log_kb': 0, 'gps_log_kb': 0}
        
        main_log = self.data_logger.get_current_log_file()
        if main_log and os.path.exists(main_log):
            sizes['main_log_kb'] = os.path.getsize(main_log) / 1024
        
        gps_log = self.data_logger.get_current_gps_log_file()
        if gps_log and os.path.exists(gps_log):
            sizes['gps_log_kb'] = os.path.getsize(gps_log) / 1024
            
        return sizes


# Convenience functions for easy integration
def create_logger(log_directory="/home/morepork/Documents/CSEM/logs"):
    """Create a new DataLogger instance"""
    return DataLogger(log_directory)

def create_log_manager(log_directory="/home/morepork/Documents/CSEM/logs"):
    """Create a new LogManager instance"""
    return LogManager(log_directory)


# Test the logger
if __name__ == "__main__":
    # Test logging functionality
    logger = DataLogger()
    
    # Test start logging
    success = logger.start_logging(
        frequency=1.5,
        transmit_time=60,
        pause_time=30,
        rpi_time=datetime.utcnow(),
        gps_time=datetime.utcnow(),
        rtc_time=datetime.utcnow(),
        lat="36.12345 N",
        lon="121.67890 W",
        sats="8"
    )
    
    if success:
        # Test logging events
        logger.log_countdown("Countdown: 01:30")
        logger.log_countdown("Countdown: 01:00")
        logger.log_countdown("Countdown: 00:30")
        logger.log_countdown("Countdown: 00:00")
        logger.log_transmission_start(1.5)
        logger.log_pause_start(30)
        logger.log_pause_complete()
        logger.log_transmission_stop()
        logger.log_sync_event("SUCCESS", "RTC synchronized to GPS time")
        logger.log_gpio_event("START", "GPIO pins initialized")
        logger.log_frequency_change(1.0, 1.5)
        
        # Stop logging
        logger.stop_logging()
        
        print("Test completed successfully!")
        print(f"Main log: {logger.get_current_log_file()}")
        print(f"GPS log: {logger.get_current_gps_log_file()}")
    else:
        print("Failed to start logging!")