/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "host.hpp"

#include "failsafeloggers/failsafe_logger_utility.hpp"

#include <cmath>
#include <iostream>
#include <memory>
#include <mutex>

namespace pid_control
{

template <typename T>
void scaleHelper(T& ptr, int64_t value)
{
    if constexpr (std::is_same_v<ValueType, int64_t>)
    {
        ptr->scale(value);
    }
}

std::unique_ptr<Sensor> HostSensor::createTemp(
    const std::string& name, int64_t timeout, sdbusplus::bus_t& bus,
    const char* objPath, bool defer)
{
    auto sensor =
        std::make_unique<HostSensor>(name, timeout, bus, objPath, defer);
    sensor->value(0);

    // DegreesC and value of 0 are the defaults at present, therefore testing
    // this code only sees scale get updated as a property.

    // TODO(venture): Need to not hard-code that this is DegreesC and scale
    // 10x-3 unless it is! :D
    sensor->unit(ValueInterface::Unit::DegreesC);
    scaleHelper(sensor, -3);
    sensor->emit_object_added();
    // emit_object_added() can be called twice, harmlessly, the second time it
    // doesn't actually happen, but we don't want to call it before we set up
    // the initial values, so we should not let someone call this with
    // defer=false.

    /* TODO(venture): Need to set that _updated is set to epoch or something
     * else.  what is the default value?
     */
    return sensor;
}

template <typename T>
int64_t getScale(T* sensor)
{
    if constexpr (std::is_same_v<ValueType, int64_t>)
    {
        return sensor->scale();
    }
    return 0;
}

ValueType HostSensor::value(ValueType value)
{
    std::lock_guard<std::mutex> guard(_lock);

    _updated = std::chrono::high_resolution_clock::now();
    _value = value * pow(10, getScale(this)); /* scale value */

    return ValueObject::value(value);
}

ReadReturn HostSensor::read(void)
{
    std::lock_guard<std::mutex> guard(_lock);

    /* This doesn't sanity check anything, that's the caller's job. */
    ReadReturn r = {_value, _updated};

    return r;
}

void HostSensor::write([[maybe_unused]] double value)
{
    throw std::runtime_error("Not Implemented.");
}

bool HostSensor::getFailed(void)
{
    if (std::isfinite(_value))
    {
        return false;
    }

    outputFailsafeLogWithSensor(getName(), true, getName(),
                                "The sensor has invalid readings.");
    return true;
}

} // namespace pid_control