package cameradar

import (
	"strings"

	"github.com/ullaakut/nmap"
)

// Scan scans the target networks and tries to find RTSP streams within them.
//
// targets can be:
//
//    - a subnet (e.g.: 172.16.100.0/24)
//    - an IP (e.g.: 172.16.100.10)
//    - a hostname (e.g.: localhost)
//    - a range of IPs (e.g.: 172.16.100.10-20)
//
// ports can be:
//
//    - one or multiple ports and port ranges separated by commas (e.g.: 554,8554-8560,18554-28554)
func (s *Scanner) Scan() ([]Stream, error) {
	s.term.StartStep("Scanning the network")

	// Run nmap command to discover open ports on the specified targets & ports.
	nmapScanner, err := nmap.NewScanner(
		nmap.WithTargets(s.targets...),
		nmap.WithPorts(s.ports...),
		nmap.WithTimingTemplate(nmap.Timing(s.speed)),
	)
	if err != nil {
		return nil, s.term.FailStepf("unable to create network scanner: %v", err)
	}

	return s.scan(nmapScanner)
}

func (s *Scanner) scan(nmapScanner nmap.ScanRunner) ([]Stream, error) {
	results, err := nmapScanner.Run()
	if err != nil {
		return nil, s.term.FailStepf("error while scanning network: %v", err)
	}

	// Get streams from nmap results.
	var streams []Stream
	for _, host := range results.Hosts {
		for _, port := range host.Ports {
			if port.Status() != "open" {
				continue
			}

			if !strings.Contains(port.Service.Name, "rtsp") {
				continue
			}

			for _, address := range host.Addresses {
				streams = append(streams, Stream{
					Device:  port.Service.Product,
					Address: address.Addr,
					Port:    port.ID,
				})
			}
		}
	}

	s.term.Debugf("Found %d RTSP streams\n", len(streams))

	s.term.EndStep()

	return streams, nil
}
