package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/0x524a/onvif-go"
	"github.com/0x524a/onvif-go/discovery"
)

// This is a comprehensive demonstration of all onvif-go features
func main() {
	// Step 1: Discover cameras on the network
	fmt.Println("=== Step 1: Discovering ONVIF Cameras ===")
	discoverCameras()

	// Step 2: Connect to a specific camera
	fmt.Println("\n=== Step 2: Connecting to Camera ===")
	client := connectToCamera()

	// Step 3: Get device information
	fmt.Println("\n=== Step 3: Getting Device Information ===")
	getDeviceInfo(client)

	// Step 4: Get media profiles and streams
	fmt.Println("\n=== Step 4: Getting Media Profiles ===")
	profiles := getMediaProfiles(client)

	// Step 5: Control PTZ
	if len(profiles) > 0 {
		fmt.Println("\n=== Step 5: PTZ Control ===")
		controlPTZ(client, profiles[0].Token)
	}

	// Step 6: Adjust imaging settings
	if len(profiles) > 0 && profiles[0].VideoSourceConfiguration != nil {
		fmt.Println("\n=== Step 6: Adjusting Imaging Settings ===")
		adjustImaging(client, profiles[0].VideoSourceConfiguration.SourceToken)
	}

	fmt.Println("\n=== All operations completed successfully! ===")
}

// discoverCameras demonstrates network discovery
func discoverCameras() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	devices, err := discovery.Discover(ctx, 5*time.Second)
	if err != nil {
		log.Printf("Discovery error: %v", err)
		return
	}

	fmt.Printf("Found %d device(s):\n", len(devices))
	for i, device := range devices {
		fmt.Printf("  [%d] %s at %s\n", i+1, device.GetName(), device.GetDeviceEndpoint())
	}
}

// connectToCamera creates and initializes a client
func connectToCamera() *onvif.Client {
	// Replace with your camera's details
	endpoint := "http://192.168.1.100/onvif/device_service"
	username := "admin"
	password := "password"

	client, err := onvif.NewClient(
		endpoint,
		onvif.WithCredentials(username, password),
		onvif.WithTimeout(30*time.Second),
	)
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	// Initialize to discover service endpoints
	ctx := context.Background()
	if err := client.Initialize(ctx); err != nil {
		log.Fatalf("Failed to initialize: %v", err)
	}

	fmt.Printf("Connected to: %s\n", endpoint)
	return client
}

// getDeviceInfo retrieves and displays device information
func getDeviceInfo(client *onvif.Client) {
	ctx := context.Background()

	info, err := client.GetDeviceInformation(ctx)
	if err != nil {
		log.Printf("Failed to get device info: %v", err)
		return
	}

	fmt.Printf("Manufacturer: %s\n", info.Manufacturer)
	fmt.Printf("Model: %s\n", info.Model)
	fmt.Printf("Firmware: %s\n", info.FirmwareVersion)
	fmt.Printf("Serial: %s\n", info.SerialNumber)

	// Get capabilities
	caps, err := client.GetCapabilities(ctx)
	if err != nil {
		log.Printf("Failed to get capabilities: %v", err)
		return
	}

	fmt.Println("\nSupported Services:")
	if caps.Media != nil {
		fmt.Printf("  ✓ Media (Streaming)\n")
	}
	if caps.PTZ != nil {
		fmt.Printf("  ✓ PTZ (Pan/Tilt/Zoom)\n")
	}
	if caps.Imaging != nil {
		fmt.Printf("  ✓ Imaging (Image Settings)\n")
	}
	if caps.Events != nil {
		fmt.Printf("  ✓ Events\n")
	}
}

