/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <string.h>
#include <errno.h>
#include <assert.h>

#include "os/mynewt.h"

#include "sensor/sensor.h"
#include "sensor/mag.h"

#include "sim/sim_mag.h"

/* Exports for the sensor interface.
 */
static int sim_mag_sensor_read(struct sensor *, sensor_type_t,
        sensor_data_func_t, void *, uint32_t);
static int sim_mag_sensor_get_config(struct sensor *, sensor_type_t,
        struct sensor_cfg *);

static const struct sensor_driver g_sim_mag_sensor_driver = {
    sim_mag_sensor_read,
    sim_mag_sensor_get_config
};

/**
 * Expects to be called back through os_dev_create().
 *
 * @param The device object associated with this magnetometer
 * @param Argument passed to OS device init, unused
 *
 * @return 0 on success, non-zero error on failure.
 */
int
sim_mag_init(struct os_dev *dev, void *arg)
{
    struct sim_mag *sm;
    struct sensor *sensor;
    int rc;

    sm = (struct sim_mag *) dev;

    sensor = &sm->sm_sensor;

    rc = sensor_init(sensor, dev);
    if (rc != 0) {
        goto err;
    }

    rc = sensor_set_driver(sensor, SENSOR_TYPE_MAGNETIC_FIELD,
            (struct sensor_driver *) &g_sim_mag_sensor_driver);
    if (rc != 0) {
        goto err;
    }

    rc = sensor_mgr_register(sensor);
    if (rc != 0) {
        goto err;
    }

    return (0);
err:
    return (rc);
}

int
sim_mag_config(struct sim_mag *sm, struct sim_mag_cfg *cfg)
{
    /* Overwrite the configuration associated with this generic magnetometer. */
    memcpy(&sm->sm_cfg, cfg, sizeof(*cfg));

    return (0);
}

static int
sim_mag_sensor_read(struct sensor *sensor, sensor_type_t type,
        sensor_data_func_t data_func, void *data_arg, uint32_t timeout)
{
    struct sim_mag *sm;
    struct sensor_mag_data smd;
    os_time_t now;
    uint32_t num_samples;
    int i;
    int rc;

    /* If the read isn't looking for mag data, then don't do anything. */
    if (!(type & SENSOR_TYPE_MAGNETIC_FIELD)) {
        rc = SYS_EINVAL;
        goto err;
    }

    sm = (struct sim_mag *) SENSOR_GET_DEVICE(sensor);

    /* When a sensor is "read", we get the last 'n' samples from the device
     * and pass them to the sensor data function.  Based on the sample
     * interval provided to sim_mag_config() and the last time this function
     * was called, 'n' samples are generated.
     */
    now = os_time_get();

    num_samples = (now - sm->sm_last_read_time) / sm->sm_cfg.smc_sample_itvl;
    num_samples = min(num_samples, sm->sm_cfg.smc_nr_samples);

    /* By default only readings are provided for 1-axis (x), however,
     * if number of axises is configured, up to 3-axises of data can be
     * returned.
     */
    smd.smd_x = 0.0;
    smd.smd_y = 0.0;
    smd.smd_z = 0.0;

    smd.smd_x_is_valid = 1;
    smd.smd_y_is_valid = 0;
    smd.smd_z_is_valid = 0;

    if (sm->sm_cfg.smc_nr_axises > 1) {
        smd.smd_y = 0.0;
    }
    if (sm->sm_cfg.smc_nr_axises > 2) {
        smd.smd_z = 0.0;
    }

    /* Call data function for each of the generated readings. */
    for (i = 0; i < num_samples; i++) {
        rc = data_func(sensor, data_arg, &smd, SENSOR_TYPE_MAGNETIC_FIELD);
        if (rc != 0) {
            goto err;
        }
    }

    return (0);
err:
    return (rc);
}

static int
sim_mag_sensor_get_config(struct sensor *sensor, sensor_type_t type,
        struct sensor_cfg *cfg)
{
    int rc;

    if (type != SENSOR_TYPE_MAGNETIC_FIELD) {
        rc = SYS_EINVAL;
        goto err;
    }

    cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT_TRIPLET;

    return (0);
err:
    return (rc);
}
