text:
// TnzCore includes
#include "tstream.h"

// TnzBase includes
#include "tdoublekeyframe.h"
#include "tparamchange.h"

// TnzExt includes
#include "ext/plasticskeleton.h"
#include "ext/plasticdeformerstorage.h"

// tcg includes
#include "tcg/tcg_misc.h"

// STL includes
#include <memory>
#include <set>
#include <map>

// Boost includes
#include <boost/bimap.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/iterator/transform_iterator.hpp>

#include "ext/plasticskeletondeformation.h"

PERSIST_IDENTIFIER(PlasticSkeletonVertexDeformation,
                   "PlasticSkeletonVertexDeformation")
PERSIST_IDENTIFIER(PlasticSkeletonDeformation, "PlasticSkeletonDeformation")

DEFINE_CLASS_CODE(PlasticSkeletonDeformation, 121)

//**************************************************************************************
//    Boost-related  stuff
//**************************************************************************************

using namespace boost::multi_index;

namespace {

typedef boost::bimap<int, PlasticSkeletonP> SkeletonSet;

//======================================================================

struct VDKey {
  QString m_name;
  int m_hookNumber;

  mutable std::map<int, int>
      m_vIndices;  //!< Skeleton index to Vertex index map
  mutable SkVD m_vd;
};

//----------------------------------------------------------------------

typedef boost::multi_index_container<
    VDKey,
    indexed_by<

        ordered_unique<tag<QString>, member<VDKey, QString, &VDKey::m_name>>,
        ordered_unique<tag<int>, member<VDKey, int, &VDKey::m_hookNumber>>

        >>
    SkVDSet;

//----------------------------------------------------------------------

typedef SkVDSet::index<int>::type SkVDByHookNumber;

}  // namespace

//**************************************************************************************
//    Local  namespace
//**************************************************************************************

namespace {

static const char *parNames[SkVD::PARAMS_COUNT] = {"Angle", "Distance", "SO"};
static const char *parMeasures[SkVD::PARAMS_COUNT] = {"angle", "fxLength", ""};

//------------------------------------------------------------------

// Extract the angle between edges vParent->v and vGrandParent->vParent
double buildAngle(const PlasticSkeleton &skeleton, int v) {
  const PlasticSkeletonVertex &vx = skeleton.vertex(v);
  int vParent                     = vx.parent();

  assert(vx.parent() >= 0);

  const PlasticSkeletonVertex &vxParent = skeleton.vertex(vParent);
  int vGrandParent                      = vxParent.parent();

  // Build reference orientation
  TPointD dir(1.0, 0.0);  // Standard horizontal axis if no grandParent
  if (vGrandParent >= 0) {
    // Relative orientation
    const PlasticSkeletonVertex &vxGrandParent = skeleton.vertex(vGrandParent);
    dir = vxParent.P() - vxGrandParent.P();
  }

  return tcg::point_ops::angle(dir, vx.P() - vxParent.P()) * M_180_PI;
}

}  // namespace

//**************************************************************************************
//    PlasticSkeletonVertex  implementation
//**************************************************************************************

SkVD::Keyframe SkVD::getKeyframe(double frame) const {
  Keyframe kf;
  for (int p          = 0; p < PARAMS_COUNT; ++p)
    kf.m_keyframes[p] = m_params[p]->getKeyframeAt(frame);

  return kf;
}

//------------------------------------------------------------------

void SkVD::setKeyframe(double frame) {
  for (int p = 0; p < PARAMS_COUNT; ++p)
    m_params[p]->setKeyframe(m_params[p]->getKeyframeAt(frame));
}

//------------------------------------------------------------------

bool SkVD::setKeyframe(const SkVD::Keyframe &values) {
  bool keyWasSet = false;
  for (int p = 0; p < PARAMS_COUNT; ++p)
    if (values.m_keyframes[p].m_isKeyframe) {
      m_params[p]->setKeyframe(values.m_keyframes[p]);
      keyWasSet = true;
    }

  return keyWasSet;
}

//------------------------------------------------------------------

bool SkVD::setKeyframe(const SkVD::Keyframe &values, double frame,
                       double easeIn, double easeOut) {
  bool keyWasSet = false;

  for (int p = 0; p < PARAMS_COUNT; ++p)
    if (values.m_keyframes[p].m_isKeyframe) {
      TDoubleKeyframe kf(values.m_keyframes[p]);
      kf.m_frame = frame;

      if (easeIn >= 0.0) kf.m_speedIn   = TPointD(-easeIn, kf.m_speedIn.y);
      if (easeOut >= 0.0) kf.m_speedOut = TPointD(easeOut, kf.m_speedOut.y);

      m_params[p]->setKeyframe(kf);
      keyWasSet = true;
    }

  return keyWasSet;
}

//------------------------------------------------------------------

bool SkVD::isKeyframe(double frame) const {
  for (int p = 0; p < PARAMS_COUNT; ++p)
    if (m_params[p]->isKeyframe(frame)) return true;

  return false;
}

//------------------------------------------------------------------

bool SkVD::isFullKeyframe(double frame) const {
  for (int p = 0; p < PARAMS_COUNT; ++p)
    if (!m_params[p]->isKeyframe(frame)) return false;

  return true;
}

//------------------------------------------------------------------

void SkVD::deleteKeyframe(double frame) {
  for (int p = 0; p < PARAMS_COUNT; ++p) m_params[p]->deleteKeyframe(frame);
}

//------------------------------------------------------------------

void SkVD::saveData(TOStream &os) {
  for (int p = 0; p < PARAMS_COUNT; ++p)
    if (!m_params[p]->isDefault()) os.child(::parNames[p]) << *m_params[p];
}

//------------------------------------------------------------------

void SkVD::loadData(TIStream &is) {
  std::string tagName;

  while (is.matchTag(tagName)) {
    int p;
    for (p = 0; p < PARAMS_COUNT; ++p) {
      if (tagName == parNames[p]) {
        is >> *m_params[p], is.matchEndTag();
        break;
      }
    }

    if (p >= PARAMS_COUNT) is.skipCurrentTag();
  }
}

//**************************************************************************************
//    PlasticSkeletonDeformation::Imp  definition
//**************************************************************************************

class PlasticSkeletonDeformation::Imp final : public TParamObserver {
public:
  PlasticSkeletonDeformation *m_back;  //!< Back-pointer to the interface class

  SkeletonSet m_skeletons;  //!< Skeletons owned by the deformation
  SkVDSet m_vds;            //!< Container of vertex deformations

  TDoubleParamP m_skelIdsParam;  //!< Curve of skeleton ids by xsheet frame

  std::set<TParamObserver *>
      m_observers;  //!< Set of the deformation's observers

  TSyntax::Grammar
      *m_grammar;  //!< The params' grammar. Weird though - it's a VERY
                   //!< occult requirement to TDoubleParams...

  // NOTE: There \a is a deformation even for a skeleton's root node. This is
  // now required due to the
  // onwership of \a multiple skeletons at once. However, its angle and distance
  // params will be unused.

public:
  Imp(PlasticSkeletonDeformation *back);
  ~Imp();

  Imp(PlasticSkeletonDeformation *back, const Imp &other);
  Imp &operator=(const Imp &other);

  PlasticSkeleton &skeleton(int skelId) const;
  SkVD &vertexDeformation(const QString &name) const;

  void attach(int skeletonId, PlasticSkeleton *skeleton);
  void detach(int skeletonId);

  void attachVertex(const QString &name, int skelId, int v);
  void detachVertex(const QString &name, int skelId, int v);
  void rebindVertex(const QString &name, int skelId, const QString &newName);

  void touchParams(SkVD &vd);

  //! Applies stored vertex deformations to the skeleton branch starting at v
  void updateBranchPositions(const PlasticSkeleton &originalSkeleton,
                             PlasticSkeleton &deformedSkeleton, double frame,
                             int v);

  void onChange(
      const TParamChange &change) override;  // Passes param notifications to
                                             // external observers

private:
  // Not directly copy-constructible
  Imp(const Imp &other);
};

//------------------------------------------------------------------

PlasticSkeletonDeformation::Imp::Imp(PlasticSkeletonDeformation *back)
    : m_back(back), m_skelIdsParam(1.0), m_grammar() {
  m_skelIdsParam->setName("Skeleton Id");
  m_skelIdsParam->addObserver(this);
}

//------------------------------------------------------------------

PlasticSkeletonDeformation::Imp::~Imp() {
  m_skelIdsParam->removeObserver(this);

  SkVDSet::iterator dt, dEnd(m_vds.end());
  for (dt = m_vds.begin(); dt != dEnd; ++dt)
    for (int p = 0; p < SkVD::PARAMS_COUNT; ++p)
      dt->m_vd.m_params[p]->removeObserver(this);
}

//------------------------------------------------------------------

PlasticSkeletonDeformation::Imp::Imp(PlasticSkeletonDeformation *back,
                                     const Imp &other)
    : m_back(back), m_skelIdsParam(other.m_skelIdsParam->clone()), m_grammar() {
  m_skelIdsParam->setGrammar(m_grammar);
  m_skelIdsParam->addObserver(this);

  // Clone the skeletons
  SkeletonSet::const_iterator st, sEnd(other.m_skeletons.end());
  for (st = other.m_skeletons.begin(); st != sEnd; ++st)
    m_skeletons.insert(SkeletonSet::value_type(
        st->get_left(), new PlasticSkeleton(*st->get_right())));

  // Clone each parameters curve
  SkVD vd;

  SkVDSet::const_iterator dt, dEnd(other.m_vds.end());
  for (dt = other.m_vds.begin(); dt != dEnd; ++dt) {
    VDKey vdKey = {dt->m_name, dt->m_hookNumber, dt->m_vIndices};

    for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) {
      TDoubleParamP &param = vdKey.m_vd.m_params[p];

      param = TDoubleParamP(dt->m_vd.m_params[p]->clone());

      param->setGrammar(m_grammar);
      param->addObserver(this);
    }

    m_vds.insert(vdKey);
  }
}

//------------------------------------------------------------------

PlasticSkeletonDeformation::Imp &PlasticSkeletonDeformation::Imp::operator=(
    const Imp &other) {
  *m_skelIdsParam = *other.m_skelIdsParam;
  m_skelIdsParam->setGrammar(m_grammar);

  // Take in all curves whose name matches one of the stored ones

  // Traverse known curves
  SkVDSet::iterator dt, dEnd(m_vds.end());
  SkVDSet::const_iterator st, sEnd(other.m_vds.end());

  for (dt = m_vds.begin(); dt != dEnd; ++dt) {
    // Search a corresponding curve in the input ones
    st = other.m_vds.find(dt->m_name);
    if (st != sEnd)
      for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) {
        TDoubleParam &param = *dt->m_vd.m_params[p];

        param = *st->m_vd.m_params[p];
        param.setGrammar(m_grammar);
      }
  }

  return *this;
}

//------------------------------------------------------------------

PlasticSkeleton &PlasticSkeletonDeformation::Imp::skeleton(int skelId) const {
  SkeletonSet::left_map::const_iterator st(m_skeletons.left.find(skelId));
  assert(st != m_skeletons.left.end());

  return *st->second;
}

//------------------------------------------------------------------

SkVD &PlasticSkeletonDeformation::Imp::vertexDeformation(
    const QString &name) const {
  SkVDSet::const_iterator vdt(m_vds.find(name));
  assert(vdt != m_vds.end());

  return vdt->m_vd;
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::attach(int skeletonId,
                                             PlasticSkeleton *skeleton) {
  assert(skeleton);

  // Store the skeleton - acquires shared ownership
  assert(m_skeletons.left.find(skeletonId) == m_skeletons.left.end());

  m_skeletons.insert(SkeletonSet::value_type(skeletonId, skeleton));

  // Deal with vertex deformations and parameter defaults
  tcg::list<PlasticSkeleton::vertex_type> &vertices = skeleton->vertices();
  if (!vertices.empty()) {
    tcg::list<PlasticSkeletonVertex>::iterator vt, vEnd(vertices.end());
    for (vt = vertices.begin(); vt != vEnd; ++vt)
      attachVertex(vt->name(), skeletonId, vt.index());
  }
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::detach(int skeletonId) {
  // First, detach all vertices
  tcg::list<PlasticSkeleton::vertex_type> &vertices =
      skeleton(skeletonId).vertices();
  if (!vertices.empty()) {
    tcg::list<PlasticSkeletonVertex>::iterator vt, vEnd(vertices.end());
    for (vt = vertices.begin(); vt != vEnd; ++vt)
      detachVertex(vt->name(), skeletonId, vt.index());
  }

  // Then, release the skeleton itself
  m_skeletons.left.erase(skeletonId);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::attachVertex(const QString &name,
                                                   int skelId, int v) {
  struct locals {
    static int newHookNumber(Imp *imp) {
      int h = 1;

      SkVDByHookNumber::iterator vdt, vdEnd(imp->m_vds.get<int>().end());
      for (vdt = imp->m_vds.get<int>().begin();
           vdt != vdEnd && vdt->m_hookNumber == h; ++vdt, ++h)
        ;

      return h;
    }

  };  // locals

  // Insert a new VD if necessary
  SkVDSet::iterator vdt(m_vds.find(name));
  if (vdt == m_vds.end()) {
    VDKey vdKey = {name, locals::newHookNumber(this)};
    touchParams(vdKey.m_vd);

    vdt = m_vds.insert(vdKey).first;
  }

  // Register (skelId, v) on the vd
  vdt->m_vIndices.insert(std::make_pair(skelId, v));
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::detachVertex(const QString &name,
                                                   int skelId, int v) {
  SkVDSet::iterator vdt = m_vds.find(name);
  assert(vdt != m_vds.end());

  // Unregister skelId
  int count = vdt->m_vIndices.erase(skelId);
  assert(count > 0);

  if (vdt->m_vIndices.empty()) {
    // De-register as vdt's observer, and release it from stored vds
    SkVD &vd = vdt->m_vd;

    for (int p = 0; p < SkVD::PARAMS_COUNT; ++p)
      vd.m_params[p]->removeObserver(this);

    m_vds.erase(vdt);
  }
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::rebindVertex(const QString &name,
                                                   int skelId,
                                                   const QString &newName) {
  if (name == newName) return;

  SkVDSet::iterator oldVdt = m_vds.find(name);
  if (oldVdt == m_vds.end()) return;  // We get here when creating a new vertex

  std::map<int, int>::iterator vit = oldVdt->m_vIndices.find(skelId);
  assert(vit != oldVdt->m_vIndices.end());

  int v = vit->second;

  SkVDSet::iterator newVdt = m_vds.find(newName);
  if (newVdt != m_vds.end()) {
    detachVertex(name, skelId, v);
    attachVertex(newName, skelId, v);
  } else {
    // Creating a new vd entry
    if (oldVdt->m_vIndices.size() == 1) {
      // The old entry should be removed - it is actually *purely* renamed
      VDKey vdKey(*oldVdt);
      vdKey.m_name = newName;

      m_vds.erase(name);
      m_vds.insert(vdKey);
    } else {
      // The old entry remains - and data must be copied from there
      detachVertex(name, skelId, v);
      attachVertex(newName, skelId, v);

      newVdt = m_vds.find(newName);  // Fetch the newly created vd

      // Copy the existing vd into the new one
      SkVD &oldVd = oldVdt->m_vd, &newVd = newVdt->m_vd;

      int p, pCount = SkVD::PARAMS_COUNT;
      for (p = 0; p != pCount; ++p) *newVd.m_params[p] = *oldVd.m_params[p];
    }
  }
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::touchParams(SkVD &vd) {
  for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) {
    if (vd.m_params[p]) continue;

    TDoubleParam *param = new TDoubleParam;
    param->setName(parNames[p]);
    param->setMeasureName(parMeasures[p]);
    param->setGrammar(m_grammar);

    vd.m_params[p] = param;

    param->addObserver(this);
  }
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::onChange(const TParamChange &change) {
  // Since the deformation was changed, any associated deformer
  // must be invalidated (at the animation-deform level only)
  PlasticDeformerStorage::instance()->invalidateDeformation(
      m_back, PlasticDeformerStorage::NONE);

  // Propagate notification to this object's observers
  std::set<TParamObserver *>::iterator ot, oEnd(m_observers.end());
  for (ot = m_observers.begin(); ot != oEnd; ++ot) (*ot)->onChange(change);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::Imp::updateBranchPositions(
    const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton,
    double frame, int v) {
  struct locals {
    static void buildParentDirection(const PlasticSkeleton &skel, int v,
                                     TPointD &dir) {
      assert(v >= 0);

      const PlasticSkeletonVertex &vx = skel.vertex(v);

      int vParent = vx.parent();
      if (vParent < 0) return;  // dir remains as passed

      const TPointD &dir_ =
          tcg::point_ops::direction(skel.vertex(vParent).P(), vx.P(), 1e-4);

      if (dir_ != tcg::point_ops::NaP<TPointD>())
        dir = dir_;
      else
        buildParentDirection(skel, vParent, dir);
    }
  };  // locals

  PlasticSkeletonVertex &dvx = deformedSkeleton.vertex(v);
  int vParent                = dvx.parent();
  if (vParent >= 0) {
    // Rebuild vertex position
    const TPointD &ovxPos       = originalSkeleton.vertex(v).P();
    const TPointD &ovxParentPos = originalSkeleton.vertex(vParent).P();

    // Start by getting the polar reference
    TPointD oDir(1.0, 0.0), dDir(1.0, 0.0);

    locals::buildParentDirection(originalSkeleton, vParent, oDir);
    locals::buildParentDirection(deformedSkeleton, vParent, dDir);

    // Now, rebuild vx's position
    const SkVD &vd = m_vds.find(dvx.name())->m_vd;

    double a = tcg::point_ops::angle(oDir, ovxPos - ovxParentPos) * M_180_PI;
    double d = tcg::point_ops::dist(ovxParentPos, ovxPos);

    double aDelta = vd.m_params[SkVD::ANGLE]->getValue(frame);
    double dDelta = vd.m_params[SkVD::DISTANCE]->getValue(frame);

    dvx.P() = deformedSkeleton.vertex(vParent).P() +
              (d + dDelta) * (TRotation(a + aDelta) * dDir);
  }

  // Finally, update children positions
  PlasticSkeleton::vertex_type::edges_iterator et, eEnd(dvx.edgesEnd());
  for (et = dvx.edgesBegin(); et != eEnd; ++et) {
    int vChild = deformedSkeleton.edge(*et).vertex(1);
    if (vChild == v) continue;

    updateBranchPositions(originalSkeleton, deformedSkeleton, frame, vChild);
  }
}

//**************************************************************************************
//    PlasticSkeletonDeformation  implementation
//**************************************************************************************

PlasticSkeletonDeformation::PlasticSkeletonDeformation()
    : m_imp(new Imp(this)) {}

//------------------------------------------------------------------

PlasticSkeletonDeformation::PlasticSkeletonDeformation(
    const PlasticSkeletonDeformation &other)
    : TSmartObject(m_classCode), m_imp(new Imp(this, *other.m_imp)) {
  // Register deformation
  SkeletonSet::iterator st, sEnd(m_imp->m_skeletons.end());
  for (st = m_imp->m_skeletons.begin(); st != sEnd; ++st)
    st->get_right()->addListener(this);
}

//------------------------------------------------------------------

PlasticSkeletonDeformation::~PlasticSkeletonDeformation() {
  // Unregister deformation
  SkeletonSet::iterator st, sEnd(m_imp->m_skeletons.end());
  for (st = m_imp->m_skeletons.begin(); st != sEnd; ++st)
    st->get_right()->removeListener(this);
}

//------------------------------------------------------------------

PlasticSkeletonDeformation &PlasticSkeletonDeformation::operator=(
    const PlasticSkeletonDeformation &other) {
  // The meaning of operator= is DIFFERENT from that implemented in the copy
  // constructor.
  // Skeletons are NOT cloned.

  *m_imp = *other.m_imp;
  return *this;
}

//------------------------------------------------------------------

bool PlasticSkeletonDeformation::empty() const {
  return m_imp->m_skeletons.empty();
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::skeletonsCount() const {
  return m_imp->m_skeletons.size();
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::skeletonIds(skelId_iterator &begin,
                                             skelId_iterator &end) const {
  auto const f = [](const SkeletonSet::left_map::value_type &val) {
    return val.first;
  };

  begin = boost::make_transform_iterator(m_imp->m_skeletons.left.begin(), f);
  end   = boost::make_transform_iterator(m_imp->m_skeletons.left.end(), f);
}

//------------------------------------------------------------------

TDoubleParamP PlasticSkeletonDeformation::skeletonIdsParam() const {
  return m_imp->m_skelIdsParam;
}

//------------------------------------------------------------------

PlasticSkeletonP PlasticSkeletonDeformation::skeleton(double frame) const {
  return skeleton(skeletonId(frame));
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::skeletonId(double frame) const {
  return m_imp->m_skelIdsParam->getValue(frame);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::attach(int skeletonId,
                                        PlasticSkeleton *skeleton) {
  m_imp->attach(skeletonId, skeleton);

  skeleton->addListener(this);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::detach(int skeletonId) {
  SkeletonSet::left_map::iterator st(m_imp->m_skeletons.left.find(skeletonId));
  if (st != m_imp->m_skeletons.left.end()) {
    st->second->removeListener(this);
    m_imp->detach(skeletonId);
  }
}

//------------------------------------------------------------------

PlasticSkeletonP PlasticSkeletonDeformation::skeleton(int skeletonId) const {
  SkeletonSet::left_map::const_iterator st =
      m_imp->m_skeletons.left.find(skeletonId);
  return (st == m_imp->m_skeletons.left.end()) ? PlasticSkeletonP()
                                               : st->second;
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::skeletonId(PlasticSkeleton *skeleton) const {
  SkeletonSet::right_map::const_iterator st =
      m_imp->m_skeletons.right.find(skeleton);
  return (st == m_imp->m_skeletons.right.end())
             ? -(std::numeric_limits<int>::max)()
             : st->second;
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::vertexDeformationsCount() const {
  return m_imp->m_vds.size();
}

//------------------------------------------------------------------

SkVD *PlasticSkeletonDeformation::vertexDeformation(
    const QString &vxName) const {
  SkVDSet::const_iterator vdt = m_imp->m_vds.find(vxName);
  return (vdt == m_imp->m_vds.end()) ? (SkVD *)0 : &vdt->m_vd;
}

//------------------------------------------------------------------

SkVD *PlasticSkeletonDeformation::vertexDeformation(int skelId, int v) const {
  const QString &name = skeleton(skelId)->vertex(v).name();
  return vertexDeformation(name);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::vertexDeformations(vd_iterator &begin,
                                                    vd_iterator &end) const {
  auto const f = [](const VDKey &vdKey) {
    return std::make_pair(&vdKey.m_name, &vdKey.m_vd);
  };

  begin = boost::make_transform_iterator(m_imp->m_vds.begin(), f);
  end   = boost::make_transform_iterator(m_imp->m_vds.end(), f);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::vdSkeletonVertices(const QString &vertexName,
                                                    vx_iterator &begin,
                                                    vx_iterator &end) const {
  auto const f = [](const std::map<int, int>::value_type &val) {
    return std::make_pair(val.first, val.second);
  };

  SkVDSet::const_iterator nt(m_imp->m_vds.find(vertexName));

  if (nt == m_imp->m_vds.end()) {
    begin = vx_iterator();
    end   = vx_iterator();
  } else {
    begin = boost::make_transform_iterator(nt->m_vIndices.begin(), f);
    end   = boost::make_transform_iterator(nt->m_vIndices.end(), f);
  }
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::hookNumber(const QString &name) const {
  SkVDSet::const_iterator nt(m_imp->m_vds.find(name));
  return (nt == m_imp->m_vds.end()) ? -1 : nt->m_hookNumber;
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::hookNumber(int skelId, int v) const {
  const QString &name = skeleton(skelId)->vertex(v).name();
  return hookNumber(name);
}

//------------------------------------------------------------------

QString PlasticSkeletonDeformation::vertexName(int hookNumber) const {
  const SkVDByHookNumber &vds = m_imp->m_vds.get<int>();

  SkVDByHookNumber::const_iterator ht(vds.find(hookNumber));
  return (ht == vds.end()) ? QString() : ht->m_name;
}

//------------------------------------------------------------------

int PlasticSkeletonDeformation::vertexIndex(int hookNumber, int skelId) const {
  const SkVDByHookNumber &vds = m_imp->m_vds.get<int>();

  SkVDByHookNumber::const_iterator ht(vds.find(hookNumber));
  if (ht == vds.end()) return -1;

  std::map<int, int>::const_iterator st(ht->m_vIndices.find(skelId));
  return (st == ht->m_vIndices.end()) ? -1 : st->second;
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::getKeyframeAt(double frame,
                                               SkDKey &keyframe) const {
  keyframe.m_skelIdKeyframe = m_imp->m_skelIdsParam->getKeyframeAt(frame);
  keyframe.m_vertexKeyframes.clear();

  SkVDSet::const_iterator dt, dEnd(m_imp->m_vds.end());
  for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt)
    keyframe.m_vertexKeyframes.insert(
        std::make_pair(dt->m_name, dt->m_vd.getKeyframe(frame)));
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::setKeyframe(double frame) {
  m_imp->m_skelIdsParam->setKeyframe(frame);

  SkVDSet::iterator dt, dEnd(m_imp->m_vds.end());
  for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt) dt->m_vd.setKeyframe(frame);
}

//------------------------------------------------------------------

bool PlasticSkeletonDeformation::setKeyframe(const SkDKey &keyframe) {
  bool keyWasSet = false;

  if (keyframe.m_skelIdKeyframe.m_isKeyframe) {
    m_imp->m_skelIdsParam->setKeyframe(keyframe.m_skelIdKeyframe);
    keyWasSet = true;
  }

  const std::map<QString, SkVD::Keyframe> &vdKeys = keyframe.m_vertexKeyframes;

  // Iterate the keyframe's vertex deformations
  std::map<QString, SkVD::Keyframe>::const_iterator kt, kEnd(vdKeys.end());
  for (kt = vdKeys.begin(); kt != vdKeys.end(); ++kt) {
    // Search for a corresponding vertex deformation among stored ones
    SkVDSet::iterator vdt = m_imp->m_vds.find(kt->first);
    if (vdt != m_imp->m_vds.end()) {
      // Set the corresponding keyframe
      keyWasSet = vdt->m_vd.setKeyframe(kt->second) || keyWasSet;
    }
  }

  return keyWasSet;
}

//------------------------------------------------------------------

bool PlasticSkeletonDeformation::setKeyframe(const SkDKey &keyframe,
                                             double frame, double easeIn,
                                             double easeOut) {
  bool keyWasSet = false;

  if (keyframe.m_skelIdKeyframe.m_isKeyframe) {
    TDoubleKeyframe kf(keyframe.m_skelIdKeyframe);
    kf.m_frame = frame;

    m_imp->m_skelIdsParam->setKeyframe(kf);
    keyWasSet = true;
  }

  const std::map<QString, SkVD::Keyframe> &vdKeys = keyframe.m_vertexKeyframes;

  // Iterate the keyframe's vertex deformations
  std::map<QString, SkVD::Keyframe>::const_iterator kt, kEnd(vdKeys.end());
  for (kt = vdKeys.begin(); kt != vdKeys.end(); ++kt) {
    // Search for a corresponding vertex deformation among stored ones
    SkVDSet::iterator vdt = m_imp->m_vds.find(kt->first);
    if (vdt != m_imp->m_vds.end()) {
      // Set the corresponding keyframe
      keyWasSet = vdt->m_vd.setKeyframe(kt->second, frame, easeIn, easeOut) ||
                  keyWasSet;
    }
  }

  return keyWasSet;
}

//------------------------------------------------------------------

bool PlasticSkeletonDeformation::isKeyframe(double frame) const {
  if (m_imp->m_skelIdsParam->isKeyframe(frame)) return true;

  SkVDSet::const_iterator dt, dEnd(m_imp->m_vds.end());
  for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt)
    if (dt->m_vd.isKeyframe(frame)) return true;

  return false;
}

//------------------------------------------------------------------

bool PlasticSkeletonDeformation::isFullKeyframe(double frame) const {
  if (!m_imp->m_skelIdsParam->isKeyframe(frame)) return false;

  SkVDSet::const_iterator dt, dEnd(m_imp->m_vds.end());
  for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt)
    if (!dt->m_vd.isFullKeyframe(frame)) return false;

  return true;
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::deleteKeyframe(double frame) {
  m_imp->m_skelIdsParam->deleteKeyframe(frame);

  SkVDSet::iterator dt, dEnd(m_imp->m_vds.end());
  for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt)
    dt->m_vd.deleteKeyframe(frame);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::storeDeformedSkeleton(
    int skelId, double frame, PlasticSkeleton &skeleton) const {
  // Copy the un-deformed skeleton to the output one
  const PlasticSkeletonP &origSkel = this->skeleton(skelId);
  skeleton                         = origSkel ? *origSkel : PlasticSkeleton();

  // Update the skeleton to the specified frame
  if (!skeleton.vertices().empty())
    m_imp->updateBranchPositions(*origSkel, skeleton, frame,
                                 skeleton.vertices().begin().m_idx);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::updatePosition(
    const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton,
    double frame, int v, const TPointD &pos) {
  const PlasticSkeletonVertex &vx = deformedSkeleton.vertex(v);
  int vParent                     = vx.parent();

  const TPointD &vParentPos = deformedSkeleton.vertex(vParent).P();
  const TPointD &vPos       = deformedSkeleton.vertex(v).P();

  SkVD &vd = m_imp->m_vds.find(vx.name())->m_vd;

  // NOTE: The following aDelta calculation should be done as a true difference
  // - this is still ok and spares
  // access to v's grandParent...

  double aDelta = tcg::point_ops::angle(vPos - vParentPos, pos - vParentPos) *
                  M_180_PI,
         dDelta = tcg::point_ops::dist(vParentPos, pos) -
                  tcg::point_ops::dist(vParentPos, vPos),

         a = tcrop(vd.m_params[SkVD::ANGLE]->getValue(frame) + aDelta,
                   vx.m_minAngle, vx.m_maxAngle),
         d = vd.m_params[SkVD::DISTANCE]->getValue(frame) + dDelta;

  vd.m_params[SkVD::ANGLE]->setValue(frame, a);
  vd.m_params[SkVD::DISTANCE]->setValue(frame, d);

  m_imp->updateBranchPositions(originalSkeleton, deformedSkeleton, frame, v);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::updateAngle(
    const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton,
    double frame, int v, const TPointD &pos) {
  const PlasticSkeletonVertex &vx = deformedSkeleton.vertex(v);
  int vParent                     = vx.parent();

  const TPointD &vParentPos = deformedSkeleton.vertex(vParent).P();

  // No need to access the grandParent, we're making the diff against the old vx
  // position

  SkVD &vd = m_imp->m_vds.find(vx.name())->m_vd;

  double aDelta = tcg::point_ops::angle(vx.P() - vParentPos, pos - vParentPos) *
                  M_180_PI,
         a = tcrop(vd.m_params[SkVD::ANGLE]->getValue(frame) + aDelta,
                   vx.m_minAngle, vx.m_maxAngle);

  vd.m_params[SkVD::ANGLE]->setValue(frame, a);

  m_imp->updateBranchPositions(originalSkeleton, deformedSkeleton, frame, v);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::addVertex(PlasticSkeleton *skel, int v) {
  int skelId = skeletonId(skel);
  assert(skelId >= 0);

  m_imp->attachVertex(skel->vertex(v).name(), skelId, v);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::insertVertex(PlasticSkeleton *skel, int v) {
  int skelId = skeletonId(skel);
  assert(skelId >= 0);

  m_imp->attachVertex(skel->vertex(v).name(), skelId, v);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::deleteVertex(PlasticSkeleton *skel, int v) {
  assert(v > 0);  // Root should not be deleted

  int skelId = skeletonId(skel);
  assert(skelId >= 0);

  // Remove the vertex deformation associated with v
  m_imp->detachVertex(skel->vertex(v).name(), skelId, v);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::vertexNameChange(PlasticSkeleton *skel, int v,
                                                  const QString &newName) {
  int skelId = skeletonId(skel);
  assert(skelId >= 0);

  m_imp->rebindVertex(skel->vertex(v).name(), skelId, newName);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::clear(PlasticSkeleton *skel) {
  int skelId = skeletonId(skel);
  assert(skelId >= 0);

  m_imp->detach(skelId);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::addObserver(TParamObserver *observer) {
  m_imp->m_observers.insert(observer);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::removeObserver(TParamObserver *observer) {
  m_imp->m_observers.erase(observer);
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::setGrammar(TSyntax::Grammar *grammar) {
  SkVDSet::iterator vdt, vdEnd(m_imp->m_vds.end());
  for (vdt = m_imp->m_vds.begin(); vdt != vdEnd; ++vdt) {
    SkVD &vd = vdt->m_vd;

    for (int c = 0; c != SkVD::PARAMS_COUNT; ++c)
      vd.m_params[c]->setGrammar(grammar);
  }

  m_imp->m_skelIdsParam->setGrammar(grammar);

  m_imp->m_grammar = grammar;
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::saveData(TOStream &os) {
  // Save skeleton vertex deformations
  os.openChild("VertexDeforms");  // These are saved *before* skeletons
  {                               // ON PURPOSE - in loadData(), we'll
    SkVDSet::iterator vdt,
        vdEnd(m_imp->m_vds.end());  // attach() skeletons to a completely
    for (vdt = m_imp->m_vds.begin(); vdt != vdEnd;
         ++vdt)  // rebuilt set of vertex deformations.
    {
      os.child("Name") << vdt->m_name;
      os.child("Hook") << vdt->m_hookNumber;
      os.child("VD") << vdt->m_vd;
    }
  }
  os.closeChild();

  // Save skeleton ids param
  os.child("SkelIdsParam") << *m_imp->m_skelIdsParam;

  // Save skeletons
  os.openChild("Skeletons");
  {
    SkeletonSet::iterator st, sEnd(m_imp->m_skeletons.end());
    for (st = m_imp->m_skeletons.begin(); st != sEnd; ++st) {
      os.child("SkelId") << st->get_left();
      os.child("Skeleton") << *st->get_right();
    }
  }
  os.closeChild();
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::loadData(TIStream &is) {
  if (is.getVersion() < VersionNumber(1, 21)) {
    loadData_prerelease(is);
    return;
  }

  int skeletonId;
  PlasticSkeleton *skeleton;

  std::string tagName;
  while (is.openChild(tagName)) {
    if (tagName == "VertexDeforms") {
      VDKey vKey;

      while (is.openChild(tagName)) {
        if (tagName == "Name")
          is >> vKey.m_name, is.matchEndTag();
        else if (tagName == "Hook")
          is >> vKey.m_hookNumber, is.matchEndTag();
        else if (tagName == "VD") {
          m_imp->touchParams(vKey.m_vd);
          is >> vKey.m_vd, is.matchEndTag();

          m_imp->m_vds.insert(vKey);

          vKey = VDKey();
        } else
          is.skipCurrentTag();
      }

      is.matchEndTag();
    } else if (tagName == "SkelIdsParam")
      is >> *m_imp->m_skelIdsParam, is.matchEndTag();
    else if (tagName == "Skeletons") {
      while (is.openChild(tagName)) {
        if (tagName == "SkelId")
          is >> skeletonId, is.matchEndTag();
        else if (tagName == "Skeleton") {
          skeleton = new PlasticSkeleton;
          is >> *skeleton, is.matchEndTag();

          attach(skeletonId, skeleton);
          skeletonId = 0, skeleton = 0;
        } else
          is.skipCurrentTag();
      }

      is.matchEndTag();
    } else
      is.skipCurrentTag();
  }
}

//------------------------------------------------------------------

void PlasticSkeletonDeformation::loadData_prerelease(TIStream &is) {
  struct locals {
    static void buildParentDirection(const PlasticSkeleton &skel, int v,
                                     TPointD &dir) {
      assert(v >= 0);

      const PlasticSkeletonVertex &vx = skel.vertex(v);

      int vParent = vx.parent();
      if (vParent < 0) return;  // dir remains as passed

      const TPointD &dir_ =
          tcg::point_ops::direction(skel.vertex(vParent).P(), vx.P(), 1e-4);

      if (dir_ != tcg::point_ops::NaP<TPointD>())
        dir = dir_;
      else
        buildParentDirection(skel, vParent, dir);
    }

    //------------------------------------------------------------------------------

    static void adjust(SkD &sd, int v) {
      PlasticSkeleton &skeleton = *sd.skeleton(1);

      PlasticSkeletonVertex &vx = skeleton.vertex(v);
      int vParent               = vx.parent();
      if (vParent >= 0) {
        // Rebuild vertex position
        const TPointD &vxPos       = skeleton.vertex(v).P();
        const TPointD &vxParentPos = skeleton.vertex(vParent).P();

        // Start by getting the polar reference
        TPointD dir(1.0, 0.0);

        buildParentDirection(skeleton, vParent, dir);

        // Now, rebuild vx's position
        SkVD &vd = sd.m_imp->m_vds.find(vx.name())->m_vd;

        double a = tcg::point_ops::angle(dir, vxPos - vxParentPos) * M_180_PI;
        double d = tcg::point_ops::dist(vxParentPos, vxPos);

        {
          TDoubleParamP param(vd.m_params[SkVD::ANGLE]);
          param->setDefaultValue(0.0);

          int k, kCount = param->getKeyframeCount();
          for (k = 0; k != kCount; ++k) {
            TDoubleKeyframe kf(param->getKeyframe(k));
            kf.m_value -= a;
            param->setKeyframe(k, kf);
          }
        }

        {
          TDoubleParamP param(vd.m_params[SkVD::DISTANCE]);
          param->setDefaultValue(0.0);

          int k, kCount = param->getKeyframeCount();
          for (k = 0; k != kCount; ++k) {
            TDoubleKeyframe kf(param->getKeyframe(k));
            kf.m_value -= d;
            param->setKeyframe(k, kf);
          }
        }
      }

      // Finally, update children positions
      PlasticSkeleton::vertex_type::edges_iterator et, eEnd(vx.edgesEnd());
      for (et = vx.edgesBegin(); et != eEnd; ++et) {
        int vChild = skeleton.edge(*et).vertex(1);
        if (vChild == v) continue;

        adjust(sd, vChild);
      }
    }
  };  // locals

  PlasticSkeletonP skeleton(new PlasticSkeleton);

  std::string tagName;
  while (is.openChild(tagName)) {
    if (tagName == "Skeleton")
      is >> *skeleton, is.matchEndTag();
    else if (tagName == "VertexDeforms") {
      while (is.openChild(tagName)) {
        if (tagName == "VD") {
          VDKey vKey;
          m_imp->touchParams(vKey.m_vd);

          is >> vKey.m_name, is >> vKey.m_vd;
          is.closeChild();

          // Rebuild vKey's data from skeleton
          int v, vCount = skeleton->verticesCount();
          for (v = 0; v != vCount; ++v)
            if (skeleton->vertex(v).name() == vKey.m_name) break;

          assert(v < vCount);
          vKey.m_hookNumber = skeleton->vertex(v).number();

          m_imp->m_vds.insert(vKey);
        } else
          is.skipCurrentTag();
      }

      is.matchEndTag();
    } else
      is.skipCurrentTag();
  }

  attach(1, skeleton.getPointer());

  // SkVD params are now intended as deltas. Adjusting...
  locals::adjust(*this, 0);
}

max_stars_repo_name: rozhuk-im/opentoonz

max_issues_repo_name: rozhuk-im/opentoonz

max_forks_repo_name: rozhuk-im/opentoonz


text:
// Copyright 2022 PingCAP, Ltd.
//
// 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 <Common/StringUtils/StringUtils.h>
#include <Flash/LogSearch.h>
#include <Poco/InflatingStream.h>

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>

namespace DB
{
using ::diagnosticspb::LogLevel;
using ::diagnosticspb::LogMessage;

static time_t fast_mktime(struct tm * tm)
{
    thread_local struct tm cache = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr};
    thread_local time_t time_cache = 0;
    time_t result;
    time_t hmsarg;

    hmsarg = 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;

    if (cache.tm_mday == tm->tm_mday && cache.tm_mon == tm->tm_mon && cache.tm_year == tm->tm_year)
    {
        result = time_cache + hmsarg;
        tm->tm_isdst = cache.tm_isdst;
    }
    else
    {
        cache.tm_mday = tm->tm_mday;
        cache.tm_mon = tm->tm_mon;
        cache.tm_year = tm->tm_year;
        time_cache = mktime(&cache);
        tm->tm_isdst = cache.tm_isdst;
        result = (-1 == time_cache) ? -1 : time_cache + hmsarg;
    }

    return result;
}

std::optional<LogMessage> LogIterator::next()
{
    for (;;)
    {
        LogEntry entry;
        std::optional<Error> maybe_err = readLog(entry);
        if (maybe_err.has_value())
        {
            Error err = maybe_err.value();
            if (!err_info.has_value())
            {
                switch (err.tp)
                {
                case Error::Type::UNEXPECTED_LOG_HEAD:
                    err_info = std::make_pair(cur_lineno, err.tp);
                    break;
                default:
                    break;
                }
            }
            if (err.tp != Error::Type::EOI && err.tp != Error::Type::UNKNOWN)
            {
                continue;
            }
            else
            {
                return {};
            }
        }

        LogLevel level;
        switch (entry.level)
        {
        case LogEntry::Level::Trace:
            level = LogLevel::Trace;
            break;
        case LogEntry::Level::Debug:
            level = LogLevel::Debug;
            break;
        case LogEntry::Level::Info:
            level = LogLevel::Info;
            break;
        case LogEntry::Level::Warn:
            level = LogLevel::Warn;
            break;
        case LogEntry::Level::Error:
            level = LogLevel::Error;
            break;
        default:
            level = LogLevel::UNKNOWN;
            break;
        }

        if (match(entry.time, level, entry.message.data(), entry.message.size()))
        {
            LogMessage msg;
            msg.set_time(entry.time);
            msg.set_level(level);
            msg.set_message(std::string(entry.message));
            return msg;
        }
    }
}

bool LogIterator::match(const int64_t time, const LogLevel level, const char * c, size_t sz) const
{
    // Check time range
    if (time >= end_time || time < start_time)
        return false;

    // Check level
    if (std::find(levels.begin(), levels.end(), level) == levels.end() && !levels.empty())
        return false;

    // Grep
    re2::StringPiece content{c, sz};
    for (auto && regex : compiled_patterns)
    {
        if (!RE2::PartialMatch(content, *regex))
            return false;
    }

    return true;
}

static inline bool read_int(const char * s, size_t n, int & result)
{
    result = 0;
    for (size_t i = 0; i < n; ++i)
    {
        result *= 10;
        int x = s[i] - '0';
        if (x < 0 || x > 9)
            return false;
        result += x;
    }
    return true;
}

static inline bool read_sint(const char * s, size_t n, int & result)
{
    result = 0;
    int start = 0;
    int sign = 1;
    if (s[start] == '+')
    {
        start++;
    }
    else if (s[start] == '-')
    {
        start++;
        sign = -1;
    }
    read_int(s + start, n - start, result);
    result *= sign;
    return true;
}

bool LogIterator::readLevel(size_t limit, const char * s, size_t & level_start, size_t & level_size)
{
    level_start = 33;
    level_size = 0;
    if (level_start >= limit)
        return false;
    if (s[level_start] != '[')
        return false;
    level_start++;
    while (true)
    {
        if (level_size > 7)
        {
            // max length is 5
            return false;
        }
        if (level_start + level_size >= limit)
            return false;
        if (s[level_start + level_size] == ']')
            break;
        if (s[level_start + level_size] == '\0')
            return false;
        level_size++;
    }
    return true;
}

bool LogIterator::readDate(
    size_t limit,
    const char * s,
    int & y,
    int & m,
    int & d,
    int & H,
    int & M,
    int & S,
    int & MS,
    int & TZH,
    int & TZM)
{
    if (31 >= limit)
        return false;
    if (!read_int(s + 1, 4, y))
        return false;
    if (!read_int(s + 6, 2, m))
        return false;
    if (!read_int(s + 9, 2, d))
        return false;
    if (!read_int(s + 12, 2, H))
        return false;
    if (!read_int(s + 15, 2, M))
        return false;
    if (!read_int(s + 18, 2, S))
        return false;
    if (!read_int(s + 21, 3, MS))
        return false;
    if (!read_sint(s + 25, 3, TZH))
        return false;
    if (!read_int(s + 29, 2, TZM))
        return false;
    return true;
}

std::optional<LogIterator::Error> LogIterator::readLog(LogEntry & entry)
{
    if (!log_input_stream)
    {
        if (log_input_stream.eof())
            return std::optional<Error>(Error{Error::Type::EOI});
        else
            return std::optional<Error>(Error{Error::Type::UNKNOWN});
    }

    std::getline(log_input_stream, line);
    cur_lineno++;

    thread_local char prev_time_buff[35] = {0};
    thread_local time_t prev_time_t;

    constexpr size_t timestamp_finish_offset = 32;
    constexpr size_t log_level_start_finish_offset = 33;
    const char * loglevel_start;
    size_t loglevel_size;
    size_t loglevel_s;

    if (strncmp(prev_time_buff, line.data(), timestamp_finish_offset) == 0)
    {
        // If we can reuse prev_time
        entry.time = prev_time_t;

        if (!LogIterator::readLevel(line.size(), line.data(), loglevel_s, loglevel_size))
        {
            return std::optional<Error>(Error{Error::Type::UNEXPECTED_LOG_HEAD});
        }
        else
        {
            loglevel_start = line.data() + loglevel_s;
        }
    }
    else
    {
        int milli_second;
        int timezone_hour, timezone_min;
        std::tm time{};
        int year, month, day, hour, minute, second;
        if (LogIterator::readDate(line.size(), line.data(), year, month, day, hour, minute, second, milli_second, timezone_hour, timezone_min)
            && LogIterator::readLevel(line.size(), line.data(), loglevel_s, loglevel_size))
        {
            loglevel_start = line.data() + loglevel_s;
        }
        else
        {
            return std::optional<Error>(Error{Error::Type::UNEXPECTED_LOG_HEAD});
        }
        time.tm_year = year - 1900;
        time.tm_mon = month - 1;
        time.tm_mday = day;
        time.tm_hour = hour;
        time.tm_min = minute;
        time.tm_sec = second;
        time_t ctime = fast_mktime(&time) * 1000; // milliseconds
        ctime += milli_second; // truncate microseconds
        entry.time = ctime;

        memset(prev_time_buff, 0, sizeof prev_time_buff);
        strncpy(prev_time_buff, line.data(), timestamp_finish_offset);
        prev_time_t = ctime;
    }

    if (entry.time > end_time)
        return std::optional<Error>(Error{Error::Type::EOI});

    if (memcmp(loglevel_start, "TRACE", loglevel_size) == 0)
    {
        entry.level = LogEntry::Level::Trace;
    }
    else if (memcmp(loglevel_start, "DEBUG", loglevel_size) == 0)
    {
        entry.level = LogEntry::Level::Debug;
    }
    else if (memcmp(loglevel_start, "INFO", loglevel_size) == 0)
    {
        entry.level = LogEntry::Level::Info;
    }
    else if (memcmp(loglevel_start, "WARN", loglevel_size) == 0)
    {
        entry.level = LogEntry::Level::Warn;
    }
    else if (memcmp(loglevel_start, "ERROR", loglevel_size) == 0)
    {
        entry.level = LogEntry::Level::Error;
    }
    if (loglevel_size == 0)
        return std::optional<Error>(Error{Error::Type::INVALID_LOG_LEVEL});

    size_t message_begin = log_level_start_finish_offset + loglevel_size + 3; // [] and a space
    if (line.size() <= message_begin)
    {
        return std::optional<Error>(Error{Error::Type::UNEXPECTED_LOG_HEAD});
    }
    entry.message = std::string_view(line);
    entry.message = entry.message.substr(message_begin, std::string_view::npos);
    return {};
}

void LogIterator::init()
{
    // Check empty, if empty then fill with ".*"
    if (patterns.empty() || (patterns.size() == 1 && patterns[0].empty()))
    {
        patterns = {".*"};
    }
    for (auto && pattern : patterns)
    {
        compiled_patterns.push_back(std::make_unique<RE2>(pattern));
    }
}

LogIterator::~LogIterator()
{
    if (err_info.has_value())
    {
        LOG_FMT_DEBUG(log, "LogIterator search end with error {} at line {}.", err_info->second, err_info->first);
    }
}

// read approximate timestamp, round up to second, return -1 if failed.
int64_t readApproxiTimestamp(const char * start, const char * date_format)
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
    int milli_second;

    if (std::sscanf(start, date_format, &year, &month, &day, &hour, &minute, &second, &milli_second) != 7)
    {
        return -1;
    }

    std::tm time;
    time.tm_year = year - 1900;
    time.tm_mon = month - 1;
    time.tm_mday = day;
    time.tm_hour = hour;
    time.tm_min = minute;
    time.tm_sec = second + (milli_second ? 1 : 0); // ignore millisecond
    time_t ctime = mktime(&time) * 1000;

    return ctime;
}

// if name ends with date format and timestamp < start-time, return true.
// otherwise(can NOT tell ts) return false.
bool filterLogEndDatetime(const std::string & path, const std::string & example, const char * date_format, const int64_t start_time)
{
    if (path.size() > example.size())
    {
        auto tso = readApproxiTimestamp(path.data() + path.size() - example.size(), date_format);
        if (tso == -1)
            return false;
        if (tso < start_time)
            return true;
    }
    return false;
}

static const std::string gz_suffix = ".gz";

// if timestamp of log file could be told, return whether it can be filtered.
bool FilterFileByDatetime(
    const std::string & path,
    const std::vector<std::string> & ignore_log_file_prefixes,
    const int64_t start_time)
{
    static const std::string date_format_example = "0000-00-00-00:00:00.000";
    static const std::string raftstore_proxy_date_format_example = "0000-00-00-00:00:00.000000000";
    static const char * date_format = "%d-%d-%d-%d:%d:%d.%d";

    for (const auto & ignore_log_file_prefix : ignore_log_file_prefixes)
    {
        if (!ignore_log_file_prefix.empty() && startsWith(path, ignore_log_file_prefix))
            return true;
    }
    if (endsWith(path, gz_suffix))
    {
        if (path.size() <= gz_suffix.size() + date_format_example.size())
            return false;

        auto date_str = std::string(path.end() - gz_suffix.size() - date_format_example.size(), path.end() - gz_suffix.size());

        if (auto ts = readApproxiTimestamp(date_str.data(), date_format); ts == -1)
        {
            return false;
        }
        else if (ts < start_time)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        // filter proxy log end datetime
        return filterLogEndDatetime(path, raftstore_proxy_date_format_example, date_format, start_time);
    }
}

// if path ends with `.gz`, try to read by `Poco::InflatingInputStream`
void ReadLogFile(const std::string & path, std::function<void(std::istream &)> && cb)
{
    if (endsWith(path, gz_suffix))
    {
        std::ifstream istr(path, std::ios::binary);
        Poco::InflatingInputStream inflater(istr, Poco::InflatingStreamBuf::STREAM_GZIP);
        cb(inflater);
        istr.close();
    }
    else
    {
        std::ifstream istr(path);
        cb(istr);
        istr.close();
    }
}

} // namespace DB

ext: cpp

max_stars_repo_name: solotzg/tiflash

max_issues_repo_name: solotzg/tiflash

max_forks_repo_name: solotzg/tiflash


text:
/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center.

All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkDiffusionFunctionCollection.h"
#include "mitkNumericTypes.h"

#include <boost/math/special_functions/legendre.hpp>
#include <boost/math/special_functions/spherical_harmonic.hpp>
#include <boost/version.hpp>
#include <boost/algorithm/string.hpp>
#include <itkPointShell.h>
#include "itkVectorContainer.h"
#include "vnl/vnl_vector.h"
#include <vtkBox.h>
#include <vtkMath.h>
#include <itksys/SystemTools.hxx>
#include <itkShToOdfImageFilter.h>
#include <itkTensorImageToOdfImageFilter.h>

// Intersect a finite line (with end points p0 and p1) with all of the
// cells of a vtkImageData
std::vector< std::pair< itk::Index<3>, double > > mitk::imv::IntersectImage(const itk::Vector<double,3>& spacing, itk::Index<3>& si, itk::Index<3>& ei, itk::ContinuousIndex<float, 3>& sf, itk::ContinuousIndex<float, 3>& ef)
{
  std::vector< std::pair< itk::Index<3>, double > > out;
  if (si == ei)
  {
    double d[3];
    for (int i=0; i<3; ++i)
      d[i] = static_cast<double>(sf[i]-ef[i])*spacing[i];
    double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] );
    out.push_back(  std::pair< itk::Index<3>, double >(si, len) );
    return out;
  }

  double bounds[6];

  double entrancePoint[3];
  double exitPoint[3];

  double startPoint[3];
  double endPoint[3];

  double t0, t1;
  for (unsigned int i=0; i<3; ++i)
  {
    startPoint[i] = static_cast<double>(sf[i]);
    endPoint[i] = static_cast<double>(ef[i]);

    if (si[i]>ei[i])
    {
      auto t = si[i];
      si[i] = ei[i];
      ei[i] = t;
    }
  }

  for (auto x = si[0]; x<=ei[0]; ++x)
    for (auto y = si[1]; y<=ei[1]; ++y)
      for (auto z = si[2]; z<=ei[2]; ++z)
      {
        bounds[0] = static_cast<double>(x) - 0.5;
        bounds[1] = static_cast<double>(x) + 0.5;
        bounds[2] = static_cast<double>(y) - 0.5;
        bounds[3] = static_cast<double>(y) + 0.5;
        bounds[4] = static_cast<double>(z) - 0.5;
        bounds[5] = static_cast<double>(z) + 0.5;

        int entryPlane;
        int exitPlane;
        int hit = vtkBox::IntersectWithLine(bounds,
                                            startPoint,
                                            endPoint,
                                            t0,
                                            t1,
                                            entrancePoint,
                                            exitPoint,
                                            entryPlane,
                                            exitPlane);
        if (hit)
        {
          if (entryPlane>=0 && exitPlane>=0)
          {
            double d[3];
            for (int i=0; i<3; ++i)
              d[i] = (exitPoint[i] - entrancePoint[i])*spacing[i];
            double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] );

            itk::Index<3> idx; idx[0] = x; idx[1] = y; idx[2] = z;
            out.push_back(  std::pair< itk::Index<3>, double >(idx, len) );
          }
          else if (entryPlane>=0)
          {
            double d[3];
            for (int i=0; i<3; ++i)
              d[i] = (static_cast<double>(ef[i]) - entrancePoint[i])*spacing[i];
            double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] );

            itk::Index<3> idx; idx[0] = x; idx[1] = y; idx[2] = z;
            out.push_back(  std::pair< itk::Index<3>, double >(idx, len) );
          }
          else if (exitPlane>=0)
          {
            double d[3];
            for (int i=0; i<3; ++i)
              d[i] = (exitPoint[i]-static_cast<double>(sf[i]))*spacing[i];
            double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] );

            itk::Index<3> idx; idx[0] = x; idx[1] = y; idx[2] = z;
            out.push_back(  std::pair< itk::Index<3>, double >(idx, len) );
          }
        }
      }
  return out;
}

//------------------------- SH-function ------------------------------------

double mitk::sh::factorial(int number) {
  if(number <= 1) return 1;
  double result = 1.0;
  for(int i=1; i<=number; i++)
    result *= i;
  return result;
}

void mitk::sh::Cart2Sph(double x, double y, double z, double *spherical)
{
  double phi, th, rad;
  rad = sqrt(x*x+y*y+z*z);
  if( rad < mitk::eps )
  {
    th = itk::Math::pi/2;
    phi = itk::Math::pi/2;
  }
  else
  {
    th = acos(z/rad);
    phi = atan2(y, x);
  }
  spherical[0] = phi;
  spherical[1] = th;
  spherical[2] = rad;
}

vnl_vector_fixed<double, 3> mitk::sh::Sph2Cart(const double& theta, const double& phi, const double& rad)
{
  vnl_vector_fixed<double, 3> dir;
  dir[0] = rad * sin(theta) * cos(phi);
  dir[1] = rad * sin(theta) * sin(phi);
  dir[2] = rad * cos(theta);
  return dir;
}

double mitk::sh::legendre0(int l)
{
  if( l%2 != 0 )
  {
    return 0;
  }
  else
  {
    double prod1 = 1.0;
    for(int i=1;i<l;i+=2) prod1 *= i;
    double prod2 = 1.0;
    for(int i=2;i<=l;i+=2) prod2 *= i;
    return pow(-1.0,l/2.0)*(prod1/prod2);
  }
}

double mitk::sh::Yj(int m, int k, float theta, float phi, bool mrtrix)
{
  if (!mrtrix)
  {
    if (m<0)
      return sqrt(2.0)*static_cast<double>(::boost::math::spherical_harmonic_r(static_cast<unsigned int>(k), -m, theta, phi));
    else if (m==0)
      return static_cast<double>(::boost::math::spherical_harmonic_r(static_cast<unsigned int>(k), m, theta, phi));
    else
      return pow(-1.0,m)*sqrt(2.0)*static_cast<double>(::boost::math::spherical_harmonic_i(static_cast<unsigned int>(k), m, theta, phi));
  }
  else
  {
    double plm = static_cast<double>(::boost::math::legendre_p<float>(k,abs(m),-cos(theta)));
    double mag = sqrt((2.0*k+1.0)/(4.0*itk::Math::pi)*::boost::math::factorial<double>(k-abs(m))/::boost::math::factorial<double>(k+abs(m)))*plm;
    if (m>0)
      return mag*static_cast<double>(cos(m*phi));
    else if (m==0)
      return mag;
    else
      return mag*static_cast<double>(sin(-m*phi));
  }

  return 0;
}

mitk::OdfImage::ItkOdfImageType::Pointer mitk::convert::GetItkOdfFromShImage(mitk::Image::Pointer mitkImage)
{
  mitk::ShImage::Pointer mitkShImage = dynamic_cast<mitk::ShImage*>(mitkImage.GetPointer());
  if (mitkShImage.IsNull())
    mitkThrow() << "Input image is not a SH image!";
  mitk::OdfImage::ItkOdfImageType::Pointer output;
  switch (mitkShImage->ShOrder())
  {
  case 2:
  {
    typedef itk::ShToOdfImageFilter< float, 2 > ShConverterType;
    typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New();
    mitk::CastToItkImage(mitkImage, itkvol);
    typename ShConverterType::Pointer converter = ShConverterType::New();
    converter->SetInput(itkvol);
    converter->SetToolkit(mitkShImage->GetShConvention());
    converter->Update();
    output = converter->GetOutput();
    break;
  }
  case 4:
  {
    typedef itk::ShToOdfImageFilter< float, 4 > ShConverterType;
    typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New();
    mitk::CastToItkImage(mitkImage, itkvol);
    typename ShConverterType::Pointer converter = ShConverterType::New();
    converter->SetInput(itkvol);
    converter->SetToolkit(mitkShImage->GetShConvention());
    converter->Update();
    output = converter->GetOutput();
    break;
  }
  case 6:
  {
    typedef itk::ShToOdfImageFilter< float, 6 > ShConverterType;
    typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New();
    mitk::CastToItkImage(mitkImage, itkvol);
    typename ShConverterType::Pointer converter = ShConverterType::New();
    converter->SetInput(itkvol);
    converter->SetToolkit(mitkShImage->GetShConvention());
    converter->Update();
    output = converter->GetOutput();
    break;
  }
  case 8:
  {
    typedef itk::ShToOdfImageFilter< float, 8 > ShConverterType;
    typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New();
    mitk::CastToItkImage(mitkImage, itkvol);
    typename ShConverterType::Pointer converter = ShConverterType::New();
    converter->SetInput(itkvol);
    converter->SetToolkit(mitkShImage->GetShConvention());
    converter->Update();
    output = converter->GetOutput();
    break;
  }
  case 10:
  {
    typedef itk::ShToOdfImageFilter< float, 10 > ShConverterType;
    typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New();
    mitk::CastToItkImage(mitkImage, itkvol);
    typename ShConverterType::Pointer converter = ShConverterType::New();
    converter->SetInput(itkvol);
    converter->SetToolkit(mitkShImage->GetShConvention());
    converter->Update();
    output = converter->GetOutput();
    break;
  }
  case 12:
  {
    typedef itk::ShToOdfImageFilter< float, 12 > ShConverterType;
    typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New();
    mitk::CastToItkImage(mitkImage, itkvol);
    typename ShConverterType::Pointer converter = ShConverterType::New();
    converter->SetInput(itkvol);
    converter->SetToolkit(mitkShImage->GetShConvention());
    converter->Update();
    output = converter->GetOutput();
    break;
  }
  default:
    mitkThrow() << "SH orders higher than 12 are not supported!";
  }

  return output;
}

mitk::OdfImage::Pointer mitk::convert::GetOdfFromShImage(mitk::Image::Pointer mitkImage)
{
  mitk::OdfImage::Pointer image = mitk::OdfImage::New();
  auto img = GetItkOdfFromShImage(mitkImage);
  image->InitializeByItk( img.GetPointer() );
  image->SetVolume( img->GetBufferPointer() );
  return image;
}

mitk::OdfImage::ItkOdfImageType::Pointer mitk::convert::GetItkOdfFromTensorImage(mitk::Image::Pointer mitkImage)
{
  typedef itk::TensorImageToOdfImageFilter< float, float > FilterType;
  FilterType::Pointer filter = FilterType::New();
  filter->SetInput( GetItkTensorFromTensorImage(mitkImage) );
  filter->Update();
  return filter->GetOutput();
}

mitk::TensorImage::ItkTensorImageType::Pointer mitk::convert::GetItkTensorFromTensorImage(mitk::Image::Pointer mitkImage)
{
  typedef mitk::ImageToItk< mitk::TensorImage::ItkTensorImageType > CasterType;
  CasterType::Pointer caster = CasterType::New();
  caster->SetInput(mitkImage);
  caster->Update();
  return caster->GetOutput();
}

mitk::PeakImage::ItkPeakImageType::Pointer mitk::convert::GetItkPeakFromPeakImage(mitk::Image::Pointer mitkImage)
{
  typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType;
  CasterType::Pointer caster = CasterType::New();
  caster->SetInput(mitkImage);
  caster->SetCopyMemFlag(true);
  caster->Update();
  return caster->GetOutput();
}

mitk::OdfImage::Pointer mitk::convert::GetOdfFromTensorImage(mitk::Image::Pointer mitkImage)
{
  mitk::OdfImage::Pointer image = mitk::OdfImage::New();
  auto img = GetItkOdfFromTensorImage(mitkImage);
  image->InitializeByItk( img.GetPointer() );
  image->SetVolume( img->GetBufferPointer() );
  return image;
}

mitk::OdfImage::ItkOdfImageType::Pointer mitk::convert::GetItkOdfFromOdfImage(mitk::Image::Pointer mitkImage)
{
  typedef mitk::ImageToItk< mitk::OdfImage::ItkOdfImageType > CasterType;
  CasterType::Pointer caster = CasterType::New();
  caster->SetInput(mitkImage);
  caster->Update();
  return caster->GetOutput();
}

vnl_matrix<float> mitk::sh::CalcShBasisForDirections(unsigned int sh_order, vnl_matrix<double> U, bool mrtrix)
{
  vnl_matrix<float> sh_basis  = vnl_matrix<float>(U.cols(), (sh_order*sh_order + sh_order + 2)/2 + sh_order );
  for(unsigned int i=0; i<U.cols(); i++)
  {
    double x = U(0,i);
    double y = U(1,i);
    double z = U(2,i);
    double spherical[3];
    mitk::sh::Cart2Sph(x,y,z,spherical);
    U(0,i) = spherical[0];
    U(1,i) = spherical[1];
    U(2,i) = spherical[2];
  }

  for(unsigned int i=0; i<U.cols(); i++)
  {
    for(int k=0; k<=static_cast<int>(sh_order); k+=2)
    {
      for(int m=-k; m<=k; m++)
      {
        int j = (k*k + k + 2)/2 + m - 1;
        double phi = U(0,i);
        double th = U(1,i);
        sh_basis(i,j) = mitk::sh::Yj(m,k,th,phi, mrtrix);
      }
    }
  }

  return sh_basis;
}

unsigned int mitk::sh::ShOrder(int num_coeffs)
{
  int c=3, d=2-2*num_coeffs;
  int D = c*c-4*d;
  if (D>0)
  {
    int s = (-c+static_cast<int>(sqrt(D)))/2;
    if (s<0)
      s = (-c-static_cast<int>(sqrt(D)))/2;
    return static_cast<unsigned int>(s);
  }
  else if (D==0)
    return static_cast<unsigned int>(-c/2);
  return 0;
}

float mitk::sh::GetValue(const vnl_vector<float> &coefficients, const int &sh_order, const double theta, const double phi, const bool mrtrix)
{
  float val = 0;
  for(int k=0; k<=sh_order; k+=2)
  {
    for(int m=-k; m<=k; m++)
    {
      unsigned int j = static_cast<unsigned int>((k*k + k + 2)/2 + m - 1);
      val += coefficients[j] * mitk::sh::Yj(m, k, theta, phi, mrtrix);
    }
  }

  return val;
}

float mitk::sh::GetValue(const vnl_vector<float> &coefficients, const int &sh_order, const vnl_vector_fixed<double, 3> &dir, const bool mrtrix)
{
  double spherical[3];
  mitk::sh::Cart2Sph(dir[0], dir[1], dir[2], spherical);

  float val = 0;
  for(int k=0; k<=sh_order; k+=2)
  {
    for(int m=-k; m<=k; m++)
    {
      int j = (k*k + k + 2)/2 + m - 1;
      val += coefficients[j] * mitk::sh::Yj(m, k, spherical[1], spherical[0], mrtrix);
    }
  }

  return val;
}

//------------------------- gradients-function ------------------------------------

mitk::gradients::GradientDirectionContainerType::ConstPointer mitk::gradients::ReadBvalsBvecs(std::string bvals_file, std::string bvecs_file, double& reference_bval)
{
  mitk::gradients::GradientDirectionContainerType::Pointer directioncontainer = mitk::gradients::GradientDirectionContainerType::New();

  std::vector<float> bvec_entries;
  if (!itksys::SystemTools::FileExists(bvecs_file))
    mitkThrow() << "bvecs file not existing: " << bvecs_file;
  else
  {
    std::string line;
    std::ifstream myfile (bvecs_file.c_str());
    if (myfile.is_open())
    {
      while (std::getline(myfile, line))
      {
        std::vector<std::string> strs;
        boost::split(strs,line,boost::is_any_of("\t \n"));
        for (auto token : strs)
        {
          if (!token.empty())
          {
            try
            {
              bvec_entries.push_back(boost::lexical_cast<float>(token));
            }
            catch(...)
            {
              mitkThrow() << "Encountered invalid bvecs file entry >" << token << "<";
            }
          }
        }
      }
      myfile.close();
    }
    else
    {
      mitkThrow() << "bvecs file could not be opened: " << bvals_file;
    }
  }

  reference_bval = -1;
  std::vector<float> bval_entries;
  if (!itksys::SystemTools::FileExists(bvals_file))
    mitkThrow() << "bvals file not existing: " << bvals_file;
  else
  {
    std::string line;
    std::ifstream myfile (bvals_file.c_str());
    if (myfile.is_open())
    {
      while (std::getline(myfile, line))
      {
        std::vector<std::string> strs;
        boost::split(strs,line,boost::is_any_of("\t \n"));
        for (auto token : strs)
        {
          if (!token.empty())
          {
            try {
              bval_entries.push_back(boost::lexical_cast<float>(token));
              if (bval_entries.back()>reference_bval)
                reference_bval = bval_entries.back();
            }
            catch(...)
            {
              mitkThrow() << "Encountered invalid bvals file entry >" << token << "<";
            }
          }
        }
      }
      myfile.close();
    }
    else
    {
      mitkThrow() << "bvals file could not be opened: " << bvals_file;
    }
  }

  for(unsigned int i=0; i<bval_entries.size(); i++)
  {
    double b_val = bval_entries.at(i);

    mitk::gradients::GradientDirectionType vec;
    vec[0] = bvec_entries.at(i);
    vec[1] = bvec_entries.at(i+bval_entries.size());
    vec[2] = bvec_entries.at(i+2*bval_entries.size());

    // Adjust the vector length to encode gradient strength
    if (reference_bval>0)
    {
      double factor = b_val/reference_bval;
      if(vec.magnitude() > 0)
      {
        vec.normalize();
        vec[0] = sqrt(factor)*vec[0];
        vec[1] = sqrt(factor)*vec[1];
        vec[2] = sqrt(factor)*vec[2];
      }
    }

    directioncontainer->InsertElement(i,vec);
  }

  return GradientDirectionContainerType::ConstPointer(directioncontainer);
}

void mitk::gradients::WriteBvalsBvecs(std::string bvals_file, std::string bvecs_file, GradientDirectionContainerType::ConstPointer gradients, double reference_bval)
{
  std::ofstream myfile;
  myfile.open (bvals_file.c_str());
  for(unsigned int i=0; i<gradients->Size(); i++)
  {
    double twonorm = gradients->ElementAt(i).two_norm();
    myfile << std::round(reference_bval*twonorm*twonorm) << " ";
  }
  myfile.close();

  std::ofstream myfile2;
  myfile2.open (bvecs_file.c_str());
  for(int j=0; j<3; j++)
  {
    for(unsigned int i=0; i<gradients->Size(); i++)
    {
      GradientDirectionType direction = gradients->ElementAt(i);
      direction.normalize();
      myfile2 << direction.get(j) << " ";
    }
    myfile2 << std::endl;
  }
}

std::vector<unsigned int> mitk::gradients::GetAllUniqueDirections(const BValueMap & refBValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer )
{

  IndiciesVector directioncontainer;
  auto mapIterator = refBValueMap.begin();

  if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1)
    mapIterator++; //skip bzero Values

  for( ; mapIterator != refBValueMap.end(); mapIterator++){

    IndiciesVector currentShell = mapIterator->second;

    while(currentShell.size()>0)
    {
      unsigned int wntIndex = currentShell.back();
      currentShell.pop_back();

      auto containerIt = directioncontainer.begin();
      bool directionExist = false;
      while(containerIt != directioncontainer.end())
      {
        if (fabs(dot_product(refGradientsContainer->ElementAt(*containerIt), refGradientsContainer->ElementAt(wntIndex)))  > 0.9998)
        {
          directionExist = true;
          break;
        }
        containerIt++;
      }
      if(!directionExist)
      {
        directioncontainer.push_back(wntIndex);
      }
    }
  }

  return directioncontainer;
}

bool mitk::gradients::CheckForDifferingShellDirections(const BValueMap & refBValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer)
{
  auto mapIterator = refBValueMap.begin();

  if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1)
    mapIterator++; //skip bzero Values

  for( ; mapIterator != refBValueMap.end(); mapIterator++){

    auto mapIterator_2 = refBValueMap.begin();
    if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1)
      mapIterator_2++; //skip bzero Values

    for( ; mapIterator_2 != refBValueMap.end(); mapIterator_2++){

      if(mapIterator_2 == mapIterator) continue;

      IndiciesVector currentShell = mapIterator->second;
      IndiciesVector testShell = mapIterator_2->second;
      for (unsigned int i = 0; i< currentShell.size(); i++)
        if (fabs(dot_product(refGradientsContainer->ElementAt(currentShell[i]), refGradientsContainer->ElementAt(testShell[i])))  <= 0.9998) { return true; }

    }
  }
  return false;
}

vnl_matrix<double> mitk::gradients::ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer)
{

  vnl_matrix<double> Q(3, refShell.size());
  Q.fill(0.0);

  for(unsigned int i = 0; i < refShell.size(); i++)
  {
    GradientDirectionType dir = refGradientsContainer->ElementAt(refShell[i]);
    double x = dir.normalize().get(0);
    double y = dir.normalize().get(1);
    double z = dir.normalize().get(2);
    double cart[3];
    mitk::sh::Cart2Sph(x,y,z,cart);
    Q(0,i) = cart[0];
    Q(1,i) = cart[1];
    Q(2,i) = cart[2];
  }
  return Q;
}

vnl_matrix<double> mitk::gradients::ComputeSphericalHarmonicsBasis(const vnl_matrix<double> & QBallReference, const unsigned int & LOrder)
{
  vnl_matrix<double> SHBasisOutput(QBallReference.cols(), (LOrder+1)*(LOrder+2)*0.5);
  SHBasisOutput.fill(0.0);
  for(unsigned int i=0; i<SHBasisOutput.rows(); i++)
    for(int k = 0; k <= (int)LOrder; k += 2)
      for(int m =- k; m <= k; m++)
      {
        int j = ( k * k + k + 2 ) / 2.0 + m - 1;
        double phi = QBallReference(0,i);
        double th = QBallReference(1,i);
        double val = mitk::sh::Yj(m,k,th,phi);
        SHBasisOutput(i,j) = val;
      }
  return SHBasisOutput;
}

mitk::gradients::GradientDirectionContainerType::Pointer mitk::gradients::CreateNormalizedUniqueGradientDirectionContainer(const mitk::gradients::BValueMap & bValueMap,
                                                                                const GradientDirectionContainerType *origninalGradentcontainer)
{
  mitk::gradients::GradientDirectionContainerType::Pointer directioncontainer = mitk::gradients::GradientDirectionContainerType::New();
  auto mapIterator = bValueMap.begin();

  if(bValueMap.find(0) != bValueMap.end() && bValueMap.size() > 1){
    mapIterator++; //skip bzero Values
    vnl_vector_fixed<double, 3> vec;
    vec.fill(0.0);
    directioncontainer->push_back(vec);
  }

  for( ; mapIterator != bValueMap.end(); mapIterator++){

    IndiciesVector currentShell = mapIterator->second;

    while(currentShell.size()>0)
    {
      unsigned int wntIndex = currentShell.back();
      currentShell.pop_back();

      mitk::gradients::GradientDirectionContainerType::Iterator containerIt = directioncontainer->Begin();
      bool directionExist = false;
      while(containerIt != directioncontainer->End())
      {
        if (fabs(dot_product(containerIt.Value(), origninalGradentcontainer->ElementAt(wntIndex)))  > 0.9998)
        {
          directionExist = true;
          break;
        }
        containerIt++;
      }
      if(!directionExist)
      {
        GradientDirectionType dir(origninalGradentcontainer->ElementAt(wntIndex));
        directioncontainer->push_back(dir.normalize());
      }
    }
  }

  return directioncontainer;
}

max_stars_repo_name: HRS-Navigation/MITK-Diffusion

max_issues_repo_name: HRS-Navigation/MITK-Diffusion

max_forks_repo_name: HRS-Navigation/MITK-Diffusion


text:
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <boost/scoped_ptr.hpp>
#include "exec/hash-table.inline.h"
#include "exprs/scalar-expr-evaluator.h"
#include "exprs/slot-ref.h"
#include "gtest/gtest.h"
#include "runtime/bufferpool/buffer-pool.h"
#include "runtime/bufferpool/reservation-tracker.h"
#include "runtime/mem-pool.h"
#include "runtime/mem-tracker.h"
#include "runtime/runtime-state.h"
#include "runtime/test-env.h"
#include "service/fe-support.h"
#include "util/benchmark.h"
#include "util/cpu-info.h"
#include "util/runtime-profile-counters.h"

using namespace impala;
using namespace std;

// Sample Benchmark Results:
//
// Machine Info: Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
// Note: Benchmark name are in format <name>_XX_YY:
// name represents the name of benchmark (probe|build|memory).
// XX represents the number of rows in the dataset.
// YY represents the percentage of unique values in dataset.
// Runtime Benchmark
// -----------------
// 21/06/30 08:44:20 INFO util.JvmPauseMonitor: Starting JVM pause monitor
// Hash Table Build:          Function  iters/ms   10%ile   50%ile   90%ile     10%ile     50%ile     90%ile
//                                                                          (relative) (relative) (relative)
// ---------------------------------------------------------------------------------------------------------
//                     build_65536_100               4.04     4.35     4.35         1X         1X         1X

//                      build_65536_60               8.27     9.09     9.26         1X         1X         1X

//                      build_65536_20               8.78     9.0     9.3           1X         1X         1X

//                    build_262144_100              0.386    0.407    0.407         1X         1X         1X

//                     build_262144_60              0.316    0.407    0.415         1X         1X         1X

//                     build_262144_20              0.295     0.31    0.316         1X         1X         1X

// Hash Table Probe:          Function  iters/ms   10%ile   50%ile   90%ile     10%ile     50%ile     90%ile
//                                                                          (relative) (relative) (relative)
// ---------------------------------------------------------------------------------------------------------
//                     probe_65536_100               2.64     2.64     2.69         1X         1X         1X

//                      probe_65536_60                5.1      5.96     6.08        1X         1X         1X

//                      probe_65536_20               6.2      6.35      6.35        1X         1X         1X

//                    probe_262144_100              0.233    0.237    0.237         1X         1X         1X

//                     probe_262144_60              0.237     0.25     0.25         1X         1X         1X

//                     probe_262144_20              0.255    0.259    0.259         1X         1X         1X

//              probe_65536_absentkeys               20.1     21.6     21.6         1X         1X         1X

//             probe_262144_absentkeys              0.727    0.741    0.741         1X         1X         1X

// Memory Benchmark
// ----------------
// Hash Table Memory Consumption:            Function      Bytes Consumed
// ----------------------------------------------------------------------
//                                 memory_1048576_100            12582912
//                                  memory_1048576_60            16777212
//                                  memory_1048576_20            20971512
//                                 memory_4194304_100            50331648
//                                  memory_4194304_60            67108868
//                                  memory_4194304_20            83886088

namespace htbenchmark {

class TestCtx {
 public:
  TestCtx() : mem_pool_(&tracker_) {}
  /// Temporary runtime environment for the hash table.
  unique_ptr<TestEnv> test_env_;
  RuntimeState* runtime_state_ = nullptr;

  /// Data for HashTable
  vector<TupleRow*> data;
  /// Hash tables and associated clients - automatically closed in TearDown().
  vector<BufferPool::ClientHandle*> clients_;
  HashTable* hash_table_;
  boost::scoped_ptr<HashTableCtx> hash_context_;
  vector<ScalarExpr*> build_exprs_;
  vector<ScalarExpr*> probe_exprs_;
  ObjectPool pool_;
  /// A dummy MemTracker used for exprs and other things we don't need to have limits on.
  MemTracker tracker_;
  MemPool mem_pool_;
  int initial_num_buckets;
  void SetUp(int num_buckets) {
    CreateTestEnv();
    initial_num_buckets = num_buckets;
    bool ht_success = CreateHashTable();
    CHECK(ht_success) << "Creation of HashTable failed";
    RowDescriptor rd;
    ScalarExpr* build_expr = pool_.Add(new SlotRef(ColumnType(TYPE_INT), 1, false));
    Status status = ((SlotRef*)build_expr)->Init(rd, true, nullptr);
    CHECK(status.ok());
    build_exprs_.push_back(build_expr);
    ScalarExpr* probe_expr = pool_.Add(new SlotRef(ColumnType(TYPE_INT), 1, false));
    status = ((SlotRef*)probe_expr)->Init(rd, true, nullptr);
    CHECK(status.ok());
    probe_exprs_.push_back(probe_expr);
    status = HashTableCtx::Create(&pool_, runtime_state_, build_exprs_, probe_exprs_,
        false /* !stores_nulls_ */, vector<bool>(build_exprs_.size(), false), 1, 0, 1,
        &mem_pool_, &mem_pool_, &mem_pool_, &hash_context_);
    CHECK(status.ok());
  }
  void TearDown() {
    ScalarExpr::Close(build_exprs_);
    ScalarExpr::Close(probe_exprs_);
    hash_table_->Close();
    hash_context_->Close(runtime_state_);
    for (BufferPool::ClientHandle* client : clients_) {
      test_env_->exec_env()->buffer_pool()->DeregisterClient(client);
    }
    clients_.clear();
    runtime_state_ = nullptr;
    mem_pool_.FreeAll();
    pool_.Clear();
  }
  /// Initialize test_env_ and runtime_state_ with the given page size and capacity
  /// for the given number of pages. If test_env_ was already created, then re-creates it.
  void CreateTestEnv(int64_t min_page_size = 64 * 1024,
      int64_t buffer_bytes_limit = 4L * 1024 * 1024 * 1024) {
    test_env_.reset(new TestEnv());
    test_env_->SetBufferPoolArgs(min_page_size, buffer_bytes_limit);
    Status status = test_env_->Init();
    CHECK(status.ok());

    TQueryOptions query_options;
    query_options.__set_default_spillable_buffer_size(min_page_size);
    query_options.__set_min_spillable_buffer_size(min_page_size);
    query_options.__set_buffer_pool_limit(buffer_bytes_limit);
    // Also initializes runtime_state_
    status = test_env_->CreateQueryState(0, &query_options, &runtime_state_);
    CHECK(status.ok());
  }
  /// Construct hash table and buffer pool client.
  /// Created objects and resources (e.g. reservations) are automatically freed
  /// in TearDown().
  bool CreateHashTable(int64_t block_size = 8 * 1024 * 1024, int max_num_blocks = 100,
      int initial_reserved_blocks = 10, int64_t suballocator_buffer_len = 64 * 1024) {
    ExecEnv* exec_env = test_env_->exec_env();
    BufferPool* buffer_pool = exec_env->buffer_pool();
    RuntimeProfile* profile = RuntimeProfile::Create(&pool_, "ht");

    // Set up memory tracking for the hash table.
    MemTracker* client_tracker =
        pool_.Add(new MemTracker(-1, "client", runtime_state_->instance_mem_tracker()));
    int64_t initial_reservation_bytes = block_size * initial_reserved_blocks;
    int64_t max_reservation_bytes = block_size * max_num_blocks;

    // Set up the memory allocator.
    BufferPool::ClientHandle* client = pool_.Add(new BufferPool::ClientHandle);
    clients_.push_back(client);
    Status status = buffer_pool->RegisterClient("htbenchmark", nullptr,
        runtime_state_->instance_buffer_reservation(), client_tracker,
        max_reservation_bytes, profile, client);
    if (!status.ok()) {
      std::cout << "Registering client to buffer pool failed" << std::endl;
      return false;
    }
    bool success = client->IncreaseReservation(initial_reservation_bytes);
    if (!success) {
      std::cout << "Client increasing reservation failed" << std::endl;
      return false;
    }
    Suballocator* allocator =
        pool_.Add(new Suballocator(buffer_pool, client, suballocator_buffer_len));

    int64_t max_num_buckets = 1L << 31;
    hash_table_ = pool_.Add(HashTable::Create(
        allocator, true, 1, nullptr, max_num_buckets, initial_num_buckets));
    status = hash_table_->Init(&success);
    if (!(status.ok() && success)) {
      std::cout << "HashTable Init failed" << std::endl;
    }
    return status.ok() && success;
  }
  TupleRow* CreateTupleRow(int32_t val) {
    uint8_t* tuple_row_mem = mem_pool_.Allocate(sizeof(int32_t*));
    Tuple* tuple_mem = Tuple::Create(sizeof(char) + sizeof(int32_t), &mem_pool_);
    *reinterpret_cast<int32_t*>(tuple_mem->GetSlot(1)) = val;
    tuple_mem->SetNotNull(NullIndicatorOffset(0, 1));
    TupleRow* row = reinterpret_cast<TupleRow*>(tuple_row_mem);
    row->SetTuple(0, tuple_mem);
    return row;
  }
  void CreateDataSet(int num_buckets, int unique_percent, int dup = 2) {
    int u_idx = (num_buckets * unique_percent) / 100;
    TupleRow* row;
    for (int i = 1; i <= u_idx; i++) {
      row = CreateTupleRow(i);
      data.push_back(row);
    }
    int count = u_idx;
    for (int j = u_idx + 1; j <= num_buckets && count < num_buckets; j++) {
      for (int i = 0; i < dup && count < num_buckets; i++) {
        row = CreateTupleRow(j);
        data.push_back(row);
        count++;
      }
    }
  }
  void CreateAbsentKeysData(int num_buckets) {
    data.clear();
    TupleRow* row;
    for (int i = 1; i <= num_buckets; i++) {
      row = CreateTupleRow(num_buckets + i);
      data.push_back(row);
    }
  }
};

void Probe(TestCtx* ctx, vector<TupleRow*>& pdata) {
  HashTable* hTable = ctx->hash_table_;
  HashTableCtx* ht_ctx = ctx->hash_context_.get();
  HashTable::Iterator iter;
  for (int i = 0; i < pdata.size(); i++) {
    const TupleRow* row = pdata[i];
    if (!ht_ctx->EvalAndHashProbe(row)) continue;
    iter = hTable->FindProbeRow(ht_ctx);
  }
}

void Build(TestCtx* ctx, vector<TupleRow*>& bdata) {
  HashTable* ht = ctx->hash_table_;
  HashTableCtx* ht_ctx = ctx->hash_context_.get();
  for (int i = 0; i < bdata.size(); i++) {
    TupleRow* row = bdata[i];
    if (!ht_ctx->EvalAndHashBuild(row)) continue;
    BufferedTupleStream::FlatRowPtr dummy_flat_row = nullptr;
    Status status;
    bool success = ht->Insert(ht_ctx, dummy_flat_row, row, &status);
    CHECK(status.ok() && success) << "Inserting a tuple in HashTable failed";
  }
}

namespace build {
void SetUp(void* args) {
  TestCtx* ctx = reinterpret_cast<TestCtx*>(args);
  // Clear old tuples of hash table
  ctx->hash_table_->Close();
  bool got_memory = true;
  Status status = ctx->hash_table_->Init(&got_memory);
  CHECK(status.ok() && got_memory) << "HashTable Reinitialization failed";
}
void Benchmark(int batch_size, void* args) {
  // batch_size is ignored. This is run just once.
  TestCtx* ctx = reinterpret_cast<TestCtx*>(args);
  Build(ctx, ctx->data);
}
}; // namespace build

namespace probe {
void Benchmark(int batch_size, void* args) {
  // batch_size is ignored. This is run just once.
  TestCtx* ctx = reinterpret_cast<TestCtx*>(args);
  Probe(ctx, ctx->data);
}
}; // namespace probe
}; // namespace htbenchmark

using namespace htbenchmark;
std::string ResultString(
    vector<std::string> benchmark_names, vector<int64_t> memory_consumption) {
  stringstream ss;
  int function_out_width = 50;
  int mem_width = 20;
  int total_width = function_out_width + mem_width;
  std::string name("Hash Table Memory Consumption");
  ss << name << ":" << setw(function_out_width - name.size() - 1) << "Function"
     << setw(mem_width) << "Bytes Consumed" << std::endl;
  for (int i = 0; i < total_width; ++i) {
    ss << '-';
  }
  ss << std::endl;
  for (int i = 0; i < benchmark_names.size(); i++) {
    ss << setw(function_out_width) << benchmark_names[i] << setw(mem_width)
       << memory_consumption[i] << std::endl;
  }
  return ss.str();
}

int64_t GetMemoryBytesConsumed(HashTable* ht) {
  int empty_buckets = ht->EmptyBuckets();
  int64_t mem_size = ht->CurrentMemSize();
  return mem_size - (empty_buckets * (HashTable::BUCKET_SIZE + 4));
}

int main(int argc, char** argv) {
  impala::InitCommonRuntime(argc, argv, true, impala::TestInfo::BE_TEST);
  impala::InitFeSupport();
  /// Runtime Benchmark
  std::cout << Benchmark::GetMachineInfo() << std::endl;
  std::cout << "Note: Benchmark name are in format <name>_XX_YY:" << std::endl
            << "name represents the name of benchmark (probe|build|memory)." << std::endl
            << "XX represents the number of rows in the dataset." << std::endl
            << "YY represents the percentage of unique values in dataset." << std::endl;
  std::cout << "Runtime Benchmark" << std::endl;
  std::cout << "-----------------" << std::endl;

  Benchmark hash_table_build("Hash Table Build", false);
  Benchmark hash_table_probe("Hash Table Probe", false);
  vector<int> num_tuples{65536, 262144};
  vector<int> unique_percent{100, 60, 20};
  vector<TestCtx*> ctxs;
  for (int num = 0; num < num_tuples.size(); num++) {
    for (int up = 0; up < unique_percent.size(); up++) {
      std::stringstream pname;
      std::stringstream bname;
      pname << "probe_" << num_tuples[num] << "_" << unique_percent[up];
      bname << "build_" << num_tuples[num] << "_" << unique_percent[up];
      TestCtx* ctx = new TestCtx();
      ctx->SetUp(num_tuples[num]);
      ctxs.push_back(ctx);
      ctx->CreateDataSet(num_tuples[num], unique_percent[up]);
      hash_table_build.AddBenchmark(bname.str(), build::Benchmark, (void*)ctx, -1);
      hash_table_probe.AddBenchmark(pname.str(), probe::Benchmark, (void*)ctx, -1);
    }
  }

  // Create Probe benchmark for Data not found in the table
  for (int num = 0; num < num_tuples.size(); num++) {
    TestCtx* ctx = new TestCtx();
    ctx->SetUp(num_tuples[num]);
    ctxs.push_back(ctx);
    ctx->CreateDataSet(num_tuples[num], 10);
    Build(ctx, ctx->data);
    ctx->CreateAbsentKeysData(num_tuples[num]);
    std::stringstream pname;
    pname << "probe_" << num_tuples[num] << "_absentkeys";
    hash_table_probe.AddBenchmark(pname.str(), probe::Benchmark, (void*)ctx, -1);
  }
  std::cout << hash_table_build.Measure(50, 10, build::SetUp) << std::endl;
  std::cout << hash_table_probe.Measure() << std::endl;
  // Cleanup contexts
  for (TestCtx* ct : ctxs) {
    ct->TearDown();
    free(ct);
  }

  /// Memory Benchmark
  std::cout << "Memory Benchmark" << std::endl;
  std::cout << "----------------" << std::endl;
  vector<std::string> benchmark_names;
  vector<int64_t> memory_consumption;
  num_tuples.clear();
  num_tuples.push_back(1024 * 1024);
  num_tuples.push_back(4 * 1024 * 1024);
  for (int num = 0; num < num_tuples.size(); num++) {
    for (int up = 0; up < unique_percent.size(); up++) {
      std::stringstream bname;
      bname << "memory_" << num_tuples[num] << "_" << unique_percent[up];
      TestCtx ctx;
      ctx.SetUp(num_tuples[num]);
      ctx.CreateDataSet(num_tuples[num], unique_percent[up]);
      Build(&ctx, ctx.data);
      benchmark_names.push_back(bname.str());
      memory_consumption.push_back(GetMemoryBytesConsumed(ctx.hash_table_));
      ctx.TearDown();
    }
  }
  std::cout << ResultString(benchmark_names, memory_consumption) << std::endl;
  return 0;
}

ext: cc

max_stars_repo_name: Keendata/impala

max_issues_repo_name: xwzbupt/impala

max_forks_repo_name: xwzbupt/impala


text:
//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id: ClientSM.cc 71 2008-07-07 15:49:14Z sriramsrao $
//
// Created 2006/06/05
// Author: Sriram Rao
//
// Copyright 2008 Quantcast Corp.
// Copyright 2006-2008 Kosmix Corp.
//
// This file is part of Kosmos File System (KFS).
//
// 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.
//
//
// \file ClientSM.h
// \brief Handlers that interface with a KFS client.
//
//----------------------------------------------------------------------------

#include "ClientSM.h"
#include "util.h"
using namespace KFS;

#include <string>
#include <sstream>
using std::string;
using std::ostringstream;

#include "common/log.h"
#include <boost/scoped_array.hpp>
using boost::scoped_array;

/// 由一个NetConnection创建一个clientSM, 用于和该Client通信
ClientSM::ClientSM(NetConnectionPtr &conn)
{
	mNetConnection = conn;
	mOp = NULL;
	SET_HANDLER(this, &ClientSM::HandleRequest);
}

ClientSM::~ClientSM()
{
	delete mOp;
}

///
/// Send out the response to the client request.  The response is
/// generated by MetaRequest as per the protocol.
/// @param[in] op The request for which we finished execution.
///
/// 向client发送回复信息, 这个信息由MetaRequest生成
void ClientSM::SendResponse(MetaRequest *op)
{
	ostringstream os;

	// 由MetaRequest生成对client的回复信息
	op->response(os);

	KFS_LOG_VA_DEBUG("Command %s, Status: %d",
			op->Show().c_str(), op->status);

	// 向该端口中发送数据
	mNetConnection->Write(os.str().c_str(), os.str().length());
}

///
/// Generic event handler.  Decode the event that occurred and
/// appropriately extract out the data and deal with the event.
/// @param[in] code: The type of event that occurred
/// @param[in] data: Data being passed in relative to the event that
/// occurred.
/// @retval 0 to indicate successful event handling; -1 otherwise.
///
/// 处理用户发送的请求: EVENT_NET_READ, EVENT_NET_WROTE, EVENT_CMD_DONE,
/// EVENT_NET_ERROR.
int ClientSM::HandleRequest(int code, void *data)
{
	IOBuffer *iobuf;
	MetaRequest *op;
	int cmdLen;

	switch (code)
	{
	case EVENT_NET_READ:
		// We read something from the network.  Run the RPC that
		// came in.
		iobuf = (IOBuffer *) data;
		if (IsMsgAvail(iobuf, &cmdLen))
			HandleClientCmd(iobuf, cmdLen);
		break;

	case EVENT_NET_WROTE:
		// Something went out on the network.  For now, we don't
		// track it. Later, we may use it for tracking throttling
		// and such.
		break;

	case EVENT_CMD_DONE:
		op = (MetaRequest *) data;
		assert(op == mOp);
		SendResponse(op);
		delete mOp;
		mOp = NULL;
		break;

	case EVENT_NET_ERROR:
		// KFS_LOG_VA_DEBUG("Closing connection");

		if (mNetConnection)
			mNetConnection->Close();

		SET_HANDLER(this, &ClientSM::HandleTerminate);
		if (mOp == NULL)
			delete this;

		break;

	default:
		assert(!"Unknown event");
		return -1;
	}
	return 0;
}

///
/// Termination handler: if the client has any op queued in the system, wait
/// for the op to finish.  Then, cleanup this.
/// @param[in] code: The type of event that occurred
/// @param[in] data: Data being passed in relative to the event that
/// occurred.
/// @retval 0 to indicate successful event handling; -1 otherwise.
///
int ClientSM::HandleTerminate(int code, void *data)
{
	MetaRequest *op;

	switch (code)
	{
	case EVENT_CMD_DONE:
		op = (MetaRequest *) data;
		assert(op == mOp);
		delete mOp;
		mOp = NULL;
		break;
	default:
		assert(!"Unknown event");
		return -1;
	}

	delete this;
	return 0;
}

///
/// We have a command in a buffer. So, parse out the command and
/// execute it if possible.
/// @param[in] iobuf: Buffer containing the command
/// @param[in] cmdLen: Length of the command in the buffer
///
void ClientSM::HandleClientCmd(IOBuffer *iobuf, int cmdLen)
{
	scoped_array<char> buf(new char[cmdLen + 1]);

	// 将iobuf中的数据拷贝到buf中
	iobuf->CopyOut(buf.get(), cmdLen);
	buf[cmdLen] = '\0';

	// 解析buf中的命令, 存放到mOp中
	if (ParseCommand(buf.get(), cmdLen, &mOp) != 0)
	{
		// 如果解析失败, 则清楚iobuf中该段数据
		iobuf->Consume(cmdLen);

		KFS_LOG_VA_DEBUG("Aye?: %s", buf.get());
		// got a bogus command
		return;
	}

	KFS_LOG_VA_DEBUG("Got command: %s", mOp->Show().c_str());

	// Command is ready to be pushed down.  So remove the cmd from the buffer.
	iobuf->Consume(cmdLen);

	mOp->clnt = this;
	// send it on its merry way
	// 提交到队列中, 等待执行操作
	submit_request(mOp);

	/*
	 if (iobuf->BytesConsumable() > 0) {
	 KFS_LOG_VA_DEBUG("More command data likely available for chunk: ");
	 }
	 */
}