// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2014 Adam Crume <adamcrume@gmail.com>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

// This code assumes that IO IDs and timestamps are related monotonically.
// In other words, (a.id < b.id) == (a.timestamp < b.timestamp) for all IOs a and b.

#include "ios.hpp"
#include "rbd_replay/ActionTypes.h"

using namespace std;
using namespace rbd_replay;

namespace {

bool compare_dependencies_by_start_time(const action::Dependency &lhs,
                                        const action::Dependency &rhs) {
  return lhs.time_delta < rhs.time_delta;
}

action::Dependencies convert_dependencies(uint64_t start_time,
                                          const io_set_t &deps) {
  action::Dependencies action_deps;
  action_deps.reserve(deps.size());
  for (io_set_t::const_iterator it = deps.begin(); it != deps.end(); ++it) {
    boost::shared_ptr<IO> io = *it;
    uint64_t time_delta = 0;
    if (start_time >= io->start_time()) {
      time_delta = start_time - io->start_time();
    }
    action_deps.push_back(action::Dependency(io->ionum(), time_delta));
  }
  std::sort(action_deps.begin(), action_deps.end(),
            compare_dependencies_by_start_time);
  return action_deps;
}

} // anonymous namespace

void IO::write_debug_base(ostream& out, const string &type) const {
  out << m_ionum << ": " << m_start_time / 1000000.0 << ": " << type << ", thread = " << m_thread_id << ", deps = {";
  bool first = true;
  for (io_set_t::iterator itr = m_dependencies.begin(), end = m_dependencies.end(); itr != end; ++itr) {
    if (first) {
      first = false;
    } else {
      out << ", ";
    }
    out << (*itr)->m_ionum << ": " << m_start_time - (*itr)->m_start_time;
  }
  out << "}";
}


ostream& operator<<(ostream &out, const IO::ptr &io) {
  io->write_debug(out);
  return out;
}

void StartThreadIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::StartThreadAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()))));
  encode(action, bl);
}

void StartThreadIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "start thread");
}

void StopThreadIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::StopThreadAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()))));
  encode(action, bl);
}

void StopThreadIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "stop thread");
}

void ReadIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::ReadAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_offset, m_length)));
  encode(action, bl);
}

void ReadIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "read");
  out << ", imagectx=" << m_imagectx << ", offset=" << m_offset << ", length=" << m_length << "]";
}

void WriteIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::WriteAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_offset, m_length)));
  encode(action, bl);
}

void WriteIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "write");
  out << ", imagectx=" << m_imagectx << ", offset=" << m_offset << ", length=" << m_length << "]";
}

void DiscardIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::DiscardAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_offset, m_length)));
  encode(action, bl);
}

void DiscardIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "discard");
  out << ", imagectx=" << m_imagectx << ", offset=" << m_offset << ", length=" << m_length << "]";
}

void AioReadIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::AioReadAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_offset, m_length)));
  encode(action, bl);
}

void AioReadIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "aio read");
  out << ", imagectx=" << m_imagectx << ", offset=" << m_offset << ", length=" << m_length << "]";
}

void AioWriteIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::AioWriteAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_offset, m_length)));
  encode(action, bl);
}

void AioWriteIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "aio write");
  out << ", imagectx=" << m_imagectx << ", offset=" << m_offset << ", length=" << m_length << "]";
}

void AioDiscardIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::AioDiscardAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_offset, m_length)));
  encode(action, bl);
}

void AioDiscardIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "aio discard");
  out << ", imagectx=" << m_imagectx << ", offset=" << m_offset << ", length=" << m_length << "]";
}

void OpenImageIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::OpenImageAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_name, m_snap_name, m_readonly)));
  encode(action, bl);
}

void OpenImageIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "open image");
  out << ", imagectx=" << m_imagectx << ", name='" << m_name << "', snap_name='" << m_snap_name << "', readonly=" << m_readonly;
}

void CloseImageIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::CloseImageAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx)));
  encode(action, bl);
}

void CloseImageIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "close image");
  out << ", imagectx=" << m_imagectx;
}

void AioOpenImageIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::AioOpenImageAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx, m_name, m_snap_name, m_readonly)));
  encode(action, bl);
}

void AioOpenImageIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "aio open image");
  out << ", imagectx=" << m_imagectx << ", name='" << m_name << "', snap_name='" << m_snap_name << "', readonly=" << m_readonly;
}

void AioCloseImageIO::encode(bufferlist &bl) const {
  using ceph::encode;
  action::Action action((action::AioCloseImageAction(
    ionum(), thread_id(), convert_dependencies(start_time(), dependencies()),
    m_imagectx)));
  encode(action, bl);
}

void AioCloseImageIO::write_debug(std::ostream& out) const {
  write_debug_base(out, "aio close image");
  out << ", imagectx=" << m_imagectx;
}
