//+----------------------------------------------------------------------------+
//| Description:  Magic Set Editor - Program to make Magic (tm) cards          |
//| Copyright:    (C) Twan van Laarhoven and the other MSE developers          |
//| License:      GNU General Public License 2 or later (see file COPYING)     |
//+----------------------------------------------------------------------------+

// ----------------------------------------------------------------------------- : Includes

#include <util/prec.hpp>
#include <script/profiler.hpp>
#include <wx/dcbuffer.h>

#if USE_SCRIPT_PROFILING

// -----------------------------------------------------------------------------
// Profiler Panel : class
// -----------------------------------------------------------------------------

class ProfilerPanel : public wxPanel {
public:
  ProfilerPanel(wxWindow* parent, bool fancy_effects);
  ~ProfilerPanel();
  
  virtual bool AcceptsFocus() const { return false; }
  virtual wxSize DoGetBestSize() const;
  
private:
  bool        fancy_effects;
  wxTimer     timer;
  wxStopWatch stopwatch;
  
  typedef std::pair<int,long> NumCallsAndFadeTime;
  typedef std::map<FunctionProfile const*,NumCallsAndFadeTime> PrevProfiles;
  PrevProfiles prev_profiles;
  
  DECLARE_EVENT_TABLE();
  void onPaint(wxPaintEvent&);
  void onTimer(wxTimerEvent&);
  void onSize(wxSizeEvent&);
  void draw_profiler(wxDC& dc, int x, int y);
};

// -----------------------------------------------------------------------------
// Drawing utilities
// -----------------------------------------------------------------------------

void clear_dc(wxDC& dc, wxColour const& color) {
  dc.SetPen(*wxTRANSPARENT_PEN);
  dc.SetBrush(color);
  wxSize s = dc.GetSize();
  dc.DrawRectangle(0,0,s.x,s.y);
}

void draw_right(wxDC& dc, wxString const& text, int x, int y) {
  int w, h;
  dc.GetTextExtent(text, &w, &h);
  dc.DrawText(text, x-w, y);
}

// -----------------------------------------------------------------------------
// Profiler Panel
// -----------------------------------------------------------------------------

ProfilerPanel::ProfilerPanel(wxWindow* parent, bool fancy_effects)
  : wxPanel(parent, wxID_ANY)
  , fancy_effects(fancy_effects)
  , timer(this)
{
  SetBackgroundStyle(wxBG_STYLE_PAINT);
//  profiler::set_function_leave_callback(refresh_profiler_panel);
}

ProfilerPanel::~ProfilerPanel() {
//  profiler::set_function_leave_callback(nullptr);
}


void ProfilerPanel::onPaint(wxPaintEvent&) {
  #ifdef __WXMSW__
    wxBufferedPaintDC dc(this);
  #else
    wxPaintDC dc(this);
  #endif
  // clear background
  clear_dc(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
  // draw table
  dc.SetFont(*wxNORMAL_FONT);
  draw_profiler(dc, 0, 0);
}

void ProfilerPanel::draw_profiler(wxDC& dc, int x0, int y0) {
  #if USE_SCRIPT_PROFILING
    // Get the profiles
    const FunctionProfile& profile = profile_aggregated(1);
    vector<FunctionProfileP> profiles;
    profile.get_children(profiles);
    // set up colors
    wxColour fg(0,0,0);
    wxColour fg_highlight(0,20,220);
    dc.SetTextForeground(fg);
    dc.SetPen(fg);
    // set up positions/sizes
    int line_height = dc.GetCharHeight() + 2;
    int x1 = dc.GetSize().x - 2;
    int pos[] = {x0+2, x1-124, x1-84, x1-44, x1-4 };
    // fancy effects
    bool any_active = false;
    long now = stopwatch.Time();
    // Draw table
    dc.DrawText(_("Function"), pos[0], y0 + 2);
    draw_right(dc,_("calls"),  pos[1], y0 + 2);
    draw_right(dc,_("avg"),    pos[2], y0 + 2);
    draw_right(dc,_("total"),  pos[3], y0 + 2);
    draw_right(dc,_("max"),    pos[4], y0 + 2);
    dc.DrawLine(x0, y0 + line_height + 2, x1, y0 + line_height + 2);
    int i = 0;
    FOR_EACH_REVERSE(prof, profiles) {
      // recently changed?
      if (fancy_effects) {
        PrevProfiles::iterator prev = prev_profiles.find(prof.get());
        float active = 0.f;
        const int FADE_TIME = 1000;
        if (prev == prev_profiles.end()) {
          prev_profiles[prof.get()] = std::make_pair(prof->calls,0);
        } else if (prev->second.first != prof->calls) {
          prev->second = std::make_pair(prof->calls,now+FADE_TIME);
          any_active = true;
          active = 1.f;
          dc.SetTextForeground(*wxRED);
        } else if (now < prev->second.second) {
          active = (prev->second.second - now) / (float)FADE_TIME;
          any_active = true;
        }
        dc.SetTextForeground(lerp(fg,fg_highlight,active));
      }
      // draw line
      int y = y0 + (++i) * line_height + 6;
      dc.DrawText(prof->name,                                        pos[0], y);
      draw_right(dc,wxString::Format(_("%d"),   prof->calls),        pos[1], y);
      draw_right(dc,wxString::Format(_("%.2f"), prof->avg_time()),   pos[2], y);
      draw_right(dc,wxString::Format(_("%.2f"), prof->total_time()), pos[3], y);
      draw_right(dc,wxString::Format(_("%.2f"), prof->max_time()),   pos[4], y);
    }
    // are any fancy effects active?
    if (fancy_effects && any_active && !timer.IsRunning()) {
      timer.Start(40,wxTIMER_ONE_SHOT);
    }
//%    profiler_panel_refreshing = false;
  #endif
}

void ProfilerPanel::onTimer(wxTimerEvent&) {
  Refresh(false);
}

void ProfilerPanel::onSize(wxSizeEvent&) {
  Refresh(true);
}

wxSize ProfilerPanel::DoGetBestSize() const {
  return wxSize(300,500);
}

BEGIN_EVENT_TABLE(ProfilerPanel, wxPanel)
  EVT_PAINT(          ProfilerPanel::onPaint)
  EVT_SIZE(           ProfilerPanel::onSize)
  EVT_TIMER(wxID_ANY, ProfilerPanel::onTimer)
END_EVENT_TABLE()


void show_profiler_window(wxWindow* parent) {
  wxDialog* dlg = new wxDialog(parent, wxID_ANY, _("Profiler"), wxDefaultPosition,wxSize(450,600), wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER);
  wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
  sizer->Add(new ProfilerPanel(dlg,true), 1, wxEXPAND | wxALL, 8);
  sizer->Add(dlg->CreateButtonSizer(wxOK), 0, wxALIGN_CENTER | wxALL, 8);
  dlg->SetSizer(sizer);
  dlg->Show();
}

#endif
