//
//
// Copyright 2018 gRPC authors.
//
// 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 <grpc/support/port_platform.h>

#include "src/core/client_channel/client_channel_channelz.h"

#include "absl/strings/str_cat.h"

#include <grpc/support/json.h>

#include "src/core/lib/transport/connectivity_state.h"

// IWYU pragma: no_include <type_traits>

namespace grpc_core {
namespace channelz {

SubchannelNode::SubchannelNode(std::string target_address,
                               size_t channel_tracer_max_nodes)
    : BaseNode(EntityType::kSubchannel, target_address),
      target_(std::move(target_address)),
      trace_(channel_tracer_max_nodes) {}

SubchannelNode::~SubchannelNode() {}

void SubchannelNode::UpdateConnectivityState(grpc_connectivity_state state) {
  connectivity_state_.store(state, std::memory_order_relaxed);
}

void SubchannelNode::SetChildSocket(RefCountedPtr<SocketNode> socket) {
  MutexLock lock(&socket_mu_);
  child_socket_ = std::move(socket);
}

Json SubchannelNode::RenderJson() {
  // Create and fill the data child.
  grpc_connectivity_state state =
      connectivity_state_.load(std::memory_order_relaxed);
  Json::Object data = {
      {"state", Json::FromObject({
                    {"state", Json::FromString(ConnectivityStateName(state))},
                })},
      {"target", Json::FromString(target_)},
  };
  // Fill in the channel trace if applicable
  Json trace_json = trace_.RenderJson();
  if (trace_json.type() != Json::Type::kNull) {
    data["trace"] = std::move(trace_json);
  }
  // Ask CallCountingHelper to populate call count data.
  call_counter_.PopulateCallCounts(&data);
  // Construct top-level object.
  Json::Object object{
      {"ref", Json::FromObject({
                  {"subchannelId", Json::FromString(absl::StrCat(uuid()))},
              })},
      {"data", Json::FromObject(std::move(data))},
  };
  // Populate the child socket.
  RefCountedPtr<SocketNode> child_socket;
  {
    MutexLock lock(&socket_mu_);
    child_socket = child_socket_;
  }
  if (child_socket != nullptr && child_socket->uuid() != 0) {
    object["socketRef"] = Json::FromArray({
        Json::FromObject({
            {"socketId", Json::FromString(absl::StrCat(child_socket->uuid()))},
            {"name", Json::FromString(child_socket->name())},
        }),
    });
  }
  return Json::FromObject(object);
}

}  // namespace channelz
}  // namespace grpc_core
