package localserver

import (
	"encoding/json"
	"fmt"
	"sync/atomic"

	"github.com/pion/webrtc/v3"
)

const (
	VIDEO      = "video"
	STOP_VIDEO = "stop_video"
)

type WebrtcCallVideoManager struct{}

func NewWebrtcCallVideoManager() *WebrtcCallVideoManager {
	return new(WebrtcCallVideoManager)
}

func (w *WebrtcCallVideoManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) {
	done, errCh := make(chan struct{}), make(chan error)
	go func() {
		logger.Println("got an event in call video manager", from, eventId, payload)
		switch eventId {
		case VIDEO:
			if e := w.video(from, squadId, manager); e != nil {
				errCh <- e
				return
			}
		case STOP_VIDEO:
			if e := w.stopVideo(from, squadId, manager); e != nil {
				errCh <- e
				return
			}
		}
		done <- struct{}{}
	}()
	select {
	case <-done:
		return nil
	case err = <-errCh:
		return
	}
}

func (w *WebrtcCallVideoManager) video(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
	if _, ok := manager.Squads[squadId]; !ok {
		err = fmt.Errorf("no correponding squad found")
		return
	}
	logger.Println("sending video event", peerId)
	manager.RemoteTracksMux.RLock()
	for _, v := range manager.RemoteTracks[peerId] {
		if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
			atomic.SwapInt32(v.rdv, 0)
		}
	}
	manager.RemoteTracksMux.RUnlock()
	manager.SquadMapMux.Lock()
	defer manager.SquadMapMux.Unlock()
	for _, member := range manager.Squads[squadId].Members {
		manager.DataChannelMapMux.Lock()
		if _, ok := manager.DataChannels[member]; ok && member != peerId {
			bs, marshalErr := json.Marshal(map[string]interface{}{
				"type":    VIDEO,
				"from":    peerId,
				"payload": map[string]interface{}{},
			})
			if marshalErr != nil {
				logger.Println(err)
				manager.DataChannelMapMux.Unlock()
				continue
			}
			if sendErr := atomicallyExecute(manager.DataChannels[member].l, func() (err error) {
				err = manager.DataChannels[member].DataChannel.SendText(string(bs))
				return
			}); sendErr != nil {
				logger.Println(sendErr)
			}
		}
		manager.DataChannelMapMux.Unlock()
	}
	return
}

func (w *WebrtcCallVideoManager) stopVideo(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
	if _, ok := manager.Squads[squadId]; !ok {
		err = fmt.Errorf("no correponding squad found")
		return
	}
	logger.Println("sending stop video event", peerId)
	manager.RemoteTracksMux.RLock()
	for _, v := range manager.RemoteTracks[peerId] {
		if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
			atomic.SwapInt32(v.rdv, 1)
		}
	}
	manager.RemoteTracksMux.RUnlock()
	manager.SquadMapMux.Lock()
	defer manager.SquadMapMux.Unlock()
	for _, member := range manager.Squads[squadId].Members {
		manager.DataChannelMapMux.Lock()
		if _, ok := manager.DataChannels[member]; ok && member != peerId {
			bs, marshalErr := json.Marshal(map[string]interface{}{
				"type":    STOP_VIDEO,
				"from":    peerId,
				"payload": map[string]interface{}{},
			})
			if marshalErr != nil {
				logger.Println(err)
				manager.DataChannelMapMux.Unlock()
				continue
			}
			for {
				if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
					defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
					if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
						logger.Println(sendErr)
					}
					break
				} else {
					continue
				}
			}
		}
		manager.DataChannelMapMux.Unlock()
	}
	return
}
