//===-- GDBRemoteCommunicationServer.cpp ------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <errno.h>

#include "lldb/Host/Config.h"

#include "GDBRemoteCommunicationServer.h"

// C Includes
// C++ Includes
#include <cstring>

// Project includes
#include "ProcessGDBRemoteLog.h"
#include "Utility/StringExtractorGDBRemote.h"

using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::process_gdb_remote;

GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(const char *comm_name,
                                                           const char *listener_name) :
    GDBRemoteCommunication (comm_name, listener_name),
    m_exit_now (false)
{
}

GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer()
{
}

void GDBRemoteCommunicationServer::RegisterPacketHandler(
        StringExtractorGDBRemote::ServerPacketType packet_type,
        PacketHandler handler)
{
    m_packet_handlers[packet_type] = std::move(handler);
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,
                                                        Error &error,
                                                        bool &interrupt,
                                                        bool &quit)
{
    StringExtractorGDBRemote packet;

    PacketResult packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec, false);
    if (packet_result == PacketResult::Success)
    {
        const StringExtractorGDBRemote::ServerPacketType packet_type = packet.GetServerPacketType ();
        switch (packet_type)
        {
        case StringExtractorGDBRemote::eServerPacketType_nack:
        case StringExtractorGDBRemote::eServerPacketType_ack:
            break;

        case StringExtractorGDBRemote::eServerPacketType_invalid:
            error.SetErrorString("invalid packet");
            quit = true;
            break;

        case StringExtractorGDBRemote::eServerPacketType_unimplemented:
            packet_result = SendUnimplementedResponse (packet.GetStringRef().c_str());
            break;

        default:
            auto handler_it = m_packet_handlers.find(packet_type);
            if (handler_it == m_packet_handlers.end())
                packet_result = SendUnimplementedResponse (packet.GetStringRef().c_str());
            else
                packet_result = handler_it->second (packet, error, interrupt, quit);
            break;
        }
    }
    else
    {
        if (!IsConnected())
        {
            error.SetErrorString("lost connection");
            quit = true;
        }
        else
        {
            error.SetErrorString("timeout");
        }
    }

    // Check if anything occurred that would force us to want to exit.
    if (m_exit_now)
        quit = true;

    return packet_result;
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendUnimplementedResponse (const char *)
{
    // TODO: Log the packet we aren't handling...
    return SendPacketNoLock ("", 0);
}


GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendErrorResponse (uint8_t err)
{
    char packet[16];
    int packet_len = ::snprintf (packet, sizeof(packet), "E%2.2x", err);
    assert (packet_len < (int)sizeof(packet));
    return SendPacketNoLock (packet, packet_len);
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendIllFormedResponse (const StringExtractorGDBRemote &failed_packet, const char *message)
{
    Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
    if (log)
        log->Printf ("GDBRemoteCommunicationServer::%s: ILLFORMED: '%s' (%s)", __FUNCTION__, failed_packet.GetStringRef ().c_str (), message ? message : "");
    return SendErrorResponse (0x03);
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::SendOKResponse ()
{
    return SendPacketNoLock ("OK", 2);
}

bool
GDBRemoteCommunicationServer::HandshakeWithClient()
{
    return GetAck() == PacketResult::Success;
}