package devices

import (
	"sync"

	"code.secondbit.org/uuid.hg"
	"golang.org/x/net/context"
)

// Memstore is an in-memory implementation of Storer, and should
// only be used for testing or for temporary local servers.
type Memstore struct {
	devices map[string]Device
	lock    sync.RWMutex
}

// NewMemstore returns a Memstore that is ready to be used as a
// Storer implementation.
func NewMemstore() *Memstore {
	return &Memstore{
		devices: map[string]Device{},
	}
}

// GetDevices returns any Devices in the Memstore that match the
// passed IDs. If an ID cannot be matched to a Device in the
// Memstore, it is ignored. The result is a map, with the values
// being the Devices that could be found, and the keys being the
// result of the String() method for each Device's ID.
//
// An empty map is a possible response, if none of the IDs could
// be found.
func (m *Memstore) GetDevices(ids []uuid.ID, c context.Context) (map[string]Device, error) {
	m.lock.RLock()
	defer m.lock.RUnlock()

	results := map[string]Device{}

	for _, id := range ids {
		device, ok := m.devices[id.String()]
		if !ok {
			continue
		}
		results[id.String()] = device
	}
	return results, nil
}

// UpdateDevice applies the passed DeviceChange to the Device
// in the Memstore specified by the DeviceChange's DeviceID
// property. If no Device in the Memstore matches the DeviceChange's
// DeviceID property, then an ErrDeviceNotFound error will be
// returned.
func (m *Memstore) UpdateDevice(change DeviceChange, c context.Context) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	device, ok := m.devices[change.DeviceID.String()]
	if !ok {
		return ErrDeviceNotFound
	}

	device = ApplyChange(device, change)
	m.devices[change.DeviceID.String()] = device

	return nil
}

// DeleteDevices will remove any Devices from the Memstore that match
// the IDs passed in. If an ID can't be matched to a Device in the
// Memstore, then it is ignored.
func (m *Memstore) DeleteDevices(id []uuid.ID, c context.Context) error {
	return nil
}

// CreateDevices stores the passed devices in the Memstore as a single
// transaction. If a Device's ID already exists in the Memstore, an
// ErrDeviceAlreadyExists error with that Device's ID is returned, and
// none of the passed Devices are stored.
func (m *Memstore) CreateDevices(devices []Device, c context.Context) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	for _, device := range devices {
		if _, ok := m.devices[device.ID.String()]; ok {
			return ErrDeviceAlreadyExists(device.ID)
		}
	}

	for _, device := range devices {
		m.devices[device.ID.String()] = device
	}
	return nil
}

// ListDevicesByOwner returns all the Devices in the Memstore that have an
// Owner property that matches the passed ID. If no Devices have an Owner
// property matching the passed ID, an empty slice is returned.
//
// ListDevicesByOwner does not guarantee any sort order for the Devices.
func (m *Memstore) ListDevicesByOwner(user uuid.ID, c context.Context) ([]Device, error) {
	var devices []Device
	for _, device := range m.devices {
		if !device.Owner.Equal(user) {
			continue
		}
		devices = append(devices, device)
	}
	return devices, nil
}
