/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * A service that exchanges time synchronization information between
 * a master that defines a timeline and clients that follow the timeline.
 */

#define LOG_TAG "common_time"
#include <utils/Log.h>

#include <arpa/inet.h>
#include <stdint.h>

#include "common_time_server_packets.h"

namespace android {

const uint32_t TimeServicePacketHeader::kMagic =
    (static_cast<uint32_t>('c') << 24) |
    (static_cast<uint32_t>('c') << 16) |
    (static_cast<uint32_t>('l') <<  8) |
     static_cast<uint32_t>('k');

const uint16_t TimeServicePacketHeader::kCurVersion = 1;

#define SERIALIZE_FIELD(field_name, type, converter)        \
    do {                                                    \
        if ((offset + sizeof(field_name)) > length)         \
            return -1;                                      \
        *((type*)(data + offset)) = converter(field_name);  \
        offset += sizeof(field_name);                       \
    } while (0)
#define SERIALIZE_INT16(field_name) SERIALIZE_FIELD(field_name, int16_t, htons)
#define SERIALIZE_INT32(field_name) SERIALIZE_FIELD(field_name, int32_t, htonl)
#define SERIALIZE_INT64(field_name) SERIALIZE_FIELD(field_name, int64_t, htonq)

#define DESERIALIZE_FIELD(field_name, type, converter)      \
    do {                                                    \
        if ((offset + sizeof(field_name)) > length)         \
            return -1;                                      \
        field_name = converter(*((type*)(data + offset)));  \
        offset += sizeof(field_name);                       \
    } while (0)
#define DESERIALIZE_INT16(field_name) DESERIALIZE_FIELD(field_name, int16_t, ntohs)
#define DESERIALIZE_INT32(field_name) DESERIALIZE_FIELD(field_name, int32_t, ntohl)
#define DESERIALIZE_INT64(field_name) DESERIALIZE_FIELD(field_name, int64_t, ntohq)

#define kDevicePriorityShift 56
#define kDeviceIDMask ((static_cast<uint64_t>(1) << kDevicePriorityShift) - 1)

inline uint64_t packDeviceID(uint64_t devID, uint8_t prio) {
    return (devID & kDeviceIDMask) |
           (static_cast<uint64_t>(prio) << kDevicePriorityShift);
}

inline uint64_t unpackDeviceID(uint64_t packed) {
    return (packed & kDeviceIDMask);
}

inline uint8_t unpackDevicePriority(uint64_t packed) {
    return static_cast<uint8_t>(packed >> kDevicePriorityShift);
}

ssize_t TimeServicePacketHeader::serializeHeader(uint8_t* data,
                                                 uint32_t length) {
    ssize_t offset = 0;
    int16_t pktType = static_cast<int16_t>(packetType);
    SERIALIZE_INT32(magic);
    SERIALIZE_INT16(version);
    SERIALIZE_INT16(pktType);
    SERIALIZE_INT64(timelineID);
    SERIALIZE_INT64(syncGroupID);
    return offset;
}

ssize_t TimeServicePacketHeader::deserializeHeader(const uint8_t* data,
                                                   uint32_t length) {
    ssize_t offset = 0;
    int16_t tmp;
    DESERIALIZE_INT32(magic);
    DESERIALIZE_INT16(version);
    DESERIALIZE_INT16(tmp);
    DESERIALIZE_INT64(timelineID);
    DESERIALIZE_INT64(syncGroupID);
    packetType = static_cast<TimeServicePacketType>(tmp);
    return offset;
}

ssize_t TimeServicePacketHeader::serializePacket(uint8_t* data,
                                                 uint32_t length) {
    ssize_t ret, tmp;

    ret = serializeHeader(data, length);
    if (ret < 0)
        return ret;

    data += ret;
    length -= ret;

    switch (packetType) {
        case TIME_PACKET_WHO_IS_MASTER_REQUEST:
            tmp =((WhoIsMasterRequestPacket*)(this))->serializePacket(data,
                                                                      length);
            break;
        case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
            tmp =((WhoIsMasterResponsePacket*)(this))->serializePacket(data,
                                                                       length);
            break;
        case TIME_PACKET_SYNC_REQUEST:
            tmp =((SyncRequestPacket*)(this))->serializePacket(data, length);
            break;
        case TIME_PACKET_SYNC_RESPONSE:
            tmp =((SyncResponsePacket*)(this))->serializePacket(data, length);
            break;
        case TIME_PACKET_MASTER_ANNOUNCEMENT:
            tmp =((MasterAnnouncementPacket*)(this))->serializePacket(data,
                                                                      length);
            break;
        default:
            return -1;
    }

    if (tmp < 0)
        return tmp;

    return ret + tmp;
}

ssize_t UniversalTimeServicePacket::deserializePacket(
        const uint8_t* data,
        uint32_t length,
        uint64_t expectedSyncGroupID) {
    ssize_t ret;
    TimeServicePacketHeader* header;
    if (length < 8)
        return -1;

    packetType = ntohs(*((uint16_t*)(data + 6)));
    switch (packetType) {
        case TIME_PACKET_WHO_IS_MASTER_REQUEST:
            ret = p.who_is_master_request.deserializePacket(data, length);
            header = &p.who_is_master_request;
            break;
        case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
            ret = p.who_is_master_response.deserializePacket(data, length);
            header = &p.who_is_master_response;
            break;
        case TIME_PACKET_SYNC_REQUEST:
            ret = p.sync_request.deserializePacket(data, length);
            header = &p.sync_request;
            break;
        case TIME_PACKET_SYNC_RESPONSE:
            ret = p.sync_response.deserializePacket(data, length);
            header = &p.sync_response;
            break;
        case TIME_PACKET_MASTER_ANNOUNCEMENT:
            ret = p.master_announcement.deserializePacket(data, length);
            header = &p.master_announcement;
            break;
        default:
            return -1;
    }

    if ((ret >= 0) && !header->checkPacket(expectedSyncGroupID))
        ret = -1;

    return ret;
}

ssize_t WhoIsMasterRequestPacket::serializePacket(uint8_t* data,
                                                  uint32_t length) {
    ssize_t offset = serializeHeader(data, length);
    if (offset > 0) {
        uint64_t packed = packDeviceID(senderDeviceID, senderDevicePriority);
        SERIALIZE_INT64(packed);
    }
    return offset;
}

ssize_t WhoIsMasterRequestPacket::deserializePacket(const uint8_t* data,
                                                    uint32_t length) {
    ssize_t offset = deserializeHeader(data, length);
    if (offset > 0) {
        uint64_t packed;
        DESERIALIZE_INT64(packed);
        senderDeviceID       = unpackDeviceID(packed);
        senderDevicePriority = unpackDevicePriority(packed);
    }
    return offset;
}

ssize_t WhoIsMasterResponsePacket::serializePacket(uint8_t* data,
                                                   uint32_t length) {
    ssize_t offset = serializeHeader(data, length);
    if (offset > 0) {
        uint64_t packed = packDeviceID(deviceID, devicePriority);
        SERIALIZE_INT64(packed);
    }
    return offset;
}

ssize_t WhoIsMasterResponsePacket::deserializePacket(const uint8_t* data,
                                                     uint32_t length) {
    ssize_t offset = deserializeHeader(data, length);
    if (offset > 0) {
        uint64_t packed;
        DESERIALIZE_INT64(packed);
        deviceID       = unpackDeviceID(packed);
        devicePriority = unpackDevicePriority(packed);
    }
    return offset;
}

ssize_t SyncRequestPacket::serializePacket(uint8_t* data,
                                           uint32_t length) {
    ssize_t offset = serializeHeader(data, length);
    if (offset > 0) {
        SERIALIZE_INT64(clientTxLocalTime);
    }
    return offset;
}

ssize_t SyncRequestPacket::deserializePacket(const uint8_t* data,
                                             uint32_t length) {
    ssize_t offset = deserializeHeader(data, length);
    if (offset > 0) {
        DESERIALIZE_INT64(clientTxLocalTime);
    }
    return offset;
}

ssize_t SyncResponsePacket::serializePacket(uint8_t* data,
                                            uint32_t length) {
    ssize_t offset = serializeHeader(data, length);
    if (offset > 0) {
        SERIALIZE_INT64(clientTxLocalTime);
        SERIALIZE_INT64(masterRxCommonTime);
        SERIALIZE_INT64(masterTxCommonTime);
        SERIALIZE_INT32(nak);
    }
    return offset;
}

ssize_t SyncResponsePacket::deserializePacket(const uint8_t* data,
                                              uint32_t length) {
    ssize_t offset = deserializeHeader(data, length);
    if (offset > 0) {
        DESERIALIZE_INT64(clientTxLocalTime);
        DESERIALIZE_INT64(masterRxCommonTime);
        DESERIALIZE_INT64(masterTxCommonTime);
        DESERIALIZE_INT32(nak);
    }
    return offset;
}

ssize_t MasterAnnouncementPacket::serializePacket(uint8_t* data,
                                                  uint32_t length) {
    ssize_t offset = serializeHeader(data, length);
    if (offset > 0) {
        uint64_t packed = packDeviceID(deviceID, devicePriority);
        SERIALIZE_INT64(packed);
    }
    return offset;
}

ssize_t MasterAnnouncementPacket::deserializePacket(const uint8_t* data,
                                                    uint32_t length) {
    ssize_t offset = deserializeHeader(data, length);
    if (offset > 0) {
        uint64_t packed;
        DESERIALIZE_INT64(packed);
        deviceID       = unpackDeviceID(packed);
        devicePriority = unpackDevicePriority(packed);
    }
    return offset;
}

}  // namespace android