#include <iostream>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <sstream>
#include <memory>
#include <opts.h>
#include <deviceManager/deviceManager.h>
#include <senseApis/senseApiManager.h>

namespace smo {
namespace device {

std::vector<std::shared_ptr<InteroceptorDevAttachmentSpec>>
    DeviceManager::interoceptorDeviceSpecs;
std::vector<std::shared_ptr<ExtrospectorDevAttachmentSpec>>
    DeviceManager::extrospectorDeviceSpecs;
std::vector<std::shared_ptr<DeviceAttachmentSpec>>
    DeviceManager::deviceAttachmentSpecs;
std::vector<std::shared_ptr<Device>>
    DeviceManager::devices;

// Async continuation structure
struct DeviceAttachmentContinuation {
	std::shared_ptr<DeviceAttachmentSpec> spec;
	std::function<void(
		bool success, std::shared_ptr<Device> device,
		std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
		> callback;

	DeviceAttachmentContinuation(
		std::shared_ptr<DeviceAttachmentSpec> s,
		std::function<void(
			bool success, std::shared_ptr<Device> device,
			std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
			> cb)
	: spec(s), callback(cb)
	{}

	void callOriginalCallback(
		bool success,
		std::shared_ptr<Device> device,
		std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
	{
		callback(success, device, deviceSpec);
	}
};

const std::string DeviceManager::stringifyDeviceSpecs(void)
{
    std::ostringstream oss;

    for (const auto& spec : DeviceManager::interoceptorDeviceSpecs) {
        oss << "Interoceptor " << spec->stringify();
    }

    for (const auto& spec : DeviceManager::extrospectorDeviceSpecs) {
        oss << "Extrospector " << spec->stringify();
    }

    return oss.str();
}

#if 0
void DeviceManager::newDeviceAttachmentSpecInd(
	std::shared_ptr<DeviceAttachmentSpec> spec,
	std::function<void(
		bool success, std::shared_ptr<Device> device,
		std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
		> callback)
{
	// Create async continuation
	auto continuation = std::make_shared<DeviceAttachmentContinuation>(
		spec, callback);

	// Check if a DeviceAttachmentSpec already matches
	for (const auto& existingSpec : deviceAttachmentSpecs)
	{
		if (!(*existingSpec == *spec)) { continue; }
		// Already exists, callback with error
		continuation->callOriginalCallback(false, nullptr, nullptr);
		return;
	}

	// Try to attach the sense device
	try {
		sense_api::SenseApiManager::getInstance().attachSenseDevice(spec);

		// Look for existing Device with same identifier
		std::shared_ptr<Device> device = nullptr;
		for (const auto& existingDevice : devices)
		{
			if (existingDevice->deviceIdentifier != spec->deviceIdentifier)
				{ continue; }

			device = existingDevice;
			break;
		}

		// If device doesn't exist, create a new one and add it
		if (!device)
		{
			device = std::make_shared<Device>(spec->deviceIdentifier);
			devices.push_back(device);
		}

		// Add DeviceAttachmentSpec to device's list
		device->deviceAttachmentSpecs.push_back(spec);

		// Add DeviceAttachmentSpec to DeviceManager's list
		deviceAttachmentSpecs.push_back(spec);

		// Callback with success
		continuation->callOriginalCallback(true, device, spec);
	} catch (const std::exception& e) {
		// Attach failed, callback with error
		continuation->callOriginalCallback(false, nullptr, nullptr);
	}
}
#endif

} // namespace device
} // namespace smo
