package brightness

import (
	"sync"
	"time"
)

type DeviceClass string

const (
	ClassBacklight DeviceClass = "backlight"
	ClassLED       DeviceClass = "leds"
	ClassDDC       DeviceClass = "ddc"
)

type Device struct {
	Class          DeviceClass `json:"class"`
	ID             string      `json:"id"`
	Name           string      `json:"name"`
	Current        int         `json:"current"`
	Max            int         `json:"max"`
	CurrentPercent int         `json:"currentPercent"`
	Backend        string      `json:"backend"`
}

type State struct {
	Devices []Device `json:"devices"`
}

type DeviceUpdate struct {
	Device Device `json:"device"`
}

type Request struct {
	ID     interface{}            `json:"id"`
	Method string                 `json:"method"`
	Params map[string]interface{} `json:"params"`
}

type Manager struct {
	logindBackend *LogindBackend
	sysfsBackend  *SysfsBackend
	ddcBackend    *DDCBackend

	logindReady bool
	sysfsReady  bool
	ddcReady    bool

	exponential bool

	stateMutex sync.RWMutex
	state      State

	subscribers       map[string]chan State
	updateSubscribers map[string]chan DeviceUpdate
	subMutex          sync.RWMutex

	broadcastMutex   sync.Mutex
	broadcastTimer   *time.Timer
	broadcastPending bool
	pendingDeviceID  string

	stopChan chan struct{}
}

type SysfsBackend struct {
	basePath string
	classes  []string

	deviceCache      map[string]*sysfsDevice
	deviceCacheMutex sync.RWMutex
}

type sysfsDevice struct {
	class         DeviceClass
	id            string
	name          string
	maxBrightness int
	minValue      int
}

type DDCBackend struct {
	devices      map[string]*ddcDevice
	devicesMutex sync.RWMutex

	scanMutex    sync.Mutex
	lastScan     time.Time
	scanInterval time.Duration

	debounceMutex   sync.Mutex
	debounceTimers  map[string]*time.Timer
	debouncePending map[string]ddcPendingSet
}

type ddcPendingSet struct {
	percent  int
	callback func()
}

type ddcDevice struct {
	bus            int
	addr           int
	id             string
	name           string
	max            int
	lastBrightness int
}

type ddcCapability struct {
	vcp     byte
	max     int
	current int
}

type SetBrightnessParams struct {
	Device      string  `json:"device"`
	Percent     int     `json:"percent"`
	Exponential bool    `json:"exponential,omitempty"`
	Exponent    float64 `json:"exponent,omitempty"`
}

func (m *Manager) Subscribe(id string) chan State {
	ch := make(chan State, 16)
	m.subMutex.Lock()
	m.subscribers[id] = ch
	m.subMutex.Unlock()
	return ch
}

func (m *Manager) Unsubscribe(id string) {
	m.subMutex.Lock()
	if ch, ok := m.subscribers[id]; ok {
		close(ch)
		delete(m.subscribers, id)
	}
	m.subMutex.Unlock()
}

func (m *Manager) SubscribeUpdates(id string) chan DeviceUpdate {
	ch := make(chan DeviceUpdate, 16)
	m.subMutex.Lock()
	m.updateSubscribers[id] = ch
	m.subMutex.Unlock()
	return ch
}

func (m *Manager) UnsubscribeUpdates(id string) {
	m.subMutex.Lock()
	if ch, ok := m.updateSubscribers[id]; ok {
		close(ch)
		delete(m.updateSubscribers, id)
	}
	m.subMutex.Unlock()
}

func (m *Manager) NotifySubscribers() {
	m.stateMutex.RLock()
	state := m.state
	m.stateMutex.RUnlock()

	m.subMutex.RLock()
	defer m.subMutex.RUnlock()

	for _, ch := range m.subscribers {
		select {
		case ch <- state:
		default:
		}
	}
}

func (m *Manager) GetState() State {
	m.stateMutex.RLock()
	defer m.stateMutex.RUnlock()
	return m.state
}

func (m *Manager) Close() {
	close(m.stopChan)

	m.subMutex.Lock()
	for _, ch := range m.subscribers {
		close(ch)
	}
	m.subscribers = make(map[string]chan State)
	for _, ch := range m.updateSubscribers {
		close(ch)
	}
	m.updateSubscribers = make(map[string]chan DeviceUpdate)
	m.subMutex.Unlock()

	if m.logindBackend != nil {
		m.logindBackend.Close()
	}

	if m.ddcBackend != nil {
		m.ddcBackend.Close()
	}
}
