/*
 * MumbleDJ
 * By Matthieu Grieger
 * bot/skiptracker.go
 * Copyright (c) 2016 Matthieu Grieger (MIT License)
 */

package bot

import (
	"fmt"
	"sync"

	"github.com/layeh/gumble/gumble"
	"github.com/spf13/viper"
)

// SkipTracker keeps track of the list of users who have skipped the current
// track or playlist.
type SkipTracker struct {
	TrackSkips    []*gumble.User
	PlaylistSkips []*gumble.User
	trackMutex    sync.RWMutex
	playlistMutex sync.RWMutex
}

// NewSkipTracker returns an empty SkipTracker.
func NewSkipTracker() *SkipTracker {
	return &SkipTracker{
		TrackSkips:    make([]*gumble.User, 0),
		PlaylistSkips: make([]*gumble.User, 0),
	}
}

// AddTrackSkip adds a skip to the SkipTracker for the current track.
func (s *SkipTracker) AddTrackSkip(skipper *gumble.User) error {
	s.trackMutex.Lock()
	for _, user := range s.TrackSkips {
		if user.Name == skipper.Name {
			s.trackMutex.Unlock()
			return fmt.Errorf("%s has already voted to skip the track", skipper.Name)
		}
	}
	s.TrackSkips = append(s.TrackSkips, skipper)
	s.trackMutex.Unlock()
	s.evaluateTrackSkips()
	return nil
}

// AddPlaylistSkip adds a skip to the SkipTracker for the current playlist.
func (s *SkipTracker) AddPlaylistSkip(skipper *gumble.User) error {
	s.playlistMutex.Lock()
	for _, user := range s.PlaylistSkips {
		if user.Name == skipper.Name {
			s.playlistMutex.Unlock()
			return fmt.Errorf("%s has already voted to skip the playlist", skipper.Name)
		}
	}
	s.PlaylistSkips = append(s.PlaylistSkips, skipper)
	s.playlistMutex.Unlock()
	s.evaluatePlaylistSkips()
	return nil
}

// RemoveTrackSkip removes a skip from the SkipTracker for the current track.
func (s *SkipTracker) RemoveTrackSkip(skipper *gumble.User) error {
	s.trackMutex.Lock()
	for i, user := range s.TrackSkips {
		if user.Name == skipper.Name {
			s.TrackSkips = append(s.TrackSkips[:i], s.TrackSkips[i+1:]...)
			s.trackMutex.Unlock()
			return nil
		}
	}
	s.trackMutex.Unlock()
	return fmt.Errorf("%s did not previously vote to skip the track", skipper.Name)
}

// RemovePlaylistSkip removes a skip from the SkipTracker for the current playlist.
func (s *SkipTracker) RemovePlaylistSkip(skipper *gumble.User) error {
	s.playlistMutex.Lock()
	for i, user := range s.PlaylistSkips {
		if user.Name == skipper.Name {
			s.PlaylistSkips = append(s.PlaylistSkips[:i], s.PlaylistSkips[i+1:]...)
			s.playlistMutex.Unlock()
			return nil
		}
	}
	s.playlistMutex.Unlock()
	return fmt.Errorf("%s did not previously vote to skip the playlist", skipper.Name)
}

// NumTrackSkips returns the number of users who have skipped the current track.
func (s *SkipTracker) NumTrackSkips() int {
	s.trackMutex.RLock()
	length := len(s.TrackSkips)
	s.trackMutex.RUnlock()
	return length
}

// NumPlaylistSkips returns the number of users who have skipped the current playlist.
func (s *SkipTracker) NumPlaylistSkips() int {
	s.playlistMutex.RLock()
	length := len(s.PlaylistSkips)
	s.playlistMutex.RUnlock()
	return length
}

// ResetTrackSkips resets the skip slice for the current track.
func (s *SkipTracker) ResetTrackSkips() {
	s.trackMutex.Lock()
	s.TrackSkips = s.TrackSkips[:0]
	s.trackMutex.Unlock()
}

// ResetPlaylistSkips resets the skip slice for the current playlist.
func (s *SkipTracker) ResetPlaylistSkips() {
	s.playlistMutex.Lock()
	s.PlaylistSkips = s.PlaylistSkips[:0]
	s.playlistMutex.Unlock()
}

func (s *SkipTracker) evaluateTrackSkips() {
	s.trackMutex.RLock()
	skipRatio := viper.GetFloat64("queue.track_skip_ratio")
	DJ.Client.Do(func() {
		if float64(len(s.TrackSkips))/float64(len(DJ.Client.Self.Channel.Users)) >= skipRatio {
			// Stopping an audio stream triggers a skip.
			DJ.Queue.StopCurrent()
		}
	})
	s.trackMutex.RUnlock()
}

func (s *SkipTracker) evaluatePlaylistSkips() {
	s.playlistMutex.RLock()
	skipRatio := viper.GetFloat64("queue.playlist_skip_ratio")
	DJ.Client.Do(func() {
		if float64(len(s.PlaylistSkips))/float64(len(DJ.Client.Self.Channel.Users)) >= skipRatio {
			DJ.Queue.SkipPlaylist()
		}
	})
	s.playlistMutex.RUnlock()
}
