#!/usr/bin/env python3
"""
Flyff - Leveling Predictor Node:
- Tracks the last N changes in EXP values.
- Calculates the average change rate and time intervals.
- Predicts the estimated time to reach level 100.
"""

import time
import numpy as np
from OdenGraphQt import BaseNode
from PyQt5.QtCore import QTimer
from Modules import data_manager

class FlyffLevelingPredictorNode(BaseNode):
    __identifier__ = "bunny-lab.io.flyff_leveling_predictor_node"
    NODE_NAME = "Flyff - Leveling Predictor"

    def __init__(self):
        super(FlyffLevelingPredictorNode, self).__init__()

        # Input port for EXP values
        self.add_input("exp", "EXP")

        # User-defined number of changes to track
        self.add_text_input("exp_track_count", "# of EXP Changes to Track", text="7")

        # Output widgets
        self.add_text_input("time_to_level", "Time to Level", text="Calculating...")
        self.add_text_input("time_between_kills", "Time Between Kills", text="N/A")
        self.add_text_input("exp_per_kill", "EXP Per Kill", text="N/A")

        # Internal tracking lists
        self.exp_history = []
        self.time_intervals = []
        self.last_exp_value = None
        self.last_update_time = None

        # Timer to periodically process EXP changes
        self.timer = QTimer()
        self.timer.timeout.connect(self.process_exp_change)
        self.timer.start(1000)  # Check for updates every second

    def reset_tracking_arrays(self):
        """
        Resets the EXP history and time interval arrays when a level-up is detected.
        """
        self.exp_history.clear()
        self.time_intervals.clear()
        self.last_exp_value = None
        self.last_update_time = None

    def process_exp_change(self):
        """
        Monitors changes in EXP values and calculates various statistics.
        """
        exp_value = data_manager.get_data().get("exp", None)
        if exp_value is None:
            return

        exp_track_count = self.get_property("exp_track_count")
        try:
            exp_track_count = int(exp_track_count)
        except ValueError:
            exp_track_count = 7  # Default to 7 if invalid input

        # Reset if EXP value decreases (indicating a level-up)
        if self.last_exp_value is not None and exp_value < self.last_exp_value:
            self.reset_tracking_arrays()

        if self.last_exp_value is not None and exp_value != self.last_exp_value:
            current_time = time.time()

            # Store EXP change history
            self.exp_history.append(exp_value)
            if len(self.exp_history) > exp_track_count:
                self.exp_history.pop(0)

            # Store time intervals
            if self.last_update_time is not None:
                interval = current_time - self.last_update_time
                self.time_intervals.append(interval)
                if len(self.time_intervals) > exp_track_count:
                    self.time_intervals.pop(0)

            # Perform calculations
            self.calculate_time_to_level()
            self.calculate_additional_metrics()

            # Update last tracking values
            self.last_update_time = current_time

        self.last_exp_value = exp_value

    def calculate_time_to_level(self):
        """
        Calculates the estimated time to reach level 100 based on EXP change history.
        """
        if len(self.exp_history) < 2 or len(self.time_intervals) < 1:
            self.set_property("time_to_level", "Insufficient data")
            return

        exp_deltas = np.diff(self.exp_history)  # Compute EXP change per interval
        avg_exp_change = np.mean(exp_deltas) if len(exp_deltas) > 0 else 0
        avg_time_change = np.mean(self.time_intervals)

        if avg_exp_change <= 0:
            self.set_property("time_to_level", "Not gaining EXP")
            return

        current_exp = self.exp_history[-1]
        remaining_exp = 100.0 - current_exp  # Distance to level 100

        estimated_time = (remaining_exp / avg_exp_change) * avg_time_change

        # Convert estimated time into hours, minutes, and seconds
        hours = int(estimated_time // 3600)
        minutes = int((estimated_time % 3600) // 60)
        seconds = int(estimated_time % 60)

        time_str = f"{hours}h {minutes}m {seconds}s"
        self.set_property("time_to_level", time_str)

    def calculate_additional_metrics(self):
        """
        Calculates and updates the "Time Between Kills" and "EXP Per Kill".
        """
        if len(self.time_intervals) > 0:
            avg_time_between_kills = np.mean(self.time_intervals)
            minutes = int(avg_time_between_kills // 60)
            seconds = int(avg_time_between_kills % 60)
            self.set_property("time_between_kills", f"{minutes}m {seconds}s")
        else:
            self.set_property("time_between_kills", "N/A")

        if len(self.exp_history) > 1:
            exp_deltas = np.diff(self.exp_history)
            avg_exp_per_kill = np.mean(exp_deltas) if len(exp_deltas) > 0 else 0
            self.set_property("exp_per_kill", f"{avg_exp_per_kill:.2f}%")
        else:
            self.set_property("exp_per_kill", "N/A")