package desktop

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"image"
	"image/jpeg"
	"image/png"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
	"unsafe"

	"github.com/kbinani/screenshot"
)

// ScreenCapture - Ekran yakalama servisi
type ScreenCapture struct {
	mu              sync.RWMutex
	isStreaming     int32   // atomic için int32
	streamQuality   int     // JPEG kalitesi (1-100)
	streamFPS       int     // Frame per second
	streamScale     float64 // Ölçeklendirme (0.1-1.0)
	lastFrame       []byte
	lastFrameTime   time.Time
	stopChan        chan struct{}
	cancelFunc      context.CancelFunc // Context cancel fonksiyonu
	frameChan       chan *ScreenFrame
	activeDisplays  []DisplayInfo
	selectedDisplay int
}

// ScreenFrame - Ekran frame'i
type ScreenFrame struct {
	Data      string    `json:"data"`      // Base64 encoded image
	Width     int       `json:"width"`     // Genişlik
	Height    int       `json:"height"`    // Yükseklik
	Timestamp int64     `json:"timestamp"` // Unix timestamp (ms)
	DisplayID int       `json:"displayId"` // Hangi ekran
	Format    string    `json:"format"`    // "jpeg" veya "png"
	Size      int       `json:"size"`      // Byte cinsinden boyut
	FrameNum  uint64    `json:"frameNum"`  // Frame numarası
}

// DisplayInfo - Ekran bilgisi
type DisplayInfo struct {
	ID       int    `json:"id"`
	Name     string `json:"name"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	X        int    `json:"x"`
	Y        int    `json:"y"`
	Primary  bool   `json:"primary"`
	ScaleFactor float64 `json:"scaleFactor"`
}

// ScreenCaptureConfig - Ekran yakalama ayarları
type ScreenCaptureConfig struct {
	Quality   int     `json:"quality"`   // 1-100
	FPS       int     `json:"fps"`       // 1-60
	Scale     float64 `json:"scale"`     // 0.1-1.0
	DisplayID int     `json:"displayId"` // -1 = tüm ekranlar
	Format    string  `json:"format"`    // "jpeg" veya "png"
	Region    *Region `json:"region"`    // Belirli bölge (opsiyonel)
}

// Region - Ekran bölgesi
type Region struct {
	X      int `json:"x"`
	Y      int `json:"y"`
	Width  int `json:"width"`
	Height int `json:"height"`
}

// NewScreenCapture - Yeni ekran yakalama servisi
func NewScreenCapture() *ScreenCapture {
	sc := &ScreenCapture{
		streamQuality:   70,
		streamFPS:       15,
		streamScale:     0.5,
		selectedDisplay: 0,
		frameChan:       make(chan *ScreenFrame, 10),
	}
	sc.refreshDisplays()
	return sc
}

// refreshDisplays - Ekran listesini güncelle
func (sc *ScreenCapture) refreshDisplays() {
	sc.mu.Lock()
	defer sc.mu.Unlock()

	n := screenshot.NumActiveDisplays()
	sc.activeDisplays = make([]DisplayInfo, n)

	for i := 0; i < n; i++ {
		bounds := screenshot.GetDisplayBounds(i)
		sc.activeDisplays[i] = DisplayInfo{
			ID:      i,
			Name:    "Display " + string(rune('1'+i)),
			Width:   bounds.Dx(),
			Height:  bounds.Dy(),
			X:       bounds.Min.X,
			Y:       bounds.Min.Y,
			Primary: i == 0,
			ScaleFactor: 1.0,
		}
	}
}

// GetDisplays - Aktif ekranları getir
func (sc *ScreenCapture) GetDisplays() []DisplayInfo {
	sc.mu.RLock()
	defer sc.mu.RUnlock()
	return sc.activeDisplays
}

// captureWithWindowsAPI - Windows API ile ekran yakala (RDP uyumlu, CAPTUREBLT flag)
func (sc *ScreenCapture) captureWithWindowsAPI(x, y, width, height int) (*image.RGBA, error) {
	if width <= 0 || height <= 0 {
		return nil, fmt.Errorf("geçersiz boyut: %dx%d", width, height)
	}

	// Desktop DC al
	hdcScreen, _, _ := procGetDC.Call(0)
	if hdcScreen == 0 {
		return nil, fmt.Errorf("GetDC başarısız: %v", syscall.GetLastError())
	}
	defer procReleaseDC.Call(0, hdcScreen)

	// Compatible DC oluştur
	hdcMem, _, _ := procCreateCompatibleDC.Call(hdcScreen)
	if hdcMem == 0 {
		return nil, fmt.Errorf("CreateCompatibleDC başarısız: %v", syscall.GetLastError())
	}
	defer procDeleteDC.Call(hdcMem)

	// Compatible bitmap oluştur
	hBitmap, _, _ := procCreateCompatibleBitmap.Call(hdcScreen, uintptr(width), uintptr(height))
	if hBitmap == 0 {
		return nil, fmt.Errorf("CreateCompatibleBitmap başarısız: %v", syscall.GetLastError())
	}
	defer procDeleteObject.Call(hBitmap)

	// Bitmap'i DC'ye seç
	procSelectObject.Call(hdcMem, hBitmap)

	// BitBlt ile kopyala - CAPTUREBLT flag RDP için kritik
	ret, _, _ := procBitBlt.Call(
		hdcMem, 0, 0, uintptr(width), uintptr(height),
		hdcScreen, uintptr(x), uintptr(y),
		SRCCOPY|CAPTUREBLT,
	)
	if ret == 0 {
		return nil, fmt.Errorf("BitBlt başarısız: %v", syscall.GetLastError())
	}

	// BITMAPINFO hazırla
	bi := BITMAPINFO{
		BmiHeader: BITMAPINFOHEADER{
			BiSize:        uint32(unsafe.Sizeof(BITMAPINFOHEADER{})),
			BiWidth:       int32(width),
			BiHeight:      -int32(height), // Top-down DIB
			BiPlanes:      1,
			BiBitCount:    32,
			BiCompression: BI_RGB,
		},
	}

	// Pixel buffer
	pixels := make([]byte, width*height*4)

	// GetDIBits ile pixel verisi al
	ret, _, _ = procGetDIBits.Call(
		hdcMem,
		hBitmap,
		0,
		uintptr(height),
		uintptr(unsafe.Pointer(&pixels[0])),
		uintptr(unsafe.Pointer(&bi)),
		DIB_RGB_COLORS,
	)
	if ret == 0 {
		return nil, fmt.Errorf("GetDIBits başarısız: %v", syscall.GetLastError())
	}

	// BGRA -> RGBA dönüşümü
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for i := 0; i < len(pixels); i += 4 {
		img.Pix[i] = pixels[i+2]   // R
		img.Pix[i+1] = pixels[i+1] // G
		img.Pix[i+2] = pixels[i]   // B
		img.Pix[i+3] = 255         // A
	}

	return img, nil
}

// captureWithDesktopWindow - GetWindowDC(GetDesktopWindow()) ile yakala (3. fallback)
func (sc *ScreenCapture) captureWithDesktopWindow(x, y, width, height int) (*image.RGBA, error) {
	if width <= 0 || height <= 0 {
		return nil, fmt.Errorf("geçersiz boyut: %dx%d", width, height)
	}

	// Desktop window handle al
	hwndDesktop, _, _ := procGetDesktopWindow.Call()
	if hwndDesktop == 0 {
		return nil, fmt.Errorf("GetDesktopWindow başarısız: %v", syscall.GetLastError())
	}

	// Desktop window DC al (GetDC(0) yerine)
	hdcScreen, _, _ := procGetWindowDC.Call(hwndDesktop)
	if hdcScreen == 0 {
		return nil, fmt.Errorf("GetWindowDC başarısız: %v", syscall.GetLastError())
	}
	defer procReleaseDC.Call(hwndDesktop, hdcScreen)

	// Compatible DC oluştur
	hdcMem, _, _ := procCreateCompatibleDC.Call(hdcScreen)
	if hdcMem == 0 {
		return nil, fmt.Errorf("CreateCompatibleDC başarısız: %v", syscall.GetLastError())
	}
	defer procDeleteDC.Call(hdcMem)

	// Compatible bitmap oluştur
	hBitmap, _, _ := procCreateCompatibleBitmap.Call(hdcScreen, uintptr(width), uintptr(height))
	if hBitmap == 0 {
		return nil, fmt.Errorf("CreateCompatibleBitmap başarısız: %v", syscall.GetLastError())
	}
	defer procDeleteObject.Call(hBitmap)

	// Bitmap'i DC'ye seç
	procSelectObject.Call(hdcMem, hBitmap)

	// BitBlt ile kopyala
	ret, _, _ := procBitBlt.Call(
		hdcMem, 0, 0, uintptr(width), uintptr(height),
		hdcScreen, uintptr(x), uintptr(y),
		SRCCOPY,
	)
	if ret == 0 {
		return nil, fmt.Errorf("BitBlt başarısız: %v", syscall.GetLastError())
	}

	// BITMAPINFO hazırla
	bi := BITMAPINFO{
		BmiHeader: BITMAPINFOHEADER{
			BiSize:        uint32(unsafe.Sizeof(BITMAPINFOHEADER{})),
			BiWidth:       int32(width),
			BiHeight:      -int32(height),
			BiPlanes:      1,
			BiBitCount:    32,
			BiCompression: BI_RGB,
		},
	}

	// Pixel buffer
	pixels := make([]byte, width*height*4)

	// GetDIBits ile pixel verisi al
	ret, _, _ = procGetDIBits.Call(
		hdcMem,
		hBitmap,
		0,
		uintptr(height),
		uintptr(unsafe.Pointer(&pixels[0])),
		uintptr(unsafe.Pointer(&bi)),
		DIB_RGB_COLORS,
	)
	if ret == 0 {
		return nil, fmt.Errorf("GetDIBits başarısız: %v", syscall.GetLastError())
	}

	// BGRA -> RGBA dönüşümü
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for i := 0; i < len(pixels); i += 4 {
		img.Pix[i] = pixels[i+2]
		img.Pix[i+1] = pixels[i+1]
		img.Pix[i+2] = pixels[i]
		img.Pix[i+3] = 255
	}

	return img, nil
}

// captureWithInputDesktop - Input Desktop'a bağlanarak yakala (Session 0 / service mode için)
func (sc *ScreenCapture) captureWithInputDesktop(x, y, width, height int) (*image.RGBA, error) {
	if width <= 0 || height <= 0 {
		return nil, fmt.Errorf("geçersiz boyut: %dx%d", width, height)
	}

	// Current thread ID al
	threadId, _, _ := procGetCurrentThreadId.Call()

	// Mevcut desktop'ı sakla
	oldDesktop, _, _ := procGetThreadDesktop.Call(threadId)

	// Input desktop'ı aç (aktif konsol session'ı)
	hDesktop, _, _ := procOpenInputDesktop.Call(
		0, // dwFlags
		0, // fInherit
		GENERIC_ALL,
	)
	if hDesktop == 0 {
		return nil, fmt.Errorf("OpenInputDesktop başarısız: %v", syscall.GetLastError())
	}
	defer procCloseDesktop.Call(hDesktop)

	// Thread'i input desktop'a bağla
	ret, _, _ := procSetThreadDesktop.Call(hDesktop)
	if ret == 0 {
		return nil, fmt.Errorf("SetThreadDesktop başarısız: %v", syscall.GetLastError())
	}
	// İşlem bitince eski desktop'a geri dön
	defer procSetThreadDesktop.Call(oldDesktop)

	// Şimdi normal capture yap
	hdcScreen, _, _ := procGetDC.Call(0)
	if hdcScreen == 0 {
		return nil, fmt.Errorf("GetDC başarısız: %v", syscall.GetLastError())
	}
	defer procReleaseDC.Call(0, hdcScreen)

	hdcMem, _, _ := procCreateCompatibleDC.Call(hdcScreen)
	if hdcMem == 0 {
		return nil, fmt.Errorf("CreateCompatibleDC başarısız: %v", syscall.GetLastError())
	}
	defer procDeleteDC.Call(hdcMem)

	hBitmap, _, _ := procCreateCompatibleBitmap.Call(hdcScreen, uintptr(width), uintptr(height))
	if hBitmap == 0 {
		return nil, fmt.Errorf("CreateCompatibleBitmap başarısız: %v", syscall.GetLastError())
	}
	defer procDeleteObject.Call(hBitmap)

	procSelectObject.Call(hdcMem, hBitmap)

	ret, _, _ = procBitBlt.Call(
		hdcMem, 0, 0, uintptr(width), uintptr(height),
		hdcScreen, uintptr(x), uintptr(y),
		SRCCOPY|CAPTUREBLT,
	)
	if ret == 0 {
		return nil, fmt.Errorf("BitBlt başarısız: %v", syscall.GetLastError())
	}

	bi := BITMAPINFO{
		BmiHeader: BITMAPINFOHEADER{
			BiSize:        uint32(unsafe.Sizeof(BITMAPINFOHEADER{})),
			BiWidth:       int32(width),
			BiHeight:      -int32(height),
			BiPlanes:      1,
			BiBitCount:    32,
			BiCompression: BI_RGB,
		},
	}

	pixels := make([]byte, width*height*4)

	ret, _, _ = procGetDIBits.Call(
		hdcMem,
		hBitmap,
		0,
		uintptr(height),
		uintptr(unsafe.Pointer(&pixels[0])),
		uintptr(unsafe.Pointer(&bi)),
		DIB_RGB_COLORS,
	)
	if ret == 0 {
		return nil, fmt.Errorf("GetDIBits başarısız: %v", syscall.GetLastError())
	}

	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for i := 0; i < len(pixels); i += 4 {
		img.Pix[i] = pixels[i+2]
		img.Pix[i+1] = pixels[i+1]
		img.Pix[i+2] = pixels[i]
		img.Pix[i+3] = 255
	}

	return img, nil
}

// CaptureScreen - Tek ekran görüntüsü yakala (4 kademeli fallback ile)
func (sc *ScreenCapture) CaptureScreen(config *ScreenCaptureConfig) (*ScreenFrame, error) {
	if config == nil {
		config = &ScreenCaptureConfig{
			Quality:   sc.streamQuality,
			Scale:     sc.streamScale,
			DisplayID: sc.selectedDisplay,
			Format:    "jpeg",
		}
	}

	displayID := config.DisplayID
	if displayID < 0 {
		displayID = 0
	}

	n := screenshot.NumActiveDisplays()
	if n == 0 {
		// Display bulunamadıysa varsayılan ekran boyutu kullan
		n = 1
	}
	if displayID >= n {
		displayID = 0
	}

	bounds := screenshot.GetDisplayBounds(displayID)

	// Bounds geçerli mi kontrol et
	if bounds.Dx() <= 0 || bounds.Dy() <= 0 {
		// GetSystemMetrics ile ekran boyutunu al
		w, _, _ := procGetSystemMetrics.Call(SM_CXSCREEN)
		h, _, _ := procGetSystemMetrics.Call(SM_CYSCREEN)
		if w > 0 && h > 0 {
			bounds = image.Rect(0, 0, int(w), int(h))
		} else {
			return nil, fmt.Errorf("ekran boyutu alınamadı: bounds=%v, SystemMetrics=%dx%d", bounds, w, h)
		}
	}

	// Bölge seçili ise bounds'u güncelle
	if config.Region != nil {
		bounds = image.Rect(
			config.Region.X,
			config.Region.Y,
			config.Region.X+config.Region.Width,
			config.Region.Y+config.Region.Height,
		)
	}

	var img *image.RGBA
	var err error
	var captureMethod string
	var lastErr error

	x, y, w, h := bounds.Min.X, bounds.Min.Y, bounds.Dx(), bounds.Dy()

	// Boyut kontrolü
	if w <= 0 || h <= 0 {
		return nil, fmt.Errorf("geçersiz ekran boyutu: %dx%d", w, h)
	}

	// 4 Kademeli Fallback Sistemi (Chrome Remote Desktop tarzı)

	// 1. Önce kbinani/screenshot dene (en hızlı, fiziksel makine için)
	img, err = screenshot.CaptureRect(bounds)
	if err == nil {
		captureMethod = "kbinani/screenshot"
	} else {
		lastErr = err
	}

	// 2. Windows API + CAPTUREBLT (RDP için)
	if err != nil {
		img, err = sc.captureWithWindowsAPI(x, y, w, h)
		if err == nil {
			captureMethod = "WindowsAPI+CAPTUREBLT"
		} else {
			lastErr = err
		}
	}

	// 3. GetWindowDC(GetDesktopWindow()) (alternatif RDP)
	if err != nil {
		img, err = sc.captureWithDesktopWindow(x, y, w, h)
		if err == nil {
			captureMethod = "GetWindowDC(DesktopWindow)"
		} else {
			lastErr = err
		}
	}

	// 4. Input Desktop switch (Session 0 / Service mode için)
	if err != nil {
		img, err = sc.captureWithInputDesktop(x, y, w, h)
		if err == nil {
			captureMethod = "InputDesktop"
		} else {
			lastErr = err
		}
	}

	// Tüm yöntemler başarısız
	if err != nil {
		return nil, fmt.Errorf("tüm capture yöntemleri başarısız: %v", lastErr)
	}

	_ = captureMethod // Kullanılmayan değişken uyarısını engelle

	// Ölçeklendirme
	scale := config.Scale
	if scale <= 0 || scale > 1 {
		scale = 1.0
	}

	var finalImg image.Image = img
	if scale < 1.0 {
		finalImg = scaleImageFast(img, scale)
	}

	// Encode
	var buf bytes.Buffer
	var format string

	if config.Format == "png" {
		format = "png"
		err = png.Encode(&buf, finalImg)
	} else {
		format = "jpeg"
		quality := config.Quality
		if quality <= 0 || quality > 100 {
			quality = 70
		}
		err = jpeg.Encode(&buf, finalImg, &jpeg.Options{Quality: quality})
	}

	if err != nil {
		return nil, fmt.Errorf("encode hatası: %v", err)
	}

	data := buf.Bytes()
	bounds2 := finalImg.Bounds()
	base64Data := base64.StdEncoding.EncodeToString(data)

	frame := &ScreenFrame{
		Data:      base64Data,
		Width:     bounds2.Dx(),
		Height:    bounds2.Dy(),
		Timestamp: time.Now().UnixMilli(),
		DisplayID: displayID,
		Format:    format,
		Size:      len(data),
	}

	return frame, nil
}

// scaleImageFast - Hızlı görüntü ölçeklendirme (yavaş bağlantılar için optimize)
func scaleImageFast(img *image.RGBA, scale float64) image.Image {
	bounds := img.Bounds()
	newWidth := int(float64(bounds.Dx()) * scale)
	newHeight := int(float64(bounds.Dy()) * scale)

	scaled := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
	srcStride := img.Stride
	dstStride := scaled.Stride

	// Nearest-neighbor with direct pixel access (faster)
	invScale := 1.0 / scale
	for y := 0; y < newHeight; y++ {
		srcY := int(float64(y) * invScale)
		if srcY >= bounds.Dy() {
			srcY = bounds.Dy() - 1
		}
		srcRowStart := srcY * srcStride
		dstRowStart := y * dstStride

		for x := 0; x < newWidth; x++ {
			srcX := int(float64(x) * invScale)
			if srcX >= bounds.Dx() {
				srcX = bounds.Dx() - 1
			}
			srcIdx := srcRowStart + srcX*4
			dstIdx := dstRowStart + x*4

			// Direct copy 4 bytes (RGBA)
			scaled.Pix[dstIdx] = img.Pix[srcIdx]
			scaled.Pix[dstIdx+1] = img.Pix[srcIdx+1]
			scaled.Pix[dstIdx+2] = img.Pix[srcIdx+2]
			scaled.Pix[dstIdx+3] = img.Pix[srcIdx+3]
		}
	}

	return scaled
}

// StartStreaming - Ekran streaming başlat
func (sc *ScreenCapture) StartStreaming(config *ScreenCaptureConfig, onFrame func(*ScreenFrame)) error {
	sc.mu.Lock()
	defer sc.mu.Unlock()

	// Zaten streaming aktifse önce durdur
	if atomic.LoadInt32(&sc.isStreaming) == 1 && sc.stopChan != nil {
		close(sc.stopChan)
		sc.stopChan = nil
		if sc.cancelFunc != nil {
			sc.cancelFunc()
			sc.cancelFunc = nil
		}
		// Goroutine'in kapanması için kısa bekle
		time.Sleep(100 * time.Millisecond)
	}

	atomic.StoreInt32(&sc.isStreaming, 1)
	sc.stopChan = make(chan struct{})

	if config != nil {
		if config.Quality > 0 && config.Quality <= 100 {
			sc.streamQuality = config.Quality
		}
		if config.FPS > 0 && config.FPS <= 60 {
			sc.streamFPS = config.FPS
		}
		if config.Scale > 0 && config.Scale <= 1 {
			sc.streamScale = config.Scale
		}
		if config.DisplayID >= 0 {
			sc.selectedDisplay = config.DisplayID
		}
	}

	// Context oluştur
	ctx, cancel := context.WithCancel(context.Background())
	sc.cancelFunc = cancel

	// stopChan'i kopyala (closure için)
	stopChan := sc.stopChan

	go sc.streamLoopWithChan(ctx, onFrame, stopChan)
	return nil
}

// StopStreaming - Ekran streaming durdur
func (sc *ScreenCapture) StopStreaming() {
	// Önce atomic flag'i sıfırla (goroutine hemen görsün)
	atomic.StoreInt32(&sc.isStreaming, 0)

	sc.mu.Lock()
	defer sc.mu.Unlock()

	// Context'i iptal et
	if sc.cancelFunc != nil {
		sc.cancelFunc()
		sc.cancelFunc = nil
	}

	// stopChan'i kapat ve nil yap (tekrar close edilmesini önle)
	if sc.stopChan != nil {
		close(sc.stopChan)
		sc.stopChan = nil
	}
}

// IsStreaming - Streaming aktif mi
func (sc *ScreenCapture) IsStreaming() bool {
	return atomic.LoadInt32(&sc.isStreaming) == 1
}

// streamLoopWithChan - Streaming döngüsü (belirli stopChan ile)
func (sc *ScreenCapture) streamLoopWithChan(ctx context.Context, onFrame func(*ScreenFrame), stopChan chan struct{}) {
	var frameNum uint64 = 0

	sc.mu.RLock()
	fps := sc.streamFPS
	sc.mu.RUnlock()

	interval := time.Second / time.Duration(fps)
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		// Üç yoldan biriyle çıkış kontrolü
		select {
		case <-ctx.Done():
			return
		case <-stopChan:
			return
		default:
			// Devam et
		}

		// Atomic flag kontrolü (en hızlı çıkış yolu)
		if atomic.LoadInt32(&sc.isStreaming) == 0 {
			return
		}

		select {
		case <-ctx.Done():
			return
		case <-stopChan:
			return
		case <-ticker.C:
			// Tekrar atomic kontrol
			if atomic.LoadInt32(&sc.isStreaming) == 0 {
				return
			}

			sc.mu.RLock()
			config := &ScreenCaptureConfig{
				Quality:   sc.streamQuality,
				Scale:     sc.streamScale,
				DisplayID: sc.selectedDisplay,
				Format:    "jpeg",
			}
			sc.mu.RUnlock()

			frame, err := sc.CaptureScreen(config)
			if err != nil {
				continue
			}

			frameNum++
			frame.FrameNum = frameNum

			if onFrame != nil {
				onFrame(frame)
			}
		}
	}
}

// UpdateConfig - Streaming ayarlarını güncelle
func (sc *ScreenCapture) UpdateConfig(config *ScreenCaptureConfig) {
	sc.mu.Lock()
	defer sc.mu.Unlock()

	if config.Quality > 0 && config.Quality <= 100 {
		sc.streamQuality = config.Quality
	}
	if config.FPS > 0 && config.FPS <= 60 {
		sc.streamFPS = config.FPS
	}
	if config.Scale > 0 && config.Scale <= 1 {
		sc.streamScale = config.Scale
	}
	if config.DisplayID >= 0 {
		sc.selectedDisplay = config.DisplayID
	}
}

// GetStreamStats - Streaming istatistikleri
func (sc *ScreenCapture) GetStreamStats() map[string]interface{} {
	sc.mu.RLock()
	defer sc.mu.RUnlock()

	return map[string]interface{}{
		"isStreaming":     sc.isStreaming,
		"quality":         sc.streamQuality,
		"fps":             sc.streamFPS,
		"scale":           sc.streamScale,
		"selectedDisplay": sc.selectedDisplay,
		"displayCount":    len(sc.activeDisplays),
	}
}
