/*
 * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL). See the LICENSE file in the root
 * directory for license terms. You may choose either license, or both.
 */

#pragma once

#include <AbstractVirtualDevice.hpp>
#include <VirtualDevice.hpp>
#include <Alert.hpp>
#include <Data.hpp>
#include <DeviceModel.hpp>
#include "iotcs.h"
#include "iotcs_virtual_device.h"
#include <device_model/DeviceModelImpl.hpp>
#include <messaging/AlertImpl.hpp>
#include <messaging/DataImpl.hpp>
#include <StorageObject.hpp>
#include <iostream>
#include <type_traits>
#include <mutex>
#include <vector>
#include <string>
#include <map>

#ifdef __MBED__
#include "mbed.h"
#include "rtos.h"
#define MUTEX_TYPE Mutex
#else
#define MUTEX_TYPE std::mutex
#endif

namespace iotdcl {
    class VirtualDeviceImpl : public VirtualDevice {
    public:
        VirtualDeviceImpl(const std::string &endpointId, DeviceModelImpl &dm);
        virtual ~VirtualDeviceImpl();
        virtual DeviceModel& getDeviceModel();
        virtual const std::string& getEndpointId() const;
        virtual Alert& createAlert(const std::string &format);
        virtual Data& createData(const std::string &format);
        virtual VirtualDevice& update(void);
        virtual void setOnChange(const ChangeCallback *callback);
        virtual void setOnError(const ErrorCallback *callback);
        virtual void setOnChange(const std::string &attributeName, const ChangeCallback *callback);
        virtual void setOnError(const std::string &attributeName, const ErrorCallback *callback);
        virtual void setCallable(const std::string &actionName, const Callable *callback);
        virtual void finish(void);
    private:
        DeviceModelImpl& deviceModel;
        std::string endpointId;
        static MUTEX_TYPE mapMutex;
        static std::map<iotcs_virtual_device_handle, std::pair<VirtualDeviceImpl*, const ChangeCallback*> > globalOnChangeCallbacks;
        static std::map<iotcs_virtual_device_handle, std::pair<VirtualDeviceImpl*, const ErrorCallback*> > globalOnErrorCallbacks;
        static std::map<iotcs_virtual_device_handle, std::pair<VirtualDeviceImpl*, std::map<std::string, const ChangeCallback*> > > attributeOnChangeCallbacks;
        static std::map<iotcs_virtual_device_handle, std::pair<VirtualDeviceImpl*, std::map<std::string, const ErrorCallback*> > > attributeOnErrorCallbacks;
        static std::map<iotcs_virtual_device_handle, std::pair<VirtualDeviceImpl*, std::map<std::string, const Callable*> > > onActionCallbacks;

        static void globalOnChangeCallback(iotcs_virtual_device_change_event *event);
        static void globalOnErrorCallback(iotcs_virtual_device_error_event *event);
        static void attributeOnChangeCallback(iotcs_virtual_device_change_event *event);
        static void attributeOnErrorCallback(iotcs_virtual_device_error_event *event);
        static void onActionCallback(iotcs_virtual_device_handle virtual_device_handle, char* action_name, iotcs_typed_value argument);
    private:
        std::vector<Alert*> ah_list;
        std::vector<Data*> dh_list;
        VirtualDeviceImpl(const VirtualDeviceImpl&); // no implementation
        VirtualDeviceImpl& operator=(const VirtualDeviceImpl&); // no implementation
    };
}
