package scopes

import (
	"sort"
	"sync"

	"code.secondbit.org/scopes.hg/types"

	"golang.org/x/net/context"
)

type Memstore struct {
	scopes map[string]scopeTypes.Scope
	lock   sync.RWMutex
}

func NewMemstore() *Memstore {
	return &Memstore{
		scopes: map[string]scopeTypes.Scope{},
	}
}

func (m *Memstore) CreateScopes(scopes []scopeTypes.Scope, ctx context.Context) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	for _, scope := range scopes {
		if _, ok := m.scopes[scope.ID]; ok {
			return ErrScopeAlreadyExists(scope.ID)
		}
	}
	for _, scope := range scopes {
		m.scopes[scope.ID] = scope
	}
	return nil
}

func (m *Memstore) GetScopes(ids []string, ctx context.Context) (map[string]scopeTypes.Scope, error) {
	m.lock.RLock()
	defer m.lock.RUnlock()

	scopes := map[string]scopeTypes.Scope{}
	for _, id := range ids {
		scope, ok := m.scopes[id]
		if !ok {
			continue
		}
		scopes[id] = scope
	}
	return scopes, nil
}

func (m *Memstore) UpdateScope(change scopeTypes.ScopeChange, ctx context.Context) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	scope, ok := m.scopes[change.ScopeID]
	if !ok {
		return ErrScopeNotFound
	}
	scope = scopeTypes.ApplyChange(change, scope)
	m.scopes[change.ScopeID] = scope
	return nil
}

func (m *Memstore) RemoveScopes(ids []string, ctx context.Context) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	for _, id := range ids {
		delete(m.scopes, id)
	}
	return nil
}

func (m *Memstore) ListScopes(ctx context.Context) ([]scopeTypes.Scope, error) {
	m.lock.RLock()
	defer m.lock.RUnlock()

	scopes := []scopeTypes.Scope{}
	for _, scope := range m.scopes {
		scopes = append(scopes, scope)
	}
	sorted := scopeTypes.Scopes(scopes)
	sort.Sort(sorted)
	scopes = sorted
	return scopes, nil
}
