//
// Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//

#pragma once

#include "IPacketBuffer.hpp"

#include <client/include/ISendTimelinePacket.hpp>

#include <common/include/ICounterDirectory.hpp>
#include <common/include/Packet.hpp>
#include <common/include/ProfilingException.hpp>

#include <algorithm>
#include <cstring>
#include <memory>
#include <string>
#include <thread>
#include <vector>

namespace arm
{

namespace pipe
{

constexpr unsigned int ThreadIdSize = sizeof(int); // Is platform dependent

void WriteBytes(const IPacketBuffer& packetBuffer, unsigned int offset, const void* value, unsigned int valueSize);

uint32_t ConstructHeader(uint32_t packetFamily, uint32_t packetId);

uint32_t ConstructHeader(uint32_t packetFamily, uint32_t packetClass, uint32_t packetType);

void WriteUint64(const IPacketBufferPtr& packetBuffer, unsigned int offset, uint64_t value);

void WriteUint32(const IPacketBufferPtr& packetBuffer, unsigned int offset, uint32_t value);

void WriteUint16(const IPacketBufferPtr& packetBuffer, unsigned int offset, uint16_t value);

void WriteUint8(const IPacketBufferPtr& packetBuffer, unsigned int offset, uint8_t value);

void WriteBytes(unsigned char* buffer, unsigned int offset, const void* value, unsigned int valueSize);

void WriteUint64(unsigned char* buffer, unsigned int offset, uint64_t value);

void WriteUint32(unsigned char* buffer, unsigned int offset, uint32_t value);

void WriteUint16(unsigned char* buffer, unsigned int offset, uint16_t value);

void WriteUint8(unsigned char* buffer, unsigned int offset, uint8_t value);

void ReadBytes(const IPacketBufferPtr& packetBuffer, unsigned int offset, unsigned int valueSize, uint8_t outValue[]);

uint64_t ReadUint64(const IPacketBufferPtr& packetBuffer, unsigned int offset);

uint32_t ReadUint32(const IPacketBufferPtr& packetBuffer, unsigned int offset);

uint16_t ReadUint16(const IPacketBufferPtr& packetBuffer, unsigned int offset);

uint8_t ReadUint8(const IPacketBufferPtr& packetBuffer, unsigned int offset);

void ReadBytes(const unsigned char* buffer, unsigned int offset, unsigned int valueSize, uint8_t outValue[]);

uint64_t ReadUint64(unsigned const char* buffer, unsigned int offset);

uint32_t ReadUint32(unsigned const char* buffer, unsigned int offset);

uint16_t ReadUint16(unsigned const char* buffer, unsigned int offset);

uint8_t ReadUint8(unsigned const char* buffer, unsigned int offset);

std::pair<uint32_t, uint32_t> CreateTimelinePacketHeader(uint32_t packetFamily,
                                                         uint32_t packetClass,
                                                         uint32_t packetType,
                                                         uint32_t streamId,
                                                         uint32_t sequenceNumbered,
                                                         uint32_t dataLength);

std::string GetProcessName();

enum class TimelinePacketStatus
{
    Ok,
    Error,
    BufferExhaustion
};

TimelinePacketStatus WriteTimelineLabelBinaryPacket(uint64_t profilingGuid,
                                                    const std::string& label,
                                                    unsigned char* buffer,
                                                    unsigned int bufferSize,
                                                    unsigned int& numberOfBytesWritten);

TimelinePacketStatus WriteTimelineEntityBinary(uint64_t profilingGuid,
                                               unsigned char* buffer,
                                               unsigned int bufferSize,
                                               unsigned int& numberOfBytesWritten);

TimelinePacketStatus WriteTimelineRelationshipBinary(ProfilingRelationshipType relationshipType,
                                                     uint64_t relationshipGuid,
                                                     uint64_t headGuid,
                                                     uint64_t tailGuid,
                                                     uint64_t attributeGuid,
                                                     unsigned char* buffer,
                                                     unsigned int bufferSize,
                                                     unsigned int& numberOfBytesWritten);

TimelinePacketStatus WriteTimelineMessageDirectoryPackage(unsigned char* buffer,
                                                          unsigned int bufferSize,
                                                          unsigned int& numberOfBytesWritten);

TimelinePacketStatus WriteTimelineEventClassBinary(uint64_t profilingGuid,
                                                   uint64_t nameGuid,
                                                   unsigned char* buffer,
                                                   unsigned int bufferSize,
                                                   unsigned int& numberOfBytesWritten);

TimelinePacketStatus WriteTimelineEventBinary(uint64_t timestamp,
                                              int threadId,
                                              uint64_t profilingGuid,
                                              unsigned char* buffer,
                                              unsigned int bufferSize,
                                              unsigned int& numberOfBytesWritten);

std::string CentreAlignFormatting(const std::string& stringToPass, const int spacingWidth);

uint64_t GetTimestamp();

arm::pipe::Packet ReceivePacket(const unsigned char* buffer, uint32_t length);

} // namespace pipe

} // namespace arm

namespace std
{

bool operator==(const std::vector<uint8_t>& left, int right);

} // namespace std
