/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/traced/probes/filesystem/prefix_finder.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_splitter.h"

namespace perfetto {

std::string PrefixFinder::Node::ToString() const {
  if (parent_ != nullptr)
    return parent_->ToString() + "/" + name_;
  return name_;
}

PrefixFinder::Node* PrefixFinder::Node::AddChild(std::string name) {
  auto it = children_.emplace(std::move(name), this);
  return const_cast<Node*>(&(*it.first));
}

PrefixFinder::Node* PrefixFinder::Node::MaybeChild(const std::string& name) {
  // This will be nicer with C++14 transparent comparators.
  // Then we will be able to look up by just the key using a sutiable
  // comparator.
  //
  // For now we need to allow to construct Node from the key.
  Node node(name);
  auto it = children_.find(node);
  if (it == children_.end())
    return nullptr;
  return const_cast<Node*>(&(*it));
}

PrefixFinder::PrefixFinder(size_t limit) : limit_(limit) {}

void PrefixFinder::InsertPrefix(size_t len) {
  Node* cur = &root_;
  for (auto it = state_.cbegin() + 1;
       it != state_.cbegin() + static_cast<ssize_t>(len + 1); it++) {
    Node* next = cur->MaybeChild(it->first);
    if (!next)
      next = cur->AddChild(it->first);
    cur = next;
  }
}

void PrefixFinder::Flush(size_t i) {
  PERFETTO_CHECK(i > 0);
  for (size_t j = i; j < state_.size(); ++j) {
    if (state_[j - 1].second > limit_ && state_[j].second <= limit_) {
      InsertPrefix(i);
      break;
    }
  }
}

void PrefixFinder::Finalize() {
  Flush(1);
  state_.resize(1);
#if PERFETTO_DCHECK_IS_ON()
  PERFETTO_DCHECK(!finalized_);
  finalized_ = true;
#endif
}

void PrefixFinder::AddPath(std::string path) {
  perfetto::base::StringSplitter s(std::move(path), '/');
  // An artificial element for the root directory.
  // This simplifies the logic below because we can always assume
  // there is a parent element.
  state_[0].second++;
  for (size_t i = 1; s.Next(); ++i) {
    char* token = s.cur_token();
    if (i < state_.size()) {
      std::pair<std::string, size_t>& elem = state_[i];
      if (elem.first == token) {
        elem.second++;
      } else {
        // Check if we need to write a prefix for any element
        // in the previous state.
        Flush(i);
        elem.first = token;
        elem.second = 1;
        state_.resize(i + 1);
      }
    } else {
      state_.emplace_back(token, 1);
    }
  }
}

PrefixFinder::Node* PrefixFinder::GetPrefix(std::string path) {
#if PERFETTO_DCHECK_IS_ON()
  PERFETTO_DCHECK(finalized_);
#endif
  perfetto::base::StringSplitter s(std::move(path), '/');
  Node* cur = &root_;
  for (; s.Next();) {
    char* token = s.cur_token();
    Node* next = cur->MaybeChild(token);
    if (next == nullptr)
      break;
    cur = next;
    PERFETTO_DCHECK(cur->name_ == token);
  }
  return cur;
}

}  // namespace perfetto
