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

#include "jdwp/jdwp.h"

#include <inttypes.h>

#include "android-base/stringprintf.h"

#include "jdwp/jdwp_priv.h"

namespace art {

namespace JDWP {

Request::Request(const uint8_t* bytes, uint32_t available) : p_(bytes) {
  byte_count_ = Read4BE();
  end_ =  bytes + byte_count_;
  CHECK_LE(byte_count_, available);

  id_ = Read4BE();
  int8_t flags = Read1();
  if ((flags & kJDWPFlagReply) != 0) {
    LOG(FATAL) << "reply?!";
  }

  command_set_ = Read1();
  command_ = Read1();
}

Request::~Request() {
}

void Request::CheckConsumed() {
  if (p_ < end_) {
    CHECK(p_ == end_) << "read too few bytes: " << (end_ - p_);
  } else if (p_ > end_) {
    CHECK(p_ == end_) << "read too many bytes: " << (p_ - end_);
  }
}

std::string Request::ReadUtf8String() {
  uint32_t length = Read4BE();
  std::string s;
  s.resize(length);
  memcpy(&s[0], p_, length);
  p_ += length;
  VLOG(jdwp) << "    string \"" << s << "\"";
  return s;
}

// Helper function: read a variable-width value from the input buffer.
uint64_t Request::ReadValue(size_t width) {
  uint64_t value = -1;
  switch (width) {
    case 1: value = Read1(); break;
    case 2: value = Read2BE(); break;
    case 4: value = Read4BE(); break;
    case 8: value = Read8BE(); break;
    default: LOG(FATAL) << width; break;
  }
  return value;
}

int32_t Request::ReadSigned32(const char* what) {
  int32_t value = static_cast<int32_t>(Read4BE());
  VLOG(jdwp) << "    " << what << " " << value;
  return value;
}

uint32_t Request::ReadUnsigned32(const char* what) {
  uint32_t value = Read4BE();
  VLOG(jdwp) << "    " << what << " " << value;
  return value;
}

FieldId Request::ReadFieldId() {
  FieldId id = Read8BE();
  VLOG(jdwp) << "    field id " << DescribeField(id);
  return id;
}

MethodId Request::ReadMethodId() {
  MethodId id = Read8BE();
  VLOG(jdwp) << "    method id " << DescribeMethod(id);
  return id;
}

ObjectId Request::ReadObjectId(const char* specific_kind) {
  ObjectId id = Read8BE();
  VLOG(jdwp) << android::base::StringPrintf("    %s id %#" PRIx64, specific_kind, id);
  return id;
}

ObjectId Request::ReadArrayId() {
  return ReadObjectId("array");
}

ObjectId Request::ReadObjectId() {
  return ReadObjectId("object");
}

ObjectId Request::ReadThreadId() {
  return ReadObjectId("thread");
}

ObjectId Request::ReadThreadGroupId() {
  return ReadObjectId("thread group");
}

RefTypeId Request::ReadRefTypeId() {
  RefTypeId id = Read8BE();
  VLOG(jdwp) << "    ref type id " << DescribeRefTypeId(id);
  return id;
}

FrameId Request::ReadFrameId() {
  FrameId id = Read8BE();
  VLOG(jdwp) << "    frame id " << id;
  return id;
}

JdwpTag Request::ReadTag() {
  return ReadEnum1<JdwpTag>("tag");
}

JdwpTypeTag Request::ReadTypeTag() {
  return ReadEnum1<JdwpTypeTag>("type tag");
}

JdwpLocation Request::ReadLocation() {
  JdwpLocation location;
  memset(&location, 0, sizeof(location));  // Allows memcmp(3) later.
  location.type_tag = ReadTypeTag();
  location.class_id = ReadObjectId("class");
  location.method_id = ReadMethodId();
  location.dex_pc = Read8BE();
  VLOG(jdwp) << "    location " << location;
  return location;
}

JdwpModKind Request::ReadModKind() {
  return ReadEnum1<JdwpModKind>("mod kind");
}

uint8_t Request::Read1() {
  return *p_++;
}

uint16_t Request::Read2BE() {
  uint16_t result = p_[0] << 8 | p_[1];
  p_ += 2;
  return result;
}

uint32_t Request::Read4BE() {
  uint32_t result = p_[0] << 24;
  result |= p_[1] << 16;
  result |= p_[2] << 8;
  result |= p_[3];
  p_ += 4;
  return result;
}

uint64_t Request::Read8BE() {
  uint64_t high = Read4BE();
  uint64_t low = Read4BE();
  return (high << 32) | low;
}

}  // namespace JDWP

}  // namespace art
