func matchInArray(arr []string, value string) bool {
	for _, str := range arr {
		if str == value {
			return true
		}
	}
	return false
}
matchInArray is a generic matching function to check if a string is a list
of specific function
package xmpp
import (
	"context"
	"encoding/xml"
	"strings"
	"sync"
	"gosrc.io/xmpp/stanza"
)
type Router struct {
	routes []*Route
	IQResultRoutes    map[string]*IQResultRoute
	IQResultRouteLock sync.RWMutex
}
func NewRouter() *Router {
	return &Router{
		IQResultRoutes: make(map[string]*IQResultRoute),
	}
}
func (r *Router) route(s Sender, p stanza.Packet) {
	a, isA := p.(stanza.SMAnswer)
	if isA {
		switch tt := s.(type) {
		case *Client:
			lastAcked := a.H
			SendMissingStz(int(lastAcked), s, tt.Session.SMState.UnAckQueue)
		case *Component:
		default:
		}
	}
	iq, isIq := p.(*stanza.IQ)
	if isIq {
		r.IQResultRouteLock.RLock()
		route, ok := r.IQResultRoutes[iq.Id]
		r.IQResultRouteLock.RUnlock()
		if ok {
			r.IQResultRouteLock.Lock()
			delete(r.IQResultRoutes, iq.Id)
			r.IQResultRouteLock.Unlock()
			route.result <- *iq
			close(route.result)
			return
		}
	}
	var match RouteMatch
	if r.Match(p, &match) {
		match.Handler.HandlePacket(s, p)
		return
	}
	if isIq && (iq.Type == stanza.IQTypeGet || iq.Type == stanza.IQTypeSet) {
		iqNotImplemented(s, iq)
	}
}
func SendMissingStz(lastSent int, s Sender, uaq *stanza.UnAckQueue) error {
	uaq.RWMutex.Lock()
	if len(uaq.Uslice) <= 0 {
		uaq.RWMutex.Unlock()
		return nil
	}
	last := uaq.Uslice[len(uaq.Uslice)-1]
	if last.Id > lastSent {
		uaq.PopN(lastSent - last.Id)
		for _, elt := range uaq.PopN(len(uaq.Uslice)) {
			eltStz := elt.(*stanza.UnAckedStz)
			err := s.SendRaw(eltStz.Stz)
			if err != nil {
				return err
			}
		}
		s.Send(stanza.SMRequest{})
	}
	uaq.RWMutex.Unlock()
	return nil
}
func iqNotImplemented(s Sender, iq *stanza.IQ) {
	err := stanza.Err{
		XMLName: xml.Name{Local: "error"},
		Code:    501,
		Type:    "cancel",
		Reason:  "feature-not-implemented",
	}
	reply := iq.MakeError(err)
	_ = s.Send(reply)
}
func (r *Router) NewRoute() *Route {
	route := &Route{}
	r.routes = append(r.routes, route)
	return route
}
func (r *Router) NewIQResultRoute(ctx context.Context, id string) chan stanza.IQ {
	route := NewIQResultRoute(ctx)
	r.IQResultRouteLock.Lock()
	r.IQResultRoutes[id] = route
	r.IQResultRouteLock.Unlock()
	go func() {
		<-route.context.Done()
		r.IQResultRouteLock.Lock()
		delete(r.IQResultRoutes, id)
		r.IQResultRouteLock.Unlock()
	}()
	return route.result
}
func (r *Router) Match(p stanza.Packet, match *RouteMatch) bool {
	for _, route := range r.routes {
		if route.Match(p, match) {
			return true
		}
	}
	return false
}
func (r *Router) Handle(name string, handler Handler) *Route {
	return r.NewRoute().Packet(name).Handler(handler)
}
func (r *Router) HandleFunc(name string, f func(s Sender, p stanza.Packet)) *Route {
	return r.NewRoute().Packet(name).HandlerFunc(f)
}
type TimeoutHandlerFunc func(err error)
type IQResultRoute struct {
	context context.Context
	result  chan stanza.IQ
}
func NewIQResultRoute(ctx context.Context) *IQResultRoute {
	return &IQResultRoute{
		context: ctx,
		result:  make(chan stanza.IQ),
	}
}
type IQResultHandler interface {
	HandleIQ(ctx context.Context, s Sender, iq stanza.IQ)
}
type IQResultHandlerFunc func(ctx context.Context, s Sender, iq stanza.IQ)
func (f IQResultHandlerFunc) HandleIQ(ctx context.Context, s Sender, iq stanza.IQ) {
	f(ctx, s, iq)
}
type Handler interface {
	HandlePacket(s Sender, p stanza.Packet)
}
type Route struct {
	handler Handler
	matchers []Matcher
}
func (r *Route) Handler(handler Handler) *Route {
	r.handler = handler
	return r
}
type HandlerFunc func(s Sender, p stanza.Packet)
func (f HandlerFunc) HandlePacket(s Sender, p stanza.Packet) {
	f(s, p)
}
func (r *Route) HandlerFunc(f HandlerFunc) *Route {
	return r.Handler(f)
}
func (r *Route) AddMatcher(m Matcher) *Route {
	r.matchers = append(r.matchers, m)
	return r
}
func (r *Route) Match(p stanza.Packet, match *RouteMatch) bool {
	for _, m := range r.matchers {
		if matched := m.Match(p, match); !matched {
			return false
		}
	}
	match.Route = r
	match.Handler = r.handler
	return true
}
type nameMatcher string
func (n nameMatcher) Match(p stanza.Packet, match *RouteMatch) bool {
	var name string
	switch p.(type) {
	case stanza.Message:
		name = "message"
	case *stanza.IQ:
		name = "iq"
	case stanza.Presence:
		name = "presence"
	}
	if name == string(n) {
		return true
	}
	return false
}
func (r *Route) Packet(name string) *Route {
	name = strings.ToLower(name)
	return r.AddMatcher(nameMatcher(name))
}
type nsTypeMatcher []string
func (m nsTypeMatcher) Match(p stanza.Packet, match *RouteMatch) bool {
	var stanzaType stanza.StanzaType
	switch packet := p.(type) {
	case *stanza.IQ:
		stanzaType = packet.Type
	case stanza.Presence:
		stanzaType = packet.Type
	case stanza.Message:
		if packet.Type == "" {
			stanzaType = "normal"
		} else {
			stanzaType = packet.Type
		}
	default:
		return false
	}
	return matchInArray(m, string(stanzaType))
}
func (r *Route) StanzaType(types ...string) *Route {
	for k, v := range types {
		types[k] = strings.ToLower(v)
	}
	return r.AddMatcher(nsTypeMatcher(types))
}
type nsIQMatcher []string
func (m nsIQMatcher) Match(p stanza.Packet, match *RouteMatch) bool {
	iq, ok := p.(*stanza.IQ)
	if !ok {
		return false
	}
	if iq.Payload == nil {
		return false
	}
	return matchInArray(m, iq.Payload.Namespace())
}
func (r *Route) IQNamespaces(namespaces ...string) *Route {
	for k, v := range namespaces {
		namespaces[k] = strings.ToLower(v)
	}
	return r.AddMatcher(nsIQMatcher(namespaces))
}
type Matcher interface {
	Match(stanza.Packet, *RouteMatch) bool
}
type RouteMatch struct {
	Route   *Route
	Handler Handler
}