// Package main provides a utility for parsing and displaying Beast format ADS-B data.
//
// beast-dump can read from TCP sockets (dump1090 streams) or files containing
// Beast binary data, decode Mode S/ADS-B messages, and display the results
// in human-readable format on the console.
//
// Usage:
//   beast-dump -tcp host:port          # Read from TCP socket
//   beast-dump -file path/to/file      # Read from file
//   beast-dump -verbose               # Show detailed message parsing
//
// Examples:
//   beast-dump -tcp svovel:30005      # Connect to dump1090 Beast stream
//   beast-dump -file beast.test       # Parse Beast data from file
//   beast-dump -tcp localhost:30005 -verbose  # Verbose TCP parsing
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"time"

	"skyview/internal/beast"
	"skyview/internal/modes"
)

// Config holds command-line configuration
type Config struct {
	TCPAddress string // TCP address for Beast stream (e.g., "localhost:30005")
	FilePath   string // File path for Beast data
	Verbose    bool   // Enable verbose output
	Count      int    // Maximum messages to process (0 = unlimited)
}

// BeastDumper handles Beast data parsing and console output
type BeastDumper struct {
	config  *Config
	parser  *beast.Parser
	decoder *modes.Decoder
	stats   struct {
		totalMessages    int64
		validMessages    int64
		aircraftSeen     map[uint32]bool
		startTime        time.Time
		lastMessageTime  time.Time
	}
}

func main() {
	config := parseFlags()
	
	if config.TCPAddress == "" && config.FilePath == "" {
		fmt.Fprintf(os.Stderr, "Error: Must specify either -tcp or -file\n")
		flag.Usage()
		os.Exit(1)
	}
	
	if config.TCPAddress != "" && config.FilePath != "" {
		fmt.Fprintf(os.Stderr, "Error: Cannot specify both -tcp and -file\n")
		flag.Usage()
		os.Exit(1)
	}

	dumper := NewBeastDumper(config)
	
	if err := dumper.Run(); err != nil {
		log.Fatalf("Error: %v", err)
	}
}

// parseFlags parses command-line flags and returns configuration
func parseFlags() *Config {
	config := &Config{}
	
	flag.StringVar(&config.TCPAddress, "tcp", "", "TCP address for Beast stream (e.g., localhost:30005)")
	flag.StringVar(&config.FilePath, "file", "", "File path for Beast data")
	flag.BoolVar(&config.Verbose, "verbose", false, "Enable verbose output")
	flag.IntVar(&config.Count, "count", 0, "Maximum messages to process (0 = unlimited)")
	
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nBeast format ADS-B data parser and console dumper\n\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExamples:\n")
		fmt.Fprintf(os.Stderr, "  %s -tcp svovel:30005\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -file beast.test\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -tcp localhost:30005 -verbose -count 100\n", os.Args[0])
	}
	
	flag.Parse()
	return config
}

// NewBeastDumper creates a new Beast data dumper
func NewBeastDumper(config *Config) *BeastDumper {
	return &BeastDumper{
		config:  config,
		decoder: modes.NewDecoder(0.0, 0.0), // beast-dump doesn't have reference position, use default
		stats: struct {
			totalMessages    int64
			validMessages    int64
			aircraftSeen     map[uint32]bool
			startTime        time.Time
			lastMessageTime  time.Time
		}{
			aircraftSeen: make(map[uint32]bool),
			startTime:    time.Now(),
		},
	}
}

// Run starts the Beast data processing
func (d *BeastDumper) Run() error {
	fmt.Printf("Beast Data Dumper\n")
	fmt.Printf("=================\n\n")
	
	var reader io.Reader
	var closer io.Closer
	
	if d.config.TCPAddress != "" {
		conn, err := d.connectTCP()
		if err != nil {
			return fmt.Errorf("TCP connection failed: %w", err)
		}
		reader = conn
		closer = conn
		fmt.Printf("Connected to: %s\n", d.config.TCPAddress)
	} else {
		file, err := d.openFile()
		if err != nil {
			return fmt.Errorf("file open failed: %w", err)
		}
		reader = file
		closer = file
		fmt.Printf("Reading file: %s\n", d.config.FilePath)
	}
	
	defer closer.Close()
	
	// Create Beast parser
	d.parser = beast.NewParser(reader, "beast-dump")
	
	fmt.Printf("Verbose mode: %t\n", d.config.Verbose)
	if d.config.Count > 0 {
		fmt.Printf("Message limit: %d\n", d.config.Count)
	}
	fmt.Printf("\nStarting Beast data parsing...\n")
	fmt.Printf("%-8s %-6s %-12s %-8s %-10s %-6s %s\n", 
		"Time", "ICAO", "Type", "Signal", "Data", "Len", "Decoded")
	fmt.Printf("%s\n", 
		"------------------------------------------------------------------------")
	
	return d.parseMessages()
}

// connectTCP establishes TCP connection to Beast stream
func (d *BeastDumper) connectTCP() (net.Conn, error) {
	fmt.Printf("Connecting to %s...\n", d.config.TCPAddress)
	
	conn, err := net.DialTimeout("tcp", d.config.TCPAddress, 10*time.Second)
	if err != nil {
		return nil, err
	}
	
	return conn, nil
}

// openFile opens Beast data file
func (d *BeastDumper) openFile() (*os.File, error) {
	file, err := os.Open(d.config.FilePath)
	if err != nil {
		return nil, err
	}
	
	// Check file size
	stat, err := file.Stat()
	if err != nil {
		file.Close()
		return nil, err
	}
	
	fmt.Printf("File size: %d bytes\n", stat.Size())
	return file, nil
}

// parseMessages processes Beast messages and outputs decoded data
func (d *BeastDumper) parseMessages() error {
	for {
		// Check message count limit
		if d.config.Count > 0 && d.stats.totalMessages >= int64(d.config.Count) {
			fmt.Printf("\nReached message limit of %d\n", d.config.Count)
			break
		}
		
		// Parse Beast message
		msg, err := d.parser.ReadMessage()
		if err != nil {
			if err == io.EOF {
				fmt.Printf("\nEnd of data reached\n")
				break
			}
			if d.config.Verbose {
				fmt.Printf("Parse error: %v\n", err)
			}
			continue
		}
		
		d.stats.totalMessages++
		d.stats.lastMessageTime = time.Now()
		
		// Display Beast message info
		d.displayMessage(msg)
		
		// Decode Mode S data if available
		if msg.Type == beast.BeastModeS || msg.Type == beast.BeastModeSLong {
			d.decodeAndDisplay(msg)
		}
		
		d.stats.validMessages++
	}
	
	d.displayStatistics()
	return nil
}

// displayMessage shows basic Beast message information
func (d *BeastDumper) displayMessage(msg *beast.Message) {
	timestamp := msg.ReceivedAt.Format("15:04:05")
	
	// Extract ICAO if available
	icao := "------"
	if msg.Type == beast.BeastModeS || msg.Type == beast.BeastModeSLong {
		if icaoAddr, err := msg.GetICAO24(); err == nil {
			icao = fmt.Sprintf("%06X", icaoAddr)
			d.stats.aircraftSeen[icaoAddr] = true
		}
	}
	
	// Beast message type
	typeStr := d.formatMessageType(msg.Type)
	
	// Signal strength
	signal := msg.GetSignalStrength()
	signalStr := fmt.Sprintf("%6.1f", signal)
	
	// Data preview
	dataStr := d.formatDataPreview(msg.Data)
	
	fmt.Printf("%-8s %-6s %-12s %-8s %-10s %-6d ", 
		timestamp, icao, typeStr, signalStr, dataStr, len(msg.Data))
}

// decodeAndDisplay attempts to decode Mode S message and display results
func (d *BeastDumper) decodeAndDisplay(msg *beast.Message) {
	aircraft, err := d.decoder.Decode(msg.Data)
	if err != nil {
		if d.config.Verbose {
			fmt.Printf("Decode error: %v\n", err)
		} else {
			fmt.Printf("(decode failed)\n")
		}
		return
	}
	
	// Display decoded information
	info := d.formatAircraftInfo(aircraft)
	fmt.Printf("%s\n", info)
	
	// Verbose details
	if d.config.Verbose {
		d.displayVerboseInfo(aircraft, msg)
	}
}

// formatMessageType converts Beast message type to string
func (d *BeastDumper) formatMessageType(msgType uint8) string {
	switch msgType {
	case beast.BeastModeAC:
		return "Mode A/C"
	case beast.BeastModeS:
		return "Mode S"
	case beast.BeastModeSLong:
		return "Mode S Long"
	case beast.BeastStatusMsg:
		return "Status"
	default:
		return fmt.Sprintf("Type %02X", msgType)
	}
}

// formatDataPreview creates a hex preview of message data
func (d *BeastDumper) formatDataPreview(data []byte) string {
	if len(data) == 0 {
		return ""
	}
	
	preview := ""
	for i, b := range data {
		if i >= 4 { // Show first 4 bytes
			break
		}
		preview += fmt.Sprintf("%02X", b)
	}
	
	if len(data) > 4 {
		preview += "..."
	}
	
	return preview
}

// formatAircraftInfo creates a summary of decoded aircraft information
func (d *BeastDumper) formatAircraftInfo(aircraft *modes.Aircraft) string {
	parts := []string{}
	
	// Callsign
	if aircraft.Callsign != "" {
		parts = append(parts, fmt.Sprintf("CS:%s", aircraft.Callsign))
	}
	
	// Position
	if aircraft.Latitude != 0 || aircraft.Longitude != 0 {
		parts = append(parts, fmt.Sprintf("POS:%.4f,%.4f", aircraft.Latitude, aircraft.Longitude))
	}
	
	// Altitude
	if aircraft.Altitude != 0 {
		parts = append(parts, fmt.Sprintf("ALT:%dft", aircraft.Altitude))
	}
	
	// Speed and track
	if aircraft.GroundSpeed != 0 {
		parts = append(parts, fmt.Sprintf("SPD:%dkt", aircraft.GroundSpeed))
	}
	if aircraft.Track != 0 {
		parts = append(parts, fmt.Sprintf("HDG:%d°", aircraft.Track))
	}
	
	// Vertical rate
	if aircraft.VerticalRate != 0 {
		parts = append(parts, fmt.Sprintf("VS:%d", aircraft.VerticalRate))
	}
	
	// Squawk
	if aircraft.Squawk != "" {
		parts = append(parts, fmt.Sprintf("SQ:%s", aircraft.Squawk))
	}
	
	// Emergency
	if aircraft.Emergency != "" && aircraft.Emergency != "None" {
		parts = append(parts, fmt.Sprintf("EMG:%s", aircraft.Emergency))
	}
	
	if len(parts) == 0 {
		return "(no data decoded)"
	}
	
	info := ""
	for i, part := range parts {
		if i > 0 {
			info += " "
		}
		info += part
	}
	
	return info
}

// displayVerboseInfo shows detailed aircraft information
func (d *BeastDumper) displayVerboseInfo(aircraft *modes.Aircraft, msg *beast.Message) {
	fmt.Printf("    Message Details:\n")
	fmt.Printf("      Raw Data: %s\n", d.formatHexData(msg.Data))
	fmt.Printf("      Timestamp: %s\n", msg.ReceivedAt.Format("15:04:05.000"))
	fmt.Printf("      Signal: %.2f dBFS\n", msg.GetSignalStrength())
	
	fmt.Printf("    Aircraft Data:\n")
	if aircraft.Callsign != "" {
		fmt.Printf("      Callsign: %s\n", aircraft.Callsign)
	}
	if aircraft.Latitude != 0 || aircraft.Longitude != 0 {
		fmt.Printf("      Position: %.6f, %.6f\n", aircraft.Latitude, aircraft.Longitude)
	}
	if aircraft.Altitude != 0 {
		fmt.Printf("      Altitude: %d ft\n", aircraft.Altitude)
	}
	if aircraft.GroundSpeed != 0 || aircraft.Track != 0 {
		fmt.Printf("      Speed/Track: %d kt @ %d°\n", aircraft.GroundSpeed, aircraft.Track)
	}
	if aircraft.VerticalRate != 0 {
		fmt.Printf("      Vertical Rate: %d ft/min\n", aircraft.VerticalRate)
	}
	if aircraft.Squawk != "" {
		fmt.Printf("      Squawk: %s\n", aircraft.Squawk)
	}
	if aircraft.Category != "" {
		fmt.Printf("      Category: %s\n", aircraft.Category)
	}
	fmt.Printf("\n")
}

// formatHexData creates a formatted hex dump of data
func (d *BeastDumper) formatHexData(data []byte) string {
	result := ""
	for i, b := range data {
		if i > 0 {
			result += " "
		}
		result += fmt.Sprintf("%02X", b)
	}
	return result
}

// displayStatistics shows final parsing statistics
func (d *BeastDumper) displayStatistics() {
	duration := time.Since(d.stats.startTime)
	
	fmt.Printf("\nStatistics:\n")
	fmt.Printf("===========\n")
	fmt.Printf("Total messages: %d\n", d.stats.totalMessages)
	fmt.Printf("Valid messages: %d\n", d.stats.validMessages)
	fmt.Printf("Unique aircraft: %d\n", len(d.stats.aircraftSeen))
	fmt.Printf("Duration: %v\n", duration.Round(time.Second))
	
	if d.stats.totalMessages > 0 && duration > 0 {
		rate := float64(d.stats.totalMessages) / duration.Seconds()
		fmt.Printf("Message rate: %.1f msg/sec\n", rate)
	}
	
	if len(d.stats.aircraftSeen) > 0 {
		fmt.Printf("\nAircraft seen:\n")
		for icao := range d.stats.aircraftSeen {
			fmt.Printf("  %06X\n", icao)
		}
	}
}