/*
 * accel.c
 *
 *  Created on: Oct 13, 2024
 *      Author: true
 */

#include "accel.h"

#include "hw/lis2dw12_reg.h"
#include "comm/i2c.h"

#include "misc/i8atan2.h"

#include <stdio.h>
#include <stdlib.h>



// user data
AccelData accel;

uint8_t accel_found = 0;

uint8_t movement_idx;       // index into "read" register
int16_t movement_read[4];   // last read movement value
int16_t movement;           // last calculated movement value
uint16_t movement_worst;    // worst seen movement value


// hardware
static stmdev_ctx_t dev_ctx;



int32_t accel_i2c_write(void *handle, uint8_t reg, const uint8_t *bufp, uint16_t len)
{
    (void)(handle);

    i2c_write_reg8(LIS2DW_I2C_ADDR_SDO_LOW, reg, bufp, len);

    return 0;
}

int32_t accel_i2c_read(void *handle, uint8_t reg, uint8_t *bufp, uint16_t len)
{
    (void)(handle);

    i2c_read_reg8(LIS2DW_I2C_ADDR_SDO_LOW, reg, bufp, len);

    return 0;
}


void accel_init()
{
    uint8_t devid;
    uint8_t reset;
    uint16_t timeout = 1000;

    dev_ctx.write_reg = accel_i2c_write;
    dev_ctx.read_reg = accel_i2c_read;

    // make sure we've got the right device
    lis2dw12_device_id_get(&dev_ctx, &devid);
    if (devid == LIS2DW12_ID) {
        accel_found = 1;
    }

    // reset accelerometer
    if (accel_found) {
        lis2dw12_reset_set(&dev_ctx, PROPERTY_ENABLE);
        do {
            lis2dw12_reset_get(&dev_ctx, &reset);
            if (!reset) {
                timeout = 0;
                accel_found = 1;
            }
        } while (timeout--);
    }

    if (accel_found) {
        // disable block update
        // data in output registers is updated immediately; FIFO is disabled
        lis2dw12_block_data_update_set(&dev_ctx, PROPERTY_DISABLE);

        // configure scale, power mode
        lis2dw12_full_scale_set(&dev_ctx, LIS2DW12_2g);
        lis2dw12_power_mode_set(&dev_ctx, LIS2DW12_CONT_LOW_PWR_LOW_NOISE_4);

        // configure filter chain
        // low pass filter enabled for 6D (not currently used)
        lis2dw12_filter_path_set(&dev_ctx, LIS2DW12_LPF_ON_OUT);
        // digital LPF2 filter of output data
        lis2dw12_filter_bandwidth_set(&dev_ctx, LIS2DW12_ODR_DIV_4);

        // configure output data rate
        lis2dw12_data_rate_set(&dev_ctx, LIS2DW12_XL_ODR_200Hz);
    }
}

__HIGH_CODE
void accel_poll()
{
    uint8_t reg = 1;
    int16_t xyz[3];

    if (!accel_found) return;

    while (reg) {
        // read output only if new value is available
        lis2dw12_flag_data_ready_get(&dev_ctx, &reg);

        if (reg) {
            // read acceleration data
            memset(xyz, 0x00, 3 * sizeof(int16_t));
            lis2dw12_acceleration_raw_get(&dev_ctx, xyz);

            accel.x = xyz[0] >>= 8;
            accel.y = xyz[1] >>= 8;
            accel.z = xyz[2] >>= 8;

            // compute our shitty "movement" thing
            // awful way to detect being still
            reg = sizeof(movement_read) / sizeof(movement_read[0]);

            movement_idx++;
            if (movement_idx == reg) {
                movement_idx = 0;
                movement = abs(movement_read[3] - movement_read[0]);
                if (movement > movement_worst) {
                    movement_worst = movement;
                }
            }

            movement_read[movement_idx] = abs(accel.x) + abs(accel.y);
        }
    }
}

__HIGH_CODE
int8_t accel_get_rotation(struct AccelData *a)
{
    int8_t nx, ny, ret;

    nx = -a->x;
    ny =  a->y;

    ret = i8atan2(nx, ny) >> 1;
    if (ret < 0) {
        ret += 128;
    }

    return ret;
}

int16_t accel_get_movement()
{
    return movement;
}
