/*
 * 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 <AbstractVirtualDevice.hpp>
#include <NamedValue.hpp>

/* include iot cs device model APIs */
#include "iotcs_virtual_device.h"
#include <util/CPPUtils.hpp>
#include <scs/StorageObjectImpl.hpp>

#ifdef __MBED__
#include "mbed.h"
#endif

using namespace std;

namespace iotdcl {
    NamedValue::NamedValue(iotcs_named_value v) throw (std::invalid_argument) : name(v.name) {
        value = v;
        so = NULL;
        this->m_next = v.next != nullptr ? new NamedValue(*v.next) : nullptr;
        switch (v.typed_value.type) {
            case IOTCS_VALUE_TYPE_INT:
                type = Int;
                break;
            case IOTCS_VALUE_TYPE_NUMBER:
                type = Number;
                break;
            case IOTCS_VALUE_TYPE_BOOLEAN:
                type = Bool;
                break;
            case IOTCS_VALUE_TYPE_STRING:
                type = String;
                break;
            case IOTCS_VALUE_TYPE_DATE_TIME:
                type = DateTime;
                break;
            case IOTCS_VALUE_TYPE_URI:
                type = StorageObj;
                so = new StorageObjectImpl(value.typed_value.value.uri_object);
                break;
            case IOTCS_VALUE_TYPE_NONE:
                break;
            default:
                checkResult(IOTCS_RESULT_INVALID_ARGUMENT, "Unexpected type");
                break;
        }
    }

    NamedValue::NamedValue(const NamedValue& v) throw (std::invalid_argument) : NamedValue(v.value) {
    }

    NamedValue::~NamedValue() {
        if (so) {
            delete so;
            so = NULL;
        }
    }

    NamedValue* NamedValue::next() const {
        return m_next;
    }

    void NamedValue::checkType(Type t) const {
        if (t != type) {
            checkResult(IOTCS_RESULT_FAIL, "Wrong type requested");
        }
    }

    template<>
    int NamedValue::getValue() const {
        checkType(Int);
        return value.typed_value.value.int_value;
    }

    template<>
    float NamedValue::getValue() const {
        checkType(Number);
        return value.typed_value.value.number_value;
    }

    template<>
    bool NamedValue::getValue() const {
        checkType(Bool);
        return value.typed_value.value.bool_value;
    }

    template<>
    string NamedValue::getValue() const {
        checkType(String);
        return string(value.typed_value.value.string_value);
    }

    template<>
    time_t NamedValue::getValue() const {
        checkType(DateTime);
        return (time_t) value.typed_value.value.date_time_value;
    }

    template<>
    StorageObject& NamedValue::getValue() const {
        checkType(StorageObj);
        return *so;
    }

    const std::string& NamedValue::getName() const {
        return name;
    }
};
