/*
 * Copyright (C) 2019 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 <chrono>
#include <fstream>

#include <fmt/format.h>
#include "absl/log/check.h"
#include "absl/log/log.h"

#include "cuttlefish/common/libs/fs/shared_fd.h"
#include "cuttlefish/common/libs/fs/shared_select.h"
#include "cuttlefish/flag_parser/flag.h"
#include "cuttlefish/flag_parser/gflags_compat.h"
#include "cuttlefish/flag_parser/shared_fd_flag.h"
#include "cuttlefish/host/libs/config/logging.h"

namespace cuttlefish {

static uint num_tombstones_in_last_second = 0;
static std::string last_tombstone_name = "";

static std::string next_tombstone_path(const std::string& dir) {
  auto in_time = std::chrono::system_clock::now();
  auto retval = fmt::format("{}/tombstone_{:%Y-%m-%d-%H%M%S}", dir, in_time);

  // Gives tombstones unique names
  if(retval == last_tombstone_name) {
    num_tombstones_in_last_second++;
    retval += "_" + std::to_string(num_tombstones_in_last_second);
  } else {
    last_tombstone_name = retval;
    num_tombstones_in_last_second = 0;
  }

  VLOG(0) << "Creating " << retval;
  return retval;
}

static constexpr size_t CHUNK_RECV_MAX_LEN = 1024;
static constexpr size_t TIMEOUT_SEC = 3;

int TombstoneReceiverMain(int argc, char** argv) {
  DefaultSubprocessLogging(argv);

  std::vector<Flag> flags;

  std::string tombstone_dir;
  flags.emplace_back(GflagsCompatFlag("tombstone_dir", tombstone_dir)
                         .Help("directory to write out tombstones in"));

  SharedFD server_fd;
  flags.emplace_back(
      SharedFDFlag("server_fd", server_fd)
          .Help("File descriptor to an already created vsock server"));

  flags.emplace_back(HelpFlag(flags));

  std::vector<std::string> args(argv + 1, argv + argc);  // Skip argv[0]
  Result<void> parse_res =
      ConsumeFlags(flags, args, {.fail_on_unexpected_argument = true});
  CHECK(parse_res.ok()) << "Could not process command line flags. "
                        << parse_res.error();

  CHECK(server_fd->IsOpen()) << "Did not receive a server fd";

  VLOG(0) << "Host is starting server on port " << server_fd->VsockServerPort();

  // Server loop
  while (true) {
    auto conn = SharedFD::Accept(*server_fd);
    std::ofstream file(next_tombstone_path(tombstone_dir),
                       std::ofstream::out | std::ofstream::binary);
    auto acc = 0;
    auto cnt = 0;

    SharedFDSet read_set;
    read_set.Set(conn);

    SharedFDSet error_set;
    error_set.Set(conn);
    while (file.is_open()) {
      struct timeval timeout = {TIMEOUT_SEC, 0};
      auto val = Select(&read_set, nullptr, &error_set, &timeout);
      if (val == 0) {
        VLOG(0) << "timeout";
        break;
      }
      if (read_set.IsSet(conn)) {
        char buff[CHUNK_RECV_MAX_LEN];
        auto bytes_read = conn->Recv(buff, sizeof(buff), 0);
        acc += bytes_read;
        if (bytes_read <= 0) {
          // reset the other side if it's still connected
          break;
        } else {
          file.write(buff, bytes_read);
          cnt++;
        }
      }

      if (error_set.IsSet(conn)) {
        VLOG(0) << "error";
        break;
      }
    }
    VLOG(0) << "done: " << acc << " bytes via " << cnt;
  }

  return 0;
}

}  // namespace cuttlefish

int main(int argc, char** argv) {
  return cuttlefish::TombstoneReceiverMain(argc, argv);
}
