/*
 * Copyright (C) 2016 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.
 */

#ifndef NANOPACKET_H_
#define NANOPACKET_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "noncopyable.h"

namespace android {

/*
 * The various reasons for a NanoPacket to be sent.
 */
enum class PacketReason : uint32_t {
    Acknowledge        = 0x00000000,
    NAcknowledge       = 0x00000001,
    NAcknowledgeBusy   = 0x00000002,
    GetHardwareVersion = 0x00001000,
    ReadEventRequest   = 0x00001090,
    WriteEventRequest  = 0x00001091,
};

/*
 * A NanoPacket parsing engine. Used to take a stream of bytes and convert them
 * into an object that can more easily be worked with.
 */
class NanoPacket : public NonCopyable {
  public:
    /*
     * The result of parsing a buffer into the packet.
     */
    enum class ParseResult {
        Success,
        Incomplete,
        CrcMismatch,
    };

    // Formats data into NanoPacket format in the provided buffer.
    NanoPacket(uint32_t sequence_number, PacketReason reason,
        const std::vector<uint8_t> *data = nullptr);

    // Creates an empty NanoPacket for data to be parsed into.
    NanoPacket();

    // Resets the parsing engine to the idle state and clears parsed content.
    void Reset();

    // Parses content from a buffer. Returns true if a packet has been entirely
    // parsed.
    ParseResult Parse(uint8_t *buffer, size_t length, size_t *bytes_parsed);

    // Indicated that parsing of the packet has completed.
    bool ParsingIsComplete() const;

    // The entire content of the message.
    const std::vector<uint8_t>& packet_buffer() const;

    // Obtains the reason for the packet.
    uint32_t reason() const;

    // Obtains the reason as a PacketReason.
    PacketReason TypedReason() const;

    // Obtains the data content of the packet.
    const std::vector<uint8_t>& packet_content() const;

  private:
    /*
     * The current state of the parser.
     */
    enum class ParsingState {
        Idle,
        ParsingSequenceNumber,
        ParsingReason,
        ParsingLength,
        ParsingContent,
        ParsingCrc,
        Complete,
    };

    // Parsing engine state.
    std::vector<uint8_t> packet_buffer_;
    ParsingState parsing_state_;
    uint32_t parsing_progress_;

    // Parsed protocol fields.
    uint32_t sequence_number_;
    uint32_t reason_;
    std::vector<uint8_t> packet_content_;
    uint32_t crc_;

    // Validates that the received packet has a CRC that matches a generated
    // CRC.
    bool ValidateCrc();

    // Deserializes a little-endian word using the parsing_progress_ member to
    // maintain state.
    template<typename T>
    bool DeserializeWord(T *destination, uint8_t byte);
};

}  // namespace android

#include "nanopacket_impl.h"

#endif  // NANOPACKET_H_