#ifndef DEVICEMANAGER_H
#define DEVICEMANAGER_H

#include <vector>
#include <string>
#include <memory>
#include <opts.h>
#include <utility>
#include <iostream>
#include <functional>
#include <user/senseApiDesc.h>
#include <user/deviceAttachmentSpec.h>
#include <spinscale/asynchronousLoop.h>
#include <deviceManager/device.h>
#include <deviceManager/deviceRole.h>
#include <deviceManager/deviceReattacher.h>
#include <spinscale/callback.h>
#include <spinscale/qutex.h>

namespace smo {
namespace device {

class DeviceReattacher;

class DeviceManager
{
public:
    static DeviceManager& getInstance()
    {
        static DeviceManager instance;
        return instance;
    }

	void initialize(void)
	{};
	void finalize(void)
	{};

	void initializeDeviceReattacher();
	void finalizeDeviceReattacher();

    std::string readDapSpecFile(const std::string& filename);
    void collateAllDapSpecs(void);
    void parseAllDapSpecs(void);

    static const std::string stringifyDeviceSpecs(void);

	typedef std::function<void(
		bool success, std::shared_ptr<DeviceRole> deviceRole,
		std::shared_ptr<DeviceAttachmentSpec> deviceSpec)>
		newDeviceAttachmentSpecIndCbFn;
	typedef std::function<void(
		bool success, std::shared_ptr<DeviceAttachmentSpec> deviceSpec)>
		removeDeviceAttachmentSpecReqCbFn;

	void newDeviceAttachmentSpecInd(
		const DeviceAttachmentSpec &spec,
		sscl::Callback<newDeviceAttachmentSpecIndCbFn> callback);
	void removeDeviceAttachmentSpecReq(
		const DeviceAttachmentSpec &spec,
		sscl::Callback<removeDeviceAttachmentSpecReqCbFn> callback);

	// Device attachment/detachment methods moved from SenseApiManager
	typedef stim_buff::sal_mlo_attachDeviceReqCbFn attachStimBuffDeviceReqCbFn;
	typedef stim_buff::sal_mlo_detachDeviceReqCbFn detachStimBuffDeviceReqCbFn;

	void attachStimBuffDeviceReq(
        const std::shared_ptr<DeviceAttachmentSpec>& spec,
        sscl::Callback<attachStimBuffDeviceReqCbFn> cb);
    void detachStimBuffDeviceReq(
        const std::shared_ptr<DeviceAttachmentSpec>& spec,
        sscl::Callback<detachStimBuffDeviceReqCbFn> cb);

	typedef std::function<void(sscl::AsynchronousLoop &results)>
		attachAllUnattachedDevicesFromReqCbFn;
	typedef std::function<void(sscl::AsynchronousLoop &results)>
		detachAllAttachedDeviceRolesCbFn;

	void attachAllUnattachedDevicesFromReq(
		const std::shared_ptr<std::vector<DeviceAttachmentSpec>> &specs,
		sscl::Callback<attachAllUnattachedDevicesFromReqCbFn> cb);
	void attachAllUnattachedDevicesFromKnownListReq(
		sscl::Callback<attachAllUnattachedDevicesFromReqCbFn> cb);
	void attachAllUnattachedDevicesFromCmdlineReq(
		sscl::Callback<attachAllUnattachedDevicesFromReqCbFn> cb);
	void detachAllAttachedDeviceRoles(
		sscl::Callback<detachAllAttachedDeviceRolesCbFn> cb);

private:
    DeviceManager()
	: qutex("DeviceManager"), deviceReattacher(nullptr)
	{}
    ~DeviceManager();
    DeviceManager(const DeviceManager&) = delete;
    DeviceManager& operator=(const DeviceManager&) = delete;

public:
	sscl::Qutex qutex;
	std::string allDapSpecs;
	static std::vector<std::shared_ptr<DeviceAttachmentSpec>>
		deviceAttachmentSpecs;
	static std::vector<std::shared_ptr<Device>> devices;
	static std::vector<std::shared_ptr<DeviceRole>> attachedDeviceRoles;
	static std::vector<DeviceAttachmentSpec> commandLineDASpecs;

private:
	std::unique_ptr<DeviceReattacher> deviceReattacher;

	class NewDeviceAttachmentSpecInd;
	class RemoveDeviceAttachmentSpecReq;
	class AttachStimBuffDeviceReq;
	typedef AttachStimBuffDeviceReq DetachStimBuffDeviceReq;
	class AttachAllUnattachedDevicesFromReq;
	class AttachAllUnattachedDevicesFromKnownListReq;
	class DetachAllAttachedDeviceRoles;
};

} // namespace device
} // namespace smo

#endif // DEVICEMANAGER_H
