/*
 * Copyright (c) 2017, 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.
 */

#include <string>
#include <ExternalObject.hpp>
#include <StorageObject.hpp>
#include <scs/StorageObjectImpl.hpp>
#include "iotcs_storage_object.h"
#include "advanced/iotcs_storage_object.h"

namespace iotdcl {
    StorageObjectImpl::StorageObjectImpl(const std::string &name, const std::string &contentType) {
        iotcs_create_storage_object_handle(name.c_str(), contentType.c_str(),
            &storage_object_handle);
        iotcs_storage_object_set_callback(storage_object_handle, StorageObjectImpl::onSyncCallback);
        this->name = new std::string(name);
        this->outPath = NULL;
        this->inPath = NULL;
        this->type = new std::string(iotcs_storage_object_get_type(storage_object_handle));
        this->encoding = NULL;
        this->uri = new std::string(iotcs_storage_object_get_uri(storage_object_handle));

        mapMutex.lock();
        globalSO[storage_object_handle] = this;
        mapMutex.unlock();
    }

    StorageObjectImpl::StorageObjectImpl(iotcs_storage_object_handle storage_object_handle) {
        this->storage_object_handle = storage_object_handle;
        iotcs_storage_object_set_callback(storage_object_handle, StorageObjectImpl::onSyncCallback);
        this->name = new std::string(iotcs_storage_object_get_name(storage_object_handle));
        this->outPath = NULL;
        this->inPath = NULL;
        if (iotcs_storage_object_get_output_path(storage_object_handle)) {
            this->outPath = new std::string(iotcs_storage_object_get_output_path(storage_object_handle));
        }
        if (iotcs_storage_object_get_input_path(storage_object_handle)) {
            this->inPath = new std::string(iotcs_storage_object_get_input_path(storage_object_handle));
        }
        this->type = new std::string(iotcs_storage_object_get_type(storage_object_handle));
        this->encoding = new std::string(iotcs_storage_object_get_encoding(storage_object_handle));
        this->uri = new std::string(iotcs_storage_object_get_uri(storage_object_handle));

        mapMutex.lock();
        globalSO[storage_object_handle] = this;
        mapMutex.unlock();
    }

    StorageObjectImpl::StorageObjectImpl(const std::string &uri) {
        iotcs_create_storage_object_by_uri(uri.c_str(), &storage_object_handle);
        iotcs_storage_object_set_callback(storage_object_handle, StorageObjectImpl::onSyncCallback);
        this->name = new std::string(iotcs_storage_object_get_name(storage_object_handle));
        this->outPath = NULL;
        this->inPath = NULL;
        this->type = new std::string(iotcs_storage_object_get_type(storage_object_handle));
        this->encoding = new std::string(iotcs_storage_object_get_encoding(storage_object_handle));
        this->uri = new std::string(iotcs_storage_object_get_uri(storage_object_handle));

        mapMutex.lock();
        globalSO[storage_object_handle] = this;
        mapMutex.unlock();
    }

    VirtualDevice* StorageObjectImpl::getVirtualDevice() const {
        return vd;
    }

    MUTEX_TYPE StorageObjectImpl::mapMutex;
    std::map<iotcs_storage_object_handle, StorageObjectImpl*> StorageObjectImpl::globalSO;
    std::map<iotcs_storage_object_handle, const SyncCallback*> StorageObjectImpl::globalOnSyncCallbacks;

    void StorageObjectImpl::onSyncCallback(iotcs_storage_object_sync_event *event) {
        mapMutex.lock();
        std::map<iotcs_storage_object_handle, StorageObjectImpl*>::const_iterator it = globalSO.find(event->source);
        std::map<iotcs_storage_object_handle, const SyncCallback*> ::const_iterator itCall = globalOnSyncCallbacks.find(event->source);
        if (it != globalSO.end() && itCall != globalOnSyncCallbacks.end()) {
            try {
                StorageObjectImpl * ptr = it->second;
                VirtualDevice *vd = NULL;
                if (ptr) {
                    vd = ptr->getVirtualDevice();
                }
                SyncEventImpl evt(ptr->getName(), vd, ptr);
                if (itCall->second) {
                    itCall->second->onSync(evt);
                }
            } catch (...) {
                // ignored
            }
        }
        mapMutex.unlock();
    }
};
