#!/bin/bash

# Enhanced Autoclicker script for Sway with improved antiban features
# Uses ydotool for Wayland/Sway environments
# Features human-like timing patterns and CSV logging for visualization
# Press Ctrl+C to stop (standard shell interrupt)
# Requires ydotool and ydotoold daemon running

# Configuration
MIN_DELAY=50       # Minimum delay in milliseconds
MAX_DELAY=700      # Maximum delay in milliseconds
MIN_CLICKS_BEFORE_BREAK=30   # Minimum clicks before break
MAX_CLICKS_BEFORE_BREAK=200  # Maximum clicks before break
MIN_BREAK=3         # Minimum break duration in seconds
MAX_BREAK=15        # Maximum break duration in seconds

# Human-like timing parameters
FASTIGUE_FACTOR=0.95  # Gradually slow down over time (0.95 = 5% slower)
REACTION_VARIATION=0.3 # 30% variation in reaction times
PATTERN_BREAK_CHANCE=0.05 # 5% chance to break pattern

# File paths
STATE_FILE="/tmp/autoclicker_running"
LOCK_FILE="/tmp/autoclicker_lock"
#LOG_DIR="/tmp/autoclicker_logs"
LOG_DIR="/home/raga/repos/linux/sway-new-config/scripts/osrs/autoclicker/logs"
LOG_FILE="$LOG_DIR/click_timings_$(date +%Y%m%d_%H%M%S).csv"

export YDOTOOL_SOCKET="$HOME/.ydotool_socket"

# Create log directory if it doesn't exist
mkdir -p "$LOG_DIR"

# Initialize CSV log file with header
echo "click_number,delay_ms,timestamp" > "$LOG_FILE"

# Check if ydotool is available
if ! command -v ydotool &> /dev/null; then
    echo "Error: ydotool is not installed. Install it with: sudo pacman -S ydotool"
    exit 1
fi

# Check if bc is available (needed for calculations)
if ! command -v bc &> /dev/null; then
    echo "Error: bc is not installed. Install it with: sudo pacman -S bc"
    exit 1
fi

# Check if script is already running (prevent multiple instances)
if [ -f "$LOCK_FILE" ]; then
    echo "Autoclicker is already running!"
    echo "Press Ctrl+C in the terminal where it's running to stop it."
    # Clean up the incomplete log file that was just created
    rm -f "$LOG_FILE"
    exit 0
fi

# Create lock file
echo $$ > "$LOCK_FILE"

# Global exit flag
EXIT_REQUESTED=0

# Clean up on exit
cleanup() {
    EXIT_REQUESTED=1
    rm -f "$LOCK_FILE"
    rm -f "$STATE_FILE"
    echo ""
    echo "Autoclicker stopped."
    exit 0
}

# Set up traps for cleanup
trap cleanup INT TERM

# Check if ydotoold daemon is running
if ! pgrep -x "ydotoold" > /dev/null; then
    echo "Starting ydotoold daemon..."
    sudo ydotoold &
    sleep 1
fi

# Create state file to indicate running
echo "running" > "$STATE_FILE"

# Function to log click timing to CSV
log_click_timing() {
    local click_number=$1
    local delay_ms=$2
    local timestamp=$(date +%s%3N)  # Milliseconds since epoch
    echo "$click_number,$delay_ms,$timestamp" >> "$LOG_FILE"
}

# Function to generate random delay with human-like distribution
get_human_delay() {
    local min=$1
    local max=$2
    local base_delay=$((min + RANDOM % (max - min + 1)))

    # Apply reaction time variation (30% up or down)
    local variation=$(echo "scale=3; $base_delay * $REACTION_VARIATION * (2 * $RANDOM / 32767 - 1)" | bc)
    local varied_delay=$(echo "$base_delay + $variation" | bc)

    # Ensure we stay within reasonable bounds
    if (( $(echo "$varied_delay < $min" | bc -l) )); then
        varied_delay=$min
    elif (( $(echo "$varied_delay > $max" | bc -l) )); then
        varied_delay=$max
    fi

    # Convert to seconds for sleep function
    echo "scale=3; $varied_delay / 1000" | bc
}

# Function to generate random number of clicks before break
get_random_clicks() {
    local min=$1
    local max=$2
    echo $((min + RANDOM % (max - min + 1)))
}

# Function to sleep with interrupt checking
interruptible_sleep() {
    local duration=$1
    local start_time=$(date +%s.%N)
    local end_time=$(echo "$start_time + $duration" | bc)

    while true; do
        if [ $EXIT_REQUESTED -eq 1 ]; then
            return 1  # Exit requested
        fi

        local current_time=$(date +%s.%N)
        if (( $(echo "$current_time >= $end_time" | bc -l) )); then
            return 0  # Sleep completed
        fi

        sleep 0.1
    done
}

# Function to apply fatigue effect (gradually slow down)
apply_fatigue() {
    local current_delay=$1
    local click_count=$2
    local fatigue_effect=$(echo "scale=3; $current_delay * (1 + $FASTIGUE_FACTOR * $click_count / 1000)" | bc)
    echo "$fatigue_effect"
}

# Function to randomly break pattern (5% chance)
should_break_pattern() {
    local threshold=$(echo "$PATTERN_BREAK_CHANCE * 32767" | bc | cut -d'.' -f1)
    [ $RANDOM -lt $threshold ]
}

echo "=========================================="
echo "Enhanced Autoclicker started!"
echo "=========================================="
echo "Configuration:"
echo "  - Click delay: ${MIN_DELAY}-${MAX_DELAY}ms (human-like variation)"
echo "  - Break every: ${MIN_CLICKS_BEFORE_BREAK}-${MAX_CLICKS_BEFORE_BREAK} clicks (random)"
echo "  - Break duration: ${MIN_BREAK}-${MAX_BREAK}s"
echo "  - Fatigue factor: ${FASTIGUE_FACTOR} (gradual slowdown)"
echo "  - Pattern break chance: ${PATTERN_BREAK_CHANCE} (5%)"
echo "  - Logging to: $LOG_FILE"
echo ""
echo "Press Ctrl+C to stop"
echo "=========================================="
echo ""

click_count=0
total_clicks=0
clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK)
echo "Next break will be after $clicks_before_break clicks"
echo ""

while true; do
    if [ $EXIT_REQUESTED -eq 1 ]; then
        echo "Exit signal received!"
        break
    fi

    # Generate base random delay
    base_delay=$(get_human_delay $MIN_DELAY $MAX_DELAY)

    # Apply fatigue effect (gradually slow down)
    final_delay=$(apply_fatigue "$base_delay" "$click_count")

    # Randomly break pattern (5% chance)
    if should_break_pattern; then
        # Add a longer, unpredictable delay
        pattern_break_delay=$(echo "scale=3; ($MAX_DELAY * 2 + $RANDOM % 1000) / 1000" | bc)
        echo "[Pattern Break] Adding unpredictable delay of $(echo "$pattern_break_delay * 1000" | bc | cut -d'.' -f1)ms"

        if ! interruptible_sleep "$pattern_break_delay"; then
            break
        fi
    fi

    # Perform click (suppress ydotool output)
    ydotool click 0xC0 2>/dev/null
    click_count=$((click_count + 1))
    total_clicks=$((total_clicks + 1))

    # Convert delay to milliseconds for display
    delay_ms=$(echo "$final_delay * 1000" | bc | cut -d'.' -f1)

    # Log click timing to CSV
    log_click_timing "$total_clicks" "$delay_ms"

    # Show each click with its delay
    echo "Click #$total_clicks (delay: ${delay_ms}ms, next break in $((clicks_before_break - click_count)) clicks)"

    # Check if it's time for a break
    if [ $click_count -ge $clicks_before_break ]; then
        break_duration=$(get_human_delay $((MIN_BREAK * 1000)) $((MAX_BREAK * 1000)))
        echo ""
        echo "[Break] Completed $clicks_before_break clicks (total: $total_clicks). Taking a break for ${break_duration}s..."

        if ! interruptible_sleep "$break_duration"; then
            break
        fi

        click_count=0
        # Generate new random click count for next cycle
        clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK)
        echo "[Resumed] Next break will be after $clicks_before_break clicks"
        echo ""
    else
        # Wait the calculated delay before next click
        if ! interruptible_sleep "$final_delay"; then
            break
        fi
    fi
done

echo "Click timings saved to: $LOG_FILE"
