// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/flow/layers/performance_overlay_layer.h"

#include <iomanip>
#include <iostream>
#include <memory>
#include <string>

#include "display_list/dl_text_skia.h"
#include "flow/stopwatch.h"
#include "flow/stopwatch_dl.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "txt/platform.h"
#ifdef IMPELLER_SUPPORTS_RENDERING
#include "impeller/display_list/dl_text_impeller.h"              // nogncheck
#include "impeller/typographer/backends/skia/text_frame_skia.h"  // nogncheck
#endif  // IMPELLER_SUPPORTS_RENDERING

namespace flutter {
namespace {

void VisualizeStopWatch(DlCanvas* canvas,
                        const bool impeller_enabled,
                        const Stopwatch& stopwatch,
                        SkScalar x,
                        SkScalar y,
                        SkScalar width,
                        SkScalar height,
                        bool show_graph,
                        bool show_labels,
                        const std::string& label_prefix,
                        std::vector<DlPoint>& point_storage,
                        std::vector<DlColor>& color_storage,
                        const SkFont& font) {
  const int label_x = 8;    // distance from x
  const int label_y = -10;  // distance from y+height

  if (show_graph) {
    DlRect visualization_rect = DlRect::MakeXYWH(x, y, width, height);
    DlStopwatchVisualizer(stopwatch, point_storage, color_storage)
        .Visualize(canvas, visualization_rect);
  }

  if (show_labels) {
    auto text = PerformanceOverlayLayer::MakeStatisticsText(stopwatch, font,
                                                            label_prefix);
    // Historically SK_ColorGRAY (== 0xFF888888) was used here
    DlPaint paint(DlColor(0xFF888888));
#ifdef IMPELLER_SUPPORTS_RENDERING
    if (impeller_enabled) {
      canvas->DrawText(
          DlTextImpeller::Make(impeller::MakeTextFrameFromTextBlobSkia(text)),
          x + label_x, y + height + label_y, paint);
      return;
    }
#endif  // IMPELLER_SUPPORTS_RENDERING
    canvas->DrawText(DlTextSkia::Make(text), x + label_x, y + height + label_y,
                     paint);
  }
}

}  // namespace

// static
SkFont PerformanceOverlayLayer::MakeStatisticsFont(std::string_view font_path) {
  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
  if (font_path == "") {
    if (sk_sp<SkTypeface> face = font_mgr->matchFamilyStyle(nullptr, {})) {
      return SkFont(face, 15);
    } else {
      // In Skia's Android fontmgr, matchFamilyStyle can return null instead
      // of falling back to a default typeface. If that's the case, we can use
      // legacyMakeTypeface, which *does* use that default typeface.
      return SkFont(font_mgr->legacyMakeTypeface(nullptr, {}), 15);
    }
  } else {
    return SkFont(font_mgr->makeFromFile(font_path.data()), 15);
  }
}

// static
sk_sp<SkTextBlob> PerformanceOverlayLayer::MakeStatisticsText(
    const Stopwatch& stopwatch,
    const SkFont& font,
    std::string_view label_prefix) {
  // Make sure there's not an empty typeface returned, or we won't see any text.
  FML_DCHECK(font.getTypeface()->countGlyphs() > 0);

  double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF();
  double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF();
  std::stringstream stream;
  stream.setf(std::ios::fixed | std::ios::showpoint);
  stream << std::setprecision(1);
  stream << label_prefix << "  " << "max " << max_ms_per_frame << " ms/frame, "
         << "avg " << average_ms_per_frame << " ms/frame";
  auto text = stream.str();
  return SkTextBlob::MakeFromText(text.c_str(), text.size(), font,
                                  SkTextEncoding::kUTF8);
}

PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options,
                                                 const char* font_path)
    : options_(options) {
  if (font_path != nullptr) {
    font_path_ = font_path;
  }
}

void PerformanceOverlayLayer::Diff(DiffContext* context,
                                   const Layer* old_layer) {
  DiffContext::AutoSubtreeRestore subtree(context);
  if (!context->IsSubtreeDirty()) {
    FML_DCHECK(old_layer);
    auto prev = old_layer->as_performance_overlay_layer();
    context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(prev));
  }
  context->AddLayerBounds(paint_bounds());
  context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
}

void PerformanceOverlayLayer::Paint(PaintContext& context) const {
  const int padding = 8;

  if (!options_) {
    return;
  }

  DlScalar x = paint_bounds().GetX() + padding;
  DlScalar y = paint_bounds().GetY() + padding;
  DlScalar width = paint_bounds().GetWidth() - (padding * 2);
  DlScalar height = paint_bounds().GetHeight() / 2;
  auto mutator = context.state_stack.save();
  // Cached storage for vertex output.
  std::vector<DlPoint> vertices_storage;
  std::vector<DlColor> color_storage;
  SkFont font = MakeStatisticsFont(font_path_);

  VisualizeStopWatch(context.canvas, context.impeller_enabled,
                     context.raster_time, x, y, width, height - padding,
                     options_ & kVisualizeRasterizerStatistics,
                     options_ & kDisplayRasterizerStatistics, "Raster",
                     vertices_storage, color_storage, font);

  VisualizeStopWatch(context.canvas, context.impeller_enabled, context.ui_time,
                     x, y + height, width, height - padding,
                     options_ & kVisualizeEngineStatistics,
                     options_ & kDisplayEngineStatistics, "UI",
                     vertices_storage, color_storage, font);
}

}  // namespace flutter