// getMediaProfiles retrieves media profiles and stream URIs
func getMediaProfiles(client *onvif.Client) []*onvif.Profile {
	ctx := context.Background()

	profiles, err := client.GetProfiles(ctx)
	if err != nil {
		log.Printf("Failed to get profiles: %v", err)
		return nil
	}

	fmt.Printf("Found %d profile(s):\n", len(profiles))

	for i, profile := range profiles {
		fmt.Printf("\nProfile [%d]: %s\n", i+1, profile.Name)

		// Video configuration
		if profile.VideoEncoderConfiguration != nil {
			fmt.Printf("  Encoding: %s\n", profile.VideoEncoderConfiguration.Encoding)
			if profile.VideoEncoderConfiguration.Resolution != nil {
				fmt.Printf("  Resolution: %dx%d\n",
					profile.VideoEncoderConfiguration.Resolution.Width,
					profile.VideoEncoderConfiguration.Resolution.Height)
			}
		}

		// Get stream URI
		streamURI, err := client.GetStreamURI(ctx, profile.Token)
		if err != nil {
			fmt.Printf("  Stream URI: Error - %v\n", err)
		} else {
			fmt.Printf("  Stream URI: %s\n", streamURI.URI)
		}

		// Get snapshot URI
		snapshotURI, err := client.GetSnapshotURI(ctx, profile.Token)
		if err != nil {
			fmt.Printf("  Snapshot URI: Error - %v\n", err)
		} else {
			fmt.Printf("  Snapshot URI: %s\n", snapshotURI.URI)
		}
	}

	return profiles
}

// controlPTZ demonstrates PTZ operations
func controlPTZ(client *onvif.Client, profileToken string) {
	ctx := context.Background()

	// Get current status
	status, err := client.GetStatus(ctx, profileToken)
	if err != nil {
		log.Printf("PTZ not supported: %v", err)
		return
	}

	fmt.Println("PTZ is supported!")

	if status.Position != nil && status.Position.PanTilt != nil {
		fmt.Printf("Current Position: Pan=%.2f, Tilt=%.2f\n",
			status.Position.PanTilt.X,
			status.Position.PanTilt.Y)
	}

	// Get presets
	presets, err := client.GetPresets(ctx, profileToken)
	if err != nil {
		log.Printf("Failed to get presets: %v", err)
	} else {
		fmt.Printf("Available Presets: %d\n", len(presets))
		for _, preset := range presets {
			fmt.Printf("  - %s\n", preset.Name)
		}
	}

	// Demonstrate movement (commented out to avoid camera movement)
	/*
		// Move right
		velocity := &onvif.PTZSpeed{
			PanTilt: &onvif.Vector2D{X: 0.3, Y: 0.0},
		}
		timeout := "PT1S"
		if err := client.ContinuousMove(ctx, profileToken, velocity, &timeout); err != nil {
			log.Printf("Move failed: %v", err)
		}
		time.Sleep(1 * time.Second)
		client.Stop(ctx, profileToken, true, false)

		// Return to home
		home := &onvif.PTZVector{
			PanTilt: &onvif.Vector2D{X: 0.0, Y: 0.0},
		}
		client.AbsoluteMove(ctx, profileToken, home, nil)
	*/

	fmt.Println("PTZ operations available (commented out in demo)")
}

// adjustImaging demonstrates imaging settings
func adjustImaging(client *onvif.Client, videoSourceToken string) {
	ctx := context.Background()

	// Get current settings
	settings, err := client.GetImagingSettings(ctx, videoSourceToken)
	if err != nil {
		log.Printf("Failed to get imaging settings: %v", err)
		return
	}

	fmt.Println("Current Imaging Settings:")
	if settings.Brightness != nil {
		fmt.Printf("  Brightness: %.1f\n", *settings.Brightness)
	}
	if settings.Contrast != nil {
		fmt.Printf("  Contrast: %.1f\n", *settings.Contrast)
	}
	if settings.ColorSaturation != nil {
		fmt.Printf("  Saturation: %.1f\n", *settings.ColorSaturation)
	}
	if settings.Sharpness != nil {
		fmt.Printf("  Sharpness: %.1f\n", *settings.Sharpness)
	}

	if settings.Exposure != nil {
		fmt.Printf("  Exposure Mode: %s\n", settings.Exposure.Mode)
	}

	if settings.Focus != nil {
		fmt.Printf("  Focus Mode: %s\n", settings.Focus.AutoFocusMode)
	}

	if settings.WhiteBalance != nil {
		fmt.Printf("  White Balance: %s\n", settings.WhiteBalance.Mode)
	}

	// Demonstrate setting adjustment (commented out to avoid changes)
	/*
		// Adjust brightness
		newBrightness := 55.0
		settings.Brightness = &newBrightness

		if err := client.SetImagingSettings(ctx, videoSourceToken, settings, true); err != nil {
			log.Printf("Failed to set imaging settings: %v", err)
		} else {
			fmt.Println("\nImaging settings updated!")
		}
	*/

	fmt.Println("Imaging adjustment available (commented out in demo)")
}
