#include <stdio.h>
 #include <chrono>
 #include <math.h>
+#include <mutex>
+#include <thread>
 
 // Pipes
 #include <sys/types.h>
 #include <spdlog/sinks/stdout_color_sinks.h>
 #include <common/mavlink.h>
 #include <bcm2835.h>
+#include <Eigen/Core>
 
 // Local
 #include "pipe_def.h"
 static err_t spi_init();
 static err_t spi_write_alias(uint8_t reg, uint8_t* data_out, uint8_t len);
 static err_t spi_read_alias(uint8_t reg, uint8_t* data_in, uint8_t len);
+[[noreturn]] static void send_mavlink(int);
+
+static vec3d gyro_vec;
+static vec3d accel_vec;
+static double temperature = 0.0;
+static Complementary_filter comp_filter{logger, 0.02};
+
+static std::mutex data_lock;
 
 int main(int argc, char* argv[])
 {
-    
-    logger = spdlog::stdout_color_mt("click_reader");
+    logger = spdlog::stdout_color_mt("controller");
     logger->set_level(GLOB_LOGLEVEL);
 
     logger->debug("Opening sendpipe for writing.");
     int sendpipe_fd = open(SEND_PIPE, O_WRONLY);
 
+    logger->debug("Opening recvpipe for reading.");
+    int recvpipe_fd = open(RECV_PIPE, O_RDONLY);
+
     logger->info("Initializing BCM2835 and SPI."); 
     if (spi_init() != OK)
     {
 
     c6dofimu17_cfg(&gyro_cfg, &accel_cfg);
 
-    c6dofimu17_axis_t accel_data;
-    c6dofimu17_axis_t gyro_data;
-    double temperature = 0.0;
-
-    Complementary_filter comp_filter{logger, 0.02};
-
     int res = 0;
     auto t_start = std::chrono::high_resolution_clock::now();
     auto t_end = std::chrono::high_resolution_clock::now();
     double time_d = 0.0;
 
-    mavlink_message_t msg;
-    mavlink_attitude_t attitude;
-    mavlink_scaled_imu_t scaled_imu; 
+    c6dofimu17_axis_t accel_data;
+    c6dofimu17_axis_t gyro_data;
+
+    // Mavlink thread
+    std::thread mavlink_task{send_mavlink, sendpipe_fd};
+
     // Control loop
     while (true) 
     {
         THREAD_SLEEP(CLICK_READ_SPI_PERIOD);
 
+        std::lock_guard<std::mutex> lk{data_lock};
+
+        logger->debug("Controlling!");
+
         res = c6dofimu17_get_gyro_data(&gyro_data);
         t_end = std::chrono::high_resolution_clock::now();
         time_d = std::chrono::duration<double>(t_end-t_start).count();
         t_start = std::chrono::high_resolution_clock::now();
 
         res += c6dofimu17_get_accel_data(&accel_data);
-
         res += c6dofimu17_get_temperature(&temperature);
 
         if (res != OK) {
             continue;
         }
 
-        vec3d gyro_vec = struct_to_vec_scaled(gyro_data, (M_PI * 1000.0) / (32768.0 * 180.0));
-        vec3d accel_vec = struct_to_vec_scaled(accel_data, (GRAVITY * 16.0) / 32768.0);
+        gyro_vec = struct_to_vec_scaled(gyro_data, (M_PI * 1000.0) / (32768.0 * 180.0));
+        accel_vec = struct_to_vec_scaled(accel_data, (GRAVITY * 16.0) / 32768.0);
 
         comp_filter.update(gyro_vec, accel_vec, time_d);
-        
-        // Pack data into MAVLINK packages
-        attitude.time_boot_ms = system_uptime_ms(); 
-        attitude.roll = comp_filter.get_state()[1];
-        attitude.pitch = comp_filter.get_state()[0];
-        attitude.yaw = 0.0;
-        attitude.rollspeed = 0.0;
-        attitude.pitchspeed = 0.0;
-        attitude.yawspeed = 0.0;
-
-        scaled_imu.time_boot_ms = system_uptime_ms();
-        scaled_imu.temperature = static_cast<int16_t>(temperature * 100);
-        scaled_imu.xacc = static_cast<int16_t>(accel_vec[0] * 1000); // Maybe not correct?
-        scaled_imu.yacc = static_cast<int16_t>(accel_vec[1] * 1000); // Maybe not correct?
-        scaled_imu.zacc = static_cast<int16_t>(accel_vec[2] * 1000); // Maybe not correct?
-        scaled_imu.xgyro = static_cast<int16_t>(gyro_vec[0] * 1000); // Maybe not correct?
-        scaled_imu.ygyro = static_cast<int16_t>(gyro_vec[1] * 1000); // Maybe not correct?
-        scaled_imu.zgyro = static_cast<int16_t>(gyro_vec[2] * 1000); // Maybe not correct?
-        scaled_imu.xmag = INT16_MAX;
-        scaled_imu.ymag = INT16_MAX;
-        scaled_imu.zmag = INT16_MAX;
-
-        mavlink_msg_attitude_encode(
-            MAV_SYS_ID, 
-            MAV_COMP_ID,
-            &msg,
-            &attitude
-        );
-
-        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
-        int len;
-
-        len = mavlink_msg_to_send_buffer(buffer, &msg);
-        write(sendpipe_fd, buffer, len);
-
-        mavlink_msg_scaled_imu_encode(
-            MAV_SYS_ID, 
-            MAV_COMP_ID,
-            &msg,
-            &scaled_imu
-        );
-        len = mavlink_msg_to_send_buffer(buffer, &msg);
-        write(sendpipe_fd, buffer, len);
     }
 
     close(sendpipe_fd);
     bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW);      // the default
 
     return OK;
+}
+
+[[noreturn]] static void send_mavlink(int sendpipe_fd)
+{
+    mavlink_message_t msg;
+    mavlink_attitude_t attitude;
+    mavlink_scaled_imu_t scaled_imu;
+
+    while (true)
+    {
+        THREAD_SLEEP(CONTROLLER_MAVLINK_PERIOD);
+        std::lock_guard<std::mutex> lk{data_lock};
+
+        logger->debug("Sending IMU Mavlink.");
+
+        // Pack data into MAVLINK packages
+        attitude.time_boot_ms = system_uptime_ms(); 
+        attitude.roll = comp_filter.get_state()[1];
+        attitude.pitch = comp_filter.get_state()[0];
+        attitude.yaw = 0.0;
+        attitude.rollspeed = 0.0;
+        attitude.pitchspeed = 0.0;
+        attitude.yawspeed = 0.0;
+
+        scaled_imu.time_boot_ms = system_uptime_ms();
+        scaled_imu.temperature = static_cast<int16_t>(temperature * 100);
+        scaled_imu.xacc = static_cast<int16_t>(accel_vec[0] * 1000); // Maybe not correct?
+        scaled_imu.yacc = static_cast<int16_t>(accel_vec[1] * 1000); // Maybe not correct?
+        scaled_imu.zacc = static_cast<int16_t>(accel_vec[2] * 1000); // Maybe not correct?
+        scaled_imu.xgyro = static_cast<int16_t>(gyro_vec[0] * 1000); // Maybe not correct?
+        scaled_imu.ygyro = static_cast<int16_t>(gyro_vec[1] * 1000); // Maybe not correct?
+        scaled_imu.zgyro = static_cast<int16_t>(gyro_vec[2] * 1000); // Maybe not correct?
+        scaled_imu.xmag = INT16_MAX;
+        scaled_imu.ymag = INT16_MAX;
+        scaled_imu.zmag = INT16_MAX;
+
+        mavlink_msg_attitude_encode(
+            MAV_SYS_ID, 
+            MAV_COMP_ID,
+            &msg,
+            &attitude
+        );
+
+        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
+        int len;
+
+        len = mavlink_msg_to_send_buffer(buffer, &msg);
+        write(sendpipe_fd, buffer, len);
+
+        mavlink_msg_scaled_imu_encode(
+            MAV_SYS_ID, 
+            MAV_COMP_ID,
+            &msg,
+            &scaled_imu
+        );
+        len = mavlink_msg_to_send_buffer(buffer, &msg);
+        write(sendpipe_fd, buffer, len);
+    }
 }
\ No newline at end of file