#include "udp_server.h"
 
+// Standard
+#include <thread>
+#include <memory>
+
+// Socket
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+// Pipes
+#include <unistd.h>
+
+// Libs
+#include <common/mavlink.h>
+#include <spdlog/spdlog.h>
+
+// Local
+#include "system_def.h"
+#include "pipe_def.h"
+#include "util.h"
+
 static std::shared_ptr<spdlog::logger> logger;
 
 static bool recv_thread_running = true;
 static gcs_t gcs = {};
 
+static pipe_fd_t recvpipe; 
+static pipe_fd_t sendpipe; 
+
 // Declarations
-static void recv_thread(pipe_fd_t sendpipe_fd);
+static void handle_request_autopilot_capabilities(const mavlink_command_long_t& cmd_long);
+static void handle_heartbeat(const mavlink_message_t& message);
+static void recv_thread();
 static void heartbeat_thread();
 static void handle_mavlink(mavlink_message_t& message);
 
 // Definitions
+static void handle_heartbeat(const mavlink_message_t& message)
+{
+    // Determine source of heartbeat
+    mavlink_heartbeat_t heartbeat;
+    mavlink_msg_heartbeat_decode(&message, &heartbeat);
+
+    switch (heartbeat.type) {
+        case MAV_TYPE_GCS:
+            logger->debug("Heartbeat from GCS!");
+            break;
+        default:
+            logger->warn("Heartbeat from unknown type: {}", heartbeat.type);
+            break;
+    }
+}
+
+static void handle_request_autopilot_capabilities(const mavlink_command_long_t& cmd_long)
+{
+    // No need to handle the message, only send out a response
+    mavlink_message_t response_msg;
+    mavlink_autopilot_version_t version_struct = 
+    {
+
+    };
+
+    mavlink_msg_autopilot_version_encode(
+        MAV_SYS_ID,
+        MAV_COMP_ID_AUTOPILOT1,
+        &response_msg,
+        &version_struct
+    );
 
-static void recv_thread(pipe_fd_t recvpipe_fd)
+    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
+    auto len = mavlink_msg_to_send_buffer(buffer, &response_msg);
+
+    auto ret = sendto(gcs.socket_fd, buffer, len, 0, (struct sockaddr*)(&(gcs.addr)), gcs.addr_len);
+    if (ret != len) {
+        logger->error("Autopilot version message error: {}.", strerror(errno));
+    } else {
+        logger->info("Sent Autopilot version!");
+    }
+}
+
+static void recv_thread()
 {
     char buffer[UDP_RECV_BUF_SIZE]; // enough for MTU 1500 bytes
 
             break;
         } else if (ret == 0) {
             // peer has done an orderly shutdown
-            logger->info("Peer has done an orderly shutdown");
+            logger->info("Peer has done an orderly shutdown.");
             break;
         }
 
                 handle_mavlink(message);
             }
         }
-        logger->debug("Socket -> recvpipe: {} bytes", ret);
-        ret = write(recvpipe_fd, buffer, ret);
-        if (ret < 0) {
-            logger->error("Error while writing message. {}", strerror(errno));
-        }
     }
 }
 
 static void heartbeat_thread()
 {
-
     mavlink_message_t message;
-
-    const uint8_t system_id = 42;
-    const uint8_t base_mode = 0;
-    const uint8_t custom_mode = 0;
-
     mavlink_msg_heartbeat_pack_chan(
-        system_id,
-        MAV_COMP_ID_PERIPHERAL,
+        1,  // System ID
+        MAV_COMP_ID_AUTOPILOT1,
         MAVLINK_COMM_0,
         &message,
         MAV_TYPE_GENERIC,
         MAV_AUTOPILOT_GENERIC,
-        base_mode,
-        custom_mode,
+        0,
+        0,
         MAV_STATE_STANDBY);
 
     uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
 
         int ret = sendto(gcs.socket_fd, buffer, len, 0, (struct sockaddr*)(&(gcs.addr)), gcs.addr_len);
         if (ret != len) {
-            logger->error("Heartbeat sendto error: {}", strerror(errno));
+            logger->error("Heartbeat sendto error: {}.", strerror(errno));
         } else {
             logger->debug("Sent Heartbeat!");
         }
 {
     switch (message.msgid) {
     case MAVLINK_MSG_ID_HEARTBEAT:
-        handle_heartbeat(&message);
+        handle_heartbeat(message);
+        break;
+    case MAVLINK_MSG_ID_COMMAND_LONG:
+        mavlink_command_long_t cmd_long;
+        mavlink_msg_command_long_decode(&message, &cmd_long);
+        
+        switch (cmd_long.command) {
+        case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
+            logger->debug("Got autopilot capabilities request.");
+            handle_request_autopilot_capabilities(cmd_long);
+            break;
+        default:
+            logger->warn("Unhandled command {} from {}/{}.", (int) cmd_long.command, message.sysid, message.compid);
+            break;
+        }
         break;
     default:
-        logger->debug("Received message {} from {}/{}", (int) message.msgid, message.sysid, message.compid);
+        logger->warn("Unhandled message {} from {}/{}.", (int) message.msgid, message.sysid, message.compid);
+        
+        logger->warn("Sending unhandled message to recvpipe: {} bytes.", message.len);
+        auto ret = write(recvpipe, message.payload64, message.len);
+        if (ret < 0) {
+            logger->error("Error while writing message to recvpipe. Errno {}.", strerror(errno));
+        }
         break;
     }
 }
 
-err_t udp_server_init(std::shared_ptr<spdlog::logger> logg)
+err_t udp_server_init(std::shared_ptr<spdlog::logger> logg, const pipe_fd_t recvpipe_fd, const pipe_fd_t sendpipe_fd)
 {
     logger = logg;
+    sendpipe = sendpipe_fd;
+    recvpipe = recvpipe_fd;
 
-    init_mavlink_handler(logger);
-
-    socket_fd_t socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
+    auto socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
 
     if (socket_fd < 0) {
         logger->error("socket error: {}", strerror(errno));
     return OK;
 }
 
-err_t udp_server_run(pipe_fd_t recvpipe_fd, pipe_fd_t sendpipe_fd)
+err_t udp_server_run()
 {
-    std::thread r_thread(recv_thread, recvpipe_fd);
+    std::thread r_thread(recv_thread);
 
     while (!(gcs.addr_set)) {
         logger->info("Waiting for GCS to connect...");
     mavlink_message_t message;
     mavlink_status_t status;
 
+    message.msgid = 0;
+
     while (true) {
-        ret = read(sendpipe_fd, &buffer, sizeof(buffer));
+        ret = read(sendpipe, &buffer, sizeof(buffer));
 
         if (ret == 0) {
             logger->error("No writing process on sendpipe. Exiting.");
         else if (ret < 0)
             logger->error("Error while reading message. {}", strerror(errno));
         else {
-            /*
+            
             for (int i = 0; i < ret; ++i) {
-                if (mavlink_parse_char(MAVLINK_COMM_0, buffer[i], &message, &status) == 1) {
-                    logger->debug("sendpipe -> socket: mavlink message id={},", (int) message.msgid);
+                if (mavlink_parse_char(MAVLINK_COMM_0, buffer[i], &message, &status)) {
+                    logger->debug("sendpipe -> socket: mavlink message id={}, bytes: {}", (int) message.msgid, ret);
                 }
             }
-            */
-            logger->debug("sendpipe -> socket: {} bytes", ret);
+            
             ret = sendto(gcs.socket_fd, buffer, ret, 0, (struct sockaddr*)(&(gcs.addr)), gcs.addr_len);
             if (ret < 0) {
-                logger->error("Sending mavlink message to GCS sendto error {}", strerror(errno));
+                logger->error("Sending mavlink message to GCS sendto error {}.", strerror(errno));
             }
 
         }
     }
 
-    logger->debug("Closing recv_thread");
+    logger->debug("Closing recv_thread.");
     recv_thread_running = false;
 
-    logger->debug("Closing socket");
+    logger->debug("Closing socket.");
     close(gcs.socket_fd);
 
     return ERR;