/******************************************************************************
 *
 *  Copyright (C) 2017 Google, Inc.
 *
 *  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.
 *
 ******************************************************************************/
#pragma once
#include <cstdint>
#include <iterator>
#include <vector>

namespace test_vendor_lib {

// Abstract representation of an SDU packet that contains an L2CAP
// payload. This class is meant to be used in collaboration with
// the L2cap class defined in l2cap.h. For example, an SDU packet
// may look as follows:
//
// vector<uint8_t> sdu = {0x04, 0x00, 0x48, 0x00, 0x04, 0x00, 0xab,
//                       0xcd, 0x78, 0x56}
//
// The first two bytes (in little endian) should be read as 0x0004
// and is the length of the payload of the SDU packet.
//
// The next two bytes (also in little endian) are the channel ID
// and should be read as 0x0048. These should remain the same for
// any number of SDUs that are a part of the same packet stream.
//
// Following the CID bytes, are the total length bytes. Since this
// SDU only requires a single packet, the length here is the same
// as the length in the first two bytes of the packet. Again stored
// in little endian.
//
// Next comes the two control bytes. These begin the L2CAP payload
// of the SDU packet; however, they will not be added to the L2CAP
// packet that is being constructed in the assemble function that
// will be creating an L2CAP packet from a stream of L2capSdu
// objects.
//
// The final two bytes are the frame check sequence that should be
// calculated from the start of the vector to the end of the
// payload.
//
// Thus, calling assemble on this example would create a
// zero-length L2CAP packet because the information payload of the
// L2CAP packet will not include either of the control or FCS
// bytes.
//
class L2capSdu {
 public:
  // Returns a completed L2capSdu object.
  L2capSdu(std::vector<uint8_t> create_from);

  static L2capSdu L2capSduBuilder(std::vector<uint8_t> create_from);

  // TODO: Remove this when the move to L2capSdu* is done
  L2capSdu& operator=(L2capSdu obj1) {
    sdu_data_.clear();

    sdu_data_ = obj1.sdu_data_;

    return *this;
  }

  // Get a vector iterator that points to the first byte of the
  // L2CAP payload within an SDU. The offset parameter will be the
  // number of bytes that are in the SDU header. This should always
  // be 6 bytes with the exception being the first SDU of a stream
  // of SDU packets where the first SDU packet will have an extra
  // two bytes and the offset should be 8 bytes.
  std::vector<uint8_t>::const_iterator get_payload_begin(
      const unsigned int offset) const;

  // Get a vector iterator that points to the last bytes of the
  // L2CAP payload within an SDU packet. There is no offset
  // parameter for this function because there will always be two
  // FCS bytes and nothing else at the end of each SDU.
  std::vector<uint8_t>::const_iterator get_payload_end() const;

  // Get the FCS bytes from the end of the L2CAP payload of an SDU
  // packet.
  uint16_t get_fcs() const;

  uint16_t get_payload_length() const;

  uint16_t calculate_fcs() const;

  // Get the two control bytes that begin the L2CAP payload. These
  // bytes will contain information such as the Segmentation and
  // Reassembly bits, and the TxSeq/ReqSeq numbers.
  uint16_t get_controls() const;

  uint16_t get_total_l2cap_length() const;

  size_t get_vector_size() const;

  uint16_t get_channel_id() const;

  // Returns true if the SDU control sequence for Segmentation and
  // Reassembly is 00b, false otherwise.
  static bool is_complete_l2cap(const L2capSdu& sdu);

  // Returns true if the SDU control sequence for Segmentation and
  // Reassembly is 01b, false otherwise.
  static bool is_starting_sdu(const L2capSdu& sdu);

  // Returns true if the SDU control sequence for Segmentation and
  // Reasembly is 10b, false otherwise.
  static bool is_ending_sdu(const L2capSdu& sdu);

 private:
  // This is the SDU packet in bytes.
  std::vector<uint8_t> sdu_data_;

  // Table for precalculated lfsr values.
  static const uint16_t lfsr_table_[256];

  uint16_t convert_from_little_endian(const unsigned int starting_index) const;

};  // L2capSdu

}  // namespace test_vendor_lib
