use std::env;
use std::net::UdpSocket;

#[derive(Clone, Copy)]
enum CommandType {
    Melody,
    Alarm,
    Stop,
}

impl CommandType {
    fn code(&self) -> u16 {
        match self {
            CommandType::Melody => 0x3001,
            CommandType::Alarm => 0x5001,
            CommandType::Stop => 0x5002,
        }
    }
    
    fn name(&self) -> &'static str {
        match self {
            CommandType::Melody => "melody",
            CommandType::Alarm => "alarm",
            CommandType::Stop => "stop",
        }
    }
}

fn compute_psa_checksum(data: &[u8]) -> [u8; 2] {
    let mut var_e: u16 = 0x0000;
    for (i, &b) in data.iter().enumerate() {
        var_e ^= (b as u16).wrapping_add(i as u16) & 0xFFFF;
    }
    var_e.to_be_bytes()
}

fn build_zone_bytes(zones: &[u8]) -> [u8; 12] {
    let mut zone_bytes = [0u8; 12];
    
    // Special case: zone 0 means all zones (set all bits)
    if zones.contains(&0) {
        return [0xff; 12];
    }
    
    for &zone in zones {
        if zone > 100 {
            continue;
        }
        let idx = (zone - 1) / 8;
        let bit = (zone - 1) % 8;
        zone_bytes[idx as usize] |= 1 << bit;
    }
    zone_bytes
}

fn main() {
    let args: Vec<String> = env::args().collect();
    
    // Show help if no arguments provided
    if args.len() == 1 {
        eprintln!("sigma-sender - Send command packets to Bodet PSA System

Usage: sigma-sender [OPTIONS] --command <COMMAND> --zones <ZONES>... [--melody <MELODY>] [--volume <VOLUME>] [--repeats <REPEATS>]

Options:
  -i, --ip <IP>              IP address to send to [default: 239.192.55.1]
  -p, --port <PORT>          UDP port [default: 1681]
  -c, --command <COMMAND>    Command type: melody, alarm, stop
  -m, --melody <MELODY>      Melody number (1-30, required for melody/alarm)
  -v, --volume <VOLUME>      Volume level (1-8, required for melody/alarm)
  -r, --repeats <REPEATS>    Repeat count (0=infinite, 1-8, required for melody/alarm)
  -z, --zones <ZONES>...     Zone numbers (0=all zones, 1-100, space-separated)
      --stop-all             Quick stop all zones (equivalent to: --command stop --zones 0)
      --debug                Show debug information
  -h, --help                 Print help

Examples:
  sigma-sender --command melody -m 5 -v 4 -r 2 -z 8
      # Send melody 5, volume 4, repeat 2 times to zone 8
  sigma-sender --command alarm -m 9 -v 8 -r 0 -z 1 2 3
      # Send alarm 9, volume 8, infinite repeats to zones 1, 2, and 3
  sigma-sender --command stop -z 0
      # Stop all zones
  sigma-sender --stop-all
      # Quick stop all zones
  sigma-sender --debug --command melody -m 1 -v 1 -r 1 -z 8
      # Send melody 1 to zone 8 and show packet hex

Notes:
  - Commands: melody (3001), alarm (5001), stop (5002)
  - Melodies: 1-30 (hex in protocol, decimal here)
  - Volume: 1-8 (max 8)
  - Repeats: 0=infinite, 1-8 (finite)
  - Zones: 0=all zones, 1-100 (multiple allowed)
  - Stop command ignores melody/volume/repeats parameters
  - Defaults: IP=239.192.55.1, Port=1681");
        std::process::exit(1);
    }

    // Parse command line arguments
    let mut ip = "239.192.55.1".to_string();
    let mut port = 1681u16;
    let mut debug = false;
    let mut command: Option<CommandType> = None;
    let mut melody: Option<u8> = None;
    let mut volume: Option<u8> = None;
    let mut repeats: Option<u8> = None;
    let mut zones: Vec<u8> = Vec::new();
    let mut stop_all = false;
    
    let mut idx = 1;
    while idx < args.len() {
        match args[idx].as_str() {
            "-i" | "--ip" => {
                if idx + 1 < args.len() {
                    ip = args[idx + 1].clone();
                    idx += 2;
                } else {
                    eprintln!("Error: --ip requires a value");
                    std::process::exit(1);
                }
            }
            "-p" | "--port" => {
                if idx + 1 < args.len() {
                    port = args[idx + 1].parse().unwrap_or_else(|_| {
                        eprintln!("Error: Invalid port number");
                        std::process::exit(1);
                    });
                    idx += 2;
                } else {
                    eprintln!("Error: --port requires a value");
                    std::process::exit(1);
                }
            }
            "-c" | "--command" => {
                if idx + 1 < args.len() {
                    command = Some(match args[idx + 1].as_str() {
                        "melody" => CommandType::Melody,
                        "alarm" => CommandType::Alarm,
                        "stop" => CommandType::Stop,
                        _ => {
                            eprintln!("Error: Invalid command type. Use: melody, alarm, or stop");
                            std::process::exit(1);
                        }
                    });
                    idx += 2;
                } else {
                    eprintln!("Error: --command requires a value");
                    std::process::exit(1);
                }
            }
            "-m" | "--melody" => {
                if idx + 1 < args.len() {
                    melody = Some(args[idx + 1].parse().unwrap_or_else(|_| {
                        eprintln!("Error: Invalid melody number");
                        std::process::exit(1);
                    }));
                    idx += 2;
                } else {
                    eprintln!("Error: --melody requires a value");
                    std::process::exit(1);
                }
            }
            "-v" | "--volume" => {
                if idx + 1 < args.len() {
                    volume = Some(args[idx + 1].parse().unwrap_or_else(|_| {
                        eprintln!("Error: Invalid volume level");
                        std::process::exit(1);
                    }));
                    idx += 2;
                } else {
                    eprintln!("Error: --volume requires a value");
                    std::process::exit(1);
                }
            }
            "-r" | "--repeats" => {
                if idx + 1 < args.len() {
                    repeats = Some(args[idx + 1].parse().unwrap_or_else(|_| {
                        eprintln!("Error: Invalid repeat count");
                        std::process::exit(1);
                    }));
                    idx += 2;
                } else {
                    eprintln!("Error: --repeats requires a value");
                    std::process::exit(1);
                }
            }
            "-z" | "--zones" => {
                idx += 1;
                while idx < args.len() && !args[idx].starts_with('-') {
                    let zone = args[idx].parse().unwrap_or_else(|_| {
                        eprintln!("Error: Invalid zone number: {}", args[idx]);
                        std::process::exit(1);
                    });
                    zones.push(zone);
                    idx += 1;
                }
            }
            "--stop-all" => {
                stop_all = true;
                idx += 1;
            }
            "--debug" => {
                debug = true;
                idx += 1;
            }
            "-h" | "--help" => {
                std::process::exit(0);
            }
            _ => {
                eprintln!("Error: Unknown argument: {}", args[idx]);
                std::process::exit(1);
            }
        }
    }

    // Handle --stop-all shortcut
    if stop_all {
        command = Some(CommandType::Stop);
        zones = vec![0]; // All zones
    }

    // Validate required arguments
    let command = command.unwrap_or_else(|| {
        eprintln!("Error: --command is required (use --stop-all for quick stop)");
        std::process::exit(1);
    });
    
    if zones.is_empty() {
        eprintln!("Error: --zones is required");
        std::process::exit(1);
    }

    // Validate command-specific requirements
    match command {
        CommandType::Melody | CommandType::Alarm => {
            let melody = melody.unwrap_or_else(|| {
                eprintln!("Error: --melody is required for {} command", command.name());
                std::process::exit(1);
            });
            let volume = volume.unwrap_or_else(|| {
                eprintln!("Error: --volume is required for {} command", command.name());
                std::process::exit(1);
            });
            let repeats = repeats.unwrap_or_else(|| {
                eprintln!("Error: --repeats is required for {} command", command.name());
                std::process::exit(1);
            });

            // Validate ranges
            if !(1..=30).contains(&melody) || !(1..=8).contains(&volume) || repeats > 8 {
                eprintln!("Error: Melody (1-30), Volume (1-8), Repeats (0-8, 0=infinite)");
                std::process::exit(1);
            }

            // Build melody/alarm packet
            let mut packet: Vec<u8> = Vec::new();
            packet.extend_from_slice(b"MEL");
            packet.extend_from_slice(&0x0021u16.to_be_bytes());
            packet.extend_from_slice(&[0x01, 0x00]);
            packet.extend_from_slice(&[0x28, 0xff]);
            packet.extend_from_slice(&command.code().to_be_bytes());
            packet.extend_from_slice(&build_zone_bytes(&zones));
            packet.extend_from_slice(&[0x00, 0x01]);
            packet.push(volume);
            packet.push(repeats);
            packet.push(0x01);
            packet.push(melody);
            packet.extend_from_slice(&[0x01, 0x00]);

            let checksum = compute_psa_checksum(&packet);
            packet.extend_from_slice(&checksum);

            if debug {
                let hexstr = packet.iter().map(|b| format!("{:02x}", b)).collect::<Vec<_>>().join("");
                println!("DEBUG: Packet hex: {}", hexstr);
            }

            let addr = format!("{}:{}", ip, port);
            let sock = UdpSocket::bind("0.0.0.0:0").expect("bind failed");
            sock.set_multicast_ttl_v4(2).ok();
            sock.send_to(&packet, &addr).expect("send failed");

            let repeat_str = if repeats == 0 { "infinite".to_string() } else { repeats.to_string() };
            println!("Sent {} {} (vol {}, rep {}, zones {:?}) to {}", 
                command.name(), melody, volume, repeat_str, zones, addr);
        }
        CommandType::Stop => {
            // Build stop packet (different format, shorter)
            let mut packet: Vec<u8> = Vec::new();
            packet.extend_from_slice(b"MEL");
            packet.extend_from_slice(&0x001bu16.to_be_bytes()); // 27 bytes for stop (to accommodate 2-byte checksum)
            packet.extend_from_slice(&[0x01, 0x00]);
            packet.extend_from_slice(&[0x2d, 0xff]); // Different sequence for stop
            packet.extend_from_slice(&command.code().to_be_bytes());
            packet.extend_from_slice(&build_zone_bytes(&zones));
            packet.extend_from_slice(&[0x0f, 0x01]); // Different ending for stop

            let checksum = compute_psa_checksum(&packet);
            packet.extend_from_slice(&checksum); // This adds 2 bytes

            if debug {
                let hexstr = packet.iter().map(|b| format!("{:02x}", b)).collect::<Vec<_>>().join("");
                println!("DEBUG: Packet hex: {}", hexstr);
                println!("DEBUG: Packet length: {} bytes", packet.len());
            }

            let addr = format!("{}:{}", ip, port);
            let sock = UdpSocket::bind("0.0.0.0:0").expect("bind failed");
            sock.set_multicast_ttl_v4(2).ok();
            sock.send_to(&packet, &addr).expect("send failed");

            let zone_str = if zones.contains(&0) { "all zones".to_string() } else { format!("zones {:?}", zones) };
            println!("Sent stop command to {} at {}", zone_str, addr);
        }
    }
}