func (s *State) quit(ev *irc.Event) string {
	if ev.Sender != string(s.selfUser.Host) {
		s.removeUser(ev.Sender)
		return ev.Sender
	}
	return ""
}
quit alters the state of the database when a QUIT message is received.
package data
import (
	"errors"
	"strings"
	"sync"
	"github.com/aarondl/ultimateq/irc"
)
var (
	errNetInfoMissing = errors.New("data: NetworkInfo missing")
)
type Self struct {
	User
	ChannelModes
}
type State struct {
	selfUser  *User
	selfModes ChannelModes
	channels map[string]*Channel
	users    map[string]*User
	channelUsers map[string]map[string]channelUser
	userChannels map[string]map[string]userChannel
	kinds *modeKinds
	protect sync.RWMutex
}
func NewState(netInfo *irc.NetworkInfo) (*State, error) {
	state := &State{}
	if err := state.SetNetworkInfo(netInfo); err != nil {
		return nil, err
	}
	state.selfModes = NewChannelModes(&modeKinds{})
	state.channels = make(map[string]*Channel)
	state.users = make(map[string]*User)
	state.channelUsers = make(map[string]map[string]channelUser)
	state.userChannels = make(map[string]map[string]userChannel)
	return state, nil
}
func (s *State) SetNetworkInfo(ni *irc.NetworkInfo) error {
	s.protect.Lock()
	defer s.protect.Unlock()
	if ni == nil {
		return errNetInfoMissing
	}
	if s.kinds != nil {
		return s.kinds.update(ni.Prefix(), ni.Chanmodes())
	}
	kinds, err := newModeKinds(ni.Prefix(), ni.Chanmodes())
	if err != nil {
		return err
	}
	s.kinds = kinds
	return nil
}
func (s *State) Self() Self {
	return Self{*s.selfUser, s.selfModes.Clone()}
}
func (s *State) User(nickorhost string) (User, bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	nick := strings.ToLower(irc.Nick(nickorhost))
	var user User
	u, ok := s.users[nick]
	if ok {
		user = *u
	}
	return user, ok
}
func (s *State) Channel(channel string) (Channel, bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	var ch Channel
	c, ok := s.channels[strings.ToLower(channel)]
	if ok {
		ch = *c.Clone()
	}
	return ch, ok
}
func (s *State) UserModes(nickorhost, channel string) (UserModes, bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	var modes UserModes
	if channelModes := s.userModes(nickorhost, channel); channelModes != nil {
		modes = *channelModes
		return modes, true
	}
	return modes, false
}
func (s *State) NUsers() int {
	s.protect.RLock()
	defer s.protect.RUnlock()
	return len(s.users)
}
func (s *State) NChannels() int {
	s.protect.RLock()
	defer s.protect.RUnlock()
	return len(s.channels)
}
func (s *State) NChannelsByUser(nickorhost string) (n int, ok bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	var ucs map[string]userChannel
	nick := strings.ToLower(irc.Nick(nickorhost))
	if ucs, ok = s.userChannels[nick]; ok {
		n = len(ucs)
	}
	return n, ok
}
func (s *State) NUsersByChannel(channel string) (n int, ok bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	var cus map[string]channelUser
	channel = strings.ToLower(channel)
	if cus, ok = s.channelUsers[channel]; ok {
		n = len(cus)
	}
	return n, ok
}
func (s *State) EachUser(fn func(User) bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	for _, u := range s.users {
		if fn(*u) {
			break
		}
	}
}
func (s *State) EachChannel(fn func(Channel) bool) {
	s.protect.RLock()
	defer s.protect.RUnlock()
	for _, c := range s.channels {
		if fn(*c.Clone()) {
			break
		}
	}
}
func (s *State) Users() []string {
	s.protect.RLock()
	defer s.protect.RUnlock()
	ret := make([]string, 0, len(s.users))
	for _, u := range s.users {
		ret = append(ret, u.Host.String())
	}
	return ret
}
func (s *State) Channels() []string {
	s.protect.RLock()
	defer s.protect.RUnlock()
	ret := make([]string, 0, len(s.channels))
	for _, c := range s.channels {
		ret = append(ret, c.Name)
	}
	return ret
}
func (s *State) ChannelsByUser(nickorhost string) []string {
	s.protect.RLock()
	defer s.protect.RUnlock()
	nick := strings.ToLower(irc.Nick(nickorhost))
	if ucs, ok := s.userChannels[nick]; ok {
		ret := make([]string, 0, len(ucs))
		for _, uc := range ucs {
			ret = append(ret, uc.Channel.Name)
		}
		return ret
	}
	return nil
}
func (s *State) UsersByChannel(channel string) []string {
	s.protect.RLock()
	defer s.protect.RUnlock()
	channel = strings.ToLower(channel)
	if cus, ok := s.channelUsers[channel]; ok {
		ret := make([]string, 0, len(cus))
		for _, cu := range cus {
			ret = append(ret, cu.User.Host.String())
		}
		return ret
	}
	return nil
}
func (s *State) IsOn(nickorhost, channel string) bool {
	s.protect.RLock()
	defer s.protect.RUnlock()
	nick := strings.ToLower(irc.Nick(nickorhost))
	channel = strings.ToLower(channel)
	if chans, ok := s.userChannels[nick]; ok {
		_, ok = chans[channel]
		return ok
	}
	return false
}
func (s *State) user(nickorhost string) *User {
	return s.users[strings.ToLower(irc.Nick(nickorhost))]
}
func (s *State) channel(name string) *Channel {
	return s.channels[strings.ToLower(irc.Nick(name))]
}
func (s *State) userModes(nickorhost, channel string) *UserModes {
	nick := strings.ToLower(irc.Nick(nickorhost))
	channel = strings.ToLower(channel)
	if nicks, ok := s.channelUsers[channel]; ok {
		if cu, ok := nicks[nick]; ok {
			return cu.UserModes
		}
	}
	return nil
}
func (s *State) addUser(nickorhost string) bool {
	excl, at, per := false, false, false
	for i := 0; i < len(nickorhost); i++ {
		switch nickorhost[i] {
		case '!':
			excl = true
		case '@':
			at = true
		case '.':
			per = true
		}
	}
	if per && !(excl && at) {
		return false
	}
	nick := strings.ToLower(irc.Nick(nickorhost))
	var user *User
	var ok bool
	if user, ok = s.users[nick]; ok {
		if excl && at && user.Host.String() != nickorhost {
			user.Host = irc.Host(nickorhost)
		}
	} else {
		user = NewUser(nickorhost)
		s.users[nick] = user
		return true
	}
	return false
}
func (s *State) removeUser(nickorhost string) {
	nick := strings.ToLower(irc.Nick(nickorhost))
	for _, cus := range s.channelUsers {
		delete(cus, nick)
	}
	delete(s.userChannels, nick)
	delete(s.users, nick)
}
func (s *State) addChannel(channel string) *Channel {
	chankey := strings.ToLower(channel)
	var ch *Channel
	var ok bool
	if ch, ok = s.channels[chankey]; !ok {
		ch = NewChannel(channel, s.kinds)
		s.channels[chankey] = ch
	}
	return ch
}
func (s *State) removeChannel(channel string) (unseen []string) {
	channel = strings.ToLower(channel)
	for _, cus := range s.userChannels {
		delete(cus, channel)
	}
	for _, cu := range s.channelUsers[channel] {
		nick := strings.ToLower(cu.User.Nick())
		if nick == s.selfUser.Nick() {
			continue
		}
		if ucs, ok := s.userChannels[nick]; ok {
			if len(ucs) == 0 {
				unseen = append(unseen, string(cu.User.Host))
				delete(s.users, strings.ToLower(nick))
			}
		}
	}
	delete(s.channelUsers, channel)
	delete(s.channels, channel)
	return unseen
}
func (s *State) addToChannel(nickorhost, channel string) {
	var user *User
	var ch *Channel
	var cu map[string]channelUser
	var uc map[string]userChannel
	var ok, cuhas, uchas bool
	nick := strings.ToLower(irc.Nick(nickorhost))
	channel = strings.ToLower(channel)
	if user, ok = s.users[nick]; !ok {
		return
	}
	if ch, ok = s.channels[channel]; !ok {
		return
	}
	if cu, ok = s.channelUsers[channel]; !ok {
		cu = make(map[string]channelUser, 1)
	} else {
		_, cuhas = s.channelUsers[channel][nick]
	}
	if uc, ok = s.userChannels[nick]; !ok {
		uc = make(map[string]userChannel, 1)
	} else {
		_, uchas = s.userChannels[nick][channel]
	}
	if cuhas || uchas {
		return
	}
	modes := NewUserModes(s.kinds)
	cu[nick] = newChannelUser(user, &modes)
	uc[channel] = newUserChannel(ch, &modes)
	s.channelUsers[channel] = cu
	s.userChannels[nick] = uc
}
func (s *State) removeFromChannel(nickorhost, channel string) {
	var cu map[string]channelUser
	var uc map[string]userChannel
	var ok bool
	nick := strings.ToLower(irc.Nick(nickorhost))
	channel = strings.ToLower(channel)
	if cu, ok = s.channelUsers[channel]; ok {
		delete(cu, nick)
	}
	shouldRemove := false
	if uc, ok = s.userChannels[nick]; ok {
		delete(uc, channel)
		shouldRemove = len(uc) == 0
	}
	if shouldRemove {
		s.removeUser(nick)
	}
}
type StateUpdate struct {
	Nick   []string
	Unseen []string
	Seen   []string
	Quit   string
}
func (s *State) Update(ev *irc.Event) (update StateUpdate) {
	s.protect.Lock()
	defer s.protect.Unlock()
	switch ev.Name {
	case irc.NICK:
		update.Nick = s.nick(ev)
	case irc.JOIN:
		update.Seen = s.join(ev)
	case irc.PART:
		update.Unseen = s.part(ev)
	case irc.QUIT:
		update.Quit = s.quit(ev)
	case irc.KICK:
		update.Seen, update.Unseen = s.kick(ev)
	case irc.MODE:
		update.Seen = s.mode(ev)
	case irc.TOPIC:
		update.Seen = s.topic(ev)
	case irc.RPL_TOPIC:
		s.rplTopic(ev)
	case irc.PRIVMSG, irc.NOTICE:
		update.Seen = s.msg(ev)
	case irc.RPL_WELCOME:
		s.rplWelcome(ev)
	case irc.RPL_NAMREPLY:
		s.rplNameReply(ev)
	case irc.RPL_WHOREPLY:
		s.rplWhoReply(ev)
	case irc.RPL_CHANNELMODEIS:
		s.rplChannelModeIs(ev)
	case irc.RPL_BANLIST:
		s.rplBanList(ev)
	}
	return update
}
func (s *State) nick(ev *irc.Event) []string {
	s.addUser(ev.Sender)
	nick, username, host := ev.SplitHost()
	newnick := ev.Args[0]
	newuser := irc.Host(newnick + "!" + username + "@" + host)
	nick = strings.ToLower(nick)
	newnick = strings.ToLower(newnick)
	if user, ok := s.users[nick]; ok {
		user.Host = newuser
		for _, cus := range s.channelUsers {
			if _, ok := cus[nick]; ok {
				cus[newnick] = cus[nick]
				delete(cus, nick)
			}
		}
		if _, ok := s.userChannels[nick]; ok {
			s.userChannels[newnick] = s.userChannels[nick]
			delete(s.userChannels, nick)
		}
		s.users[newnick] = s.users[nick]
		delete(s.users, nick)
	}
	return []string{ev.Sender, string(newuser)}
}
func (s *State) join(ev *irc.Event) []string {
	var seen []string
	if ev.Sender == string(s.selfUser.Host) {
		s.addChannel(ev.Args[0])
	} else {
		seen = []string{ev.Sender}
	}
	s.addUser(ev.Sender)
	s.addToChannel(ev.Sender, ev.Args[0])
	return seen
}
func (s *State) part(ev *irc.Event) []string {
	if ev.Sender == string(s.selfUser.Host) {
		return s.removeChannel(ev.Args[0])
	} else {
		s.removeFromChannel(ev.Sender, ev.Args[0])
		if s.user(ev.Sender) == nil {
			return []string{ev.Sender}
		}
	}
	return nil
}