package token

import (
	"bytes"
	"net/url"
	"strings"
)

// ParseScope from a Scope.String()
func ParseScope(in string) Scope {
	if in == "" {
		return NewScope()
	}

	parts := strings.Split(in, " ")
	rawScopes := make([]string, 0, len(parts))

	for _, part := range parts {
		if partParsed, err := url.PathUnescape(part); err == nil {
			rawScopes = append(rawScopes, partParsed)
		} else {
			rawScopes = append(rawScopes, part)
		}
	}

	return NewScope(rawScopes...)
}

// NewScope returns a new token scope
func NewScope(values ...string) Scope {
	val := make(Scope)
	for _, v := range values {
		val.Add(v)
	}
	return val
}

// Scope represents a set of scopes
type Scope map[string]interface{}

// Add a value to this scope
func (s Scope) Add(value string) {
	s[value] = nil
}

// Contains another set of scopes
func (s Scope) Contains(other Scope) bool {
	for k := range other {
		if _, ok := s[k]; !ok {
			return false
		}
	}
	return true
}

// IsSuperSetOf return true if all the values are in this scope
func (s Scope) IsSuperSetOf(values []string) bool {
	for _, k := range values {
		if _, ok := s[k]; !ok {
			return false
		}
	}
	return true
}

// Extend the current scope with another scope
func (s Scope) Extend(other Scope) Scope {
	for v := range other {
		s.Add(v)
	}
	return s
}

// String returns the scope representation.
// See https://tools.ietf.org/html/rfc6749#section-3.3
func (s Scope) String() string {
	var out bytes.Buffer
	first := true
	for key := range s {
		if first {
			first = false
		} else {
			// bytes.Buffer.Write never errors, only panics
			out.WriteString(" ")
		}
		out.WriteString(url.PathEscape(key))
	}
	return out.String()
}
