//===- SourceCoverageView.h - Code coverage view for source code ----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class implements rendering for code coverage of source code.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
#define LLVM_COV_SOURCECOVERAGEVIEW_H

#include "CoverageViewOptions.h"
#include "llvm/ProfileData/CoverageMapping.h"
#include "llvm/Support/MemoryBuffer.h"
#include <vector>

namespace llvm {

class SourceCoverageView;

/// \brief A view that represents a macro or include expansion
struct ExpansionView {
  coverage::CounterMappingRegion Region;
  std::unique_ptr<SourceCoverageView> View;

  ExpansionView(const coverage::CounterMappingRegion &Region,
                std::unique_ptr<SourceCoverageView> View)
      : Region(Region), View(std::move(View)) {}
  ExpansionView(ExpansionView &&RHS)
      : Region(std::move(RHS.Region)), View(std::move(RHS.View)) {}
  ExpansionView &operator=(ExpansionView &&RHS) {
    Region = std::move(RHS.Region);
    View = std::move(RHS.View);
    return *this;
  }

  unsigned getLine() const { return Region.LineStart; }
  unsigned getStartCol() const { return Region.ColumnStart; }
  unsigned getEndCol() const { return Region.ColumnEnd; }

  friend bool operator<(const ExpansionView &LHS, const ExpansionView &RHS) {
    return LHS.Region.startLoc() < RHS.Region.startLoc();
  }
};

/// \brief A view that represents a function instantiation
struct InstantiationView {
  StringRef FunctionName;
  unsigned Line;
  std::unique_ptr<SourceCoverageView> View;

  InstantiationView(StringRef FunctionName, unsigned Line,
                    std::unique_ptr<SourceCoverageView> View)
      : FunctionName(FunctionName), Line(Line), View(std::move(View)) {}
  InstantiationView(InstantiationView &&RHS)
      : FunctionName(std::move(RHS.FunctionName)), Line(std::move(RHS.Line)),
        View(std::move(RHS.View)) {}
  InstantiationView &operator=(InstantiationView &&RHS) {
    FunctionName = std::move(RHS.FunctionName);
    Line = std::move(RHS.Line);
    View = std::move(RHS.View);
    return *this;
  }

  friend bool operator<(const InstantiationView &LHS,
                        const InstantiationView &RHS) {
    return LHS.Line < RHS.Line;
  }
};

/// \brief A code coverage view of a specific source file.
/// It can have embedded coverage views.
class SourceCoverageView {
private:
  /// \brief Coverage information for a single line.
  struct LineCoverageInfo {
    uint64_t ExecutionCount;
    unsigned RegionCount;
    bool Mapped;

    LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {}

    bool isMapped() const { return Mapped; }

    bool hasMultipleRegions() const { return RegionCount > 1; }

    void addRegionStartCount(uint64_t Count) {
      // The max of all region starts is the most interesting value.
      addRegionCount(RegionCount ? std::max(ExecutionCount, Count) : Count);
      ++RegionCount;
    }

    void addRegionCount(uint64_t Count) {
      Mapped = true;
      ExecutionCount = Count;
    }
  };

  const MemoryBuffer &File;
  const CoverageViewOptions &Options;
  coverage::CoverageData CoverageInfo;
  std::vector<ExpansionView> ExpansionSubViews;
  std::vector<InstantiationView> InstantiationSubViews;

  /// \brief Render a source line with highlighting.
  void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
                  const coverage::CoverageSegment *WrappedSegment,
                  ArrayRef<const coverage::CoverageSegment *> Segments,
                  unsigned ExpansionCol);

  void renderIndent(raw_ostream &OS, unsigned Level);

  void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);

  /// \brief Render the line's execution count column.
  void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);

  /// \brief Render the line number column.
  void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);

  /// \brief Render all the region's execution counts on a line.
  void
  renderRegionMarkers(raw_ostream &OS,
                      ArrayRef<const coverage::CoverageSegment *> Segments);

  static const unsigned LineCoverageColumnWidth = 7;
  static const unsigned LineNumberColumnWidth = 5;

public:
  SourceCoverageView(const MemoryBuffer &File,
                     const CoverageViewOptions &Options,
                     coverage::CoverageData &&CoverageInfo)
      : File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {}

  const CoverageViewOptions &getOptions() const { return Options; }

  /// \brief Add an expansion subview to this view.
  void addExpansion(const coverage::CounterMappingRegion &Region,
                    std::unique_ptr<SourceCoverageView> View) {
    ExpansionSubViews.emplace_back(Region, std::move(View));
  }

  /// \brief Add a function instantiation subview to this view.
  void addInstantiation(StringRef FunctionName, unsigned Line,
                        std::unique_ptr<SourceCoverageView> View) {
    InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
  }

  /// \brief Print the code coverage information for a specific
  /// portion of a source file to the output stream.
  void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0);
};

} // namespace llvm

#endif // LLVM_COV_SOURCECOVERAGEVIEW_H
