// Copyright (c) 2021 Giorgos Vougioukas
//
// The license can be found in the LICENSE file.

#include "RSI/track_wnd.h"
#include "RSI/waveform_wnd.h"
#include "RSI/track.h"
#include "RSI/skin.h"

#include "WDL/fpcmp.h"
#include "WDL/db2val.h"

static LOGFONT lf =
{
#if defined(_WIN32)
  14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
  OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,
  "Arial"
#else
  11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
  OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,
  "Arial"
#endif
};

static LOGFONT biglf =
{
#if defined(_WIN32)
  20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
  OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,
  "Arial"
#else
  17, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
  OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,
  "Arial"
#endif
};

RSI_TrackWnd::RSI_TrackWnd()
  : m_hwnd(NULL)
  , m_bm(NULL)
  , m_font(NULL)
  , m_bigfont(NULL)
  , m_buttonfont(NULL)
  , m_trkidx(0)
  , m_sltidx(0)
  , m_wfa(NULL)
  , m_wfb(NULL)
{
#if _WIN32
  WNDCLASS wc = { CS_DBLCLKS, };
  wc.lpfnWndProc = (WNDPROC)RSI_WaveformAWnd::WaveformAWndProc;
  wc.hInstance = g_inst;
  wc.hCursor = LoadCursor(NULL,IDC_ARROW);
  wc.lpszClassName = "waveforma_superhost";
  RegisterClass(&wc);

  wc.lpfnWndProc = (WNDPROC)RSI_WaveformBWnd::WaveformBWndProc;
  wc.hInstance = g_inst;
  wc.hCursor = LoadCursor(NULL,IDC_ARROW);
  wc.lpszClassName = "waveformb_superhost";
  RegisterClass(&wc);
#endif

  m_bgc = g_preferences->GetBackgroundColor();
  m_txtc = g_preferences->GetTextColor();
}

RSI_TrackWnd::~RSI_TrackWnd()
{
  DestroyWindow(m_hwnd);

  //if (m_bm) delete m_bm;
  if (m_font) delete m_font;
  if (m_bigfont) delete m_bigfont;
  if (m_buttonfont) delete m_buttonfont;

#if _WIN32
  UnregisterClass("waveforma_superhost", g_inst);
  UnregisterClass("waveformb_superhost", g_inst);
#endif
}

void RSI_TrackWnd::UpdateScreenRegions()
{
  int acttrk = g_rendersystem->GetActiveTrack();
  if (acttrk == m_trkidx)
  {
    m_label.SetColors(LICE_RGBA(255, 0, 0, 255));
  }
  else
  {
    m_label.SetColors(LICE_RGBA(128, 128, 255, 255));
  }

  InvalidateRect(m_wfa, NULL, FALSE);
  InvalidateRect(m_wfb, NULL, FALSE);

  RSI_Track *trk = g_tracks.Get(m_trkidx, NULL);
  if (trk)
  {
    m_title.SetText(trk->GetTitle());
    m_artist.SetText(trk->GetArtist());

    int curmin, cursec, curms, totmin, totsec;
    double curtime = trk->GetTime();
    double tottime = trk->GetTotalTime();
    curmin = (int)curtime / 60;
    cursec = (int)curtime % 60;
    curms = (int)((curtime - (int)curtime) * 1000);
    totmin = (int)tottime / 60;
    totsec = (int)tottime % 60;
    m_strbuf.SetFormatted(256, "%01d:%02d.%03d / %01d:%02d",
      curmin, cursec, curms, totmin, totsec);
    m_time.SetText(m_strbuf.Get());

    double vol = trk->GetVolume();
    double sht = trk->GetShift();
    double tmp = trk->GetTempo();
    bool rpt = trk->IsRepeat();
    bool hp = trk->IsHeadphone();
    if (WDL_DefinitelyGreaterThan(vol, -150.0))
    {
      m_strbuf.SetFormatted(256,
        "VOL: %.2f dB, PS: %.2f, TS: %.2f, RPT: %s, HP: %s",
        vol, sht, tmp, rpt ? "On" : "Off", hp ? "On" : "Off");
    }
    else
    {
      m_strbuf.SetFormatted(256,
        "VOL: -inf dB, PS: %.2f, TS: %.2f, RPT: %s, HP: %s",
        sht, tmp, rpt ? "On" : "Off", hp ? "On" : "Off");
    }
    m_info.SetText(m_strbuf.Get());

    bool ispaused = trk->IsPaused();
    if (ispaused) m_pause.SetCheckState(1);
    else m_pause.SetCheckState(0);

    bool isrewind = trk->IsRewind();
    if (isrewind) m_rewind.SetCheckState(1);
    else m_rewind.SetCheckState(0);

    bool isfastforward = trk->IsFastForward();
    if (isfastforward) m_fastforward.SetCheckState(1);
    else m_fastforward.SetCheckState(0);

    if (trk->IsActive())
    {
      m_play.SetCheckState(1);
      m_load.SetEnabled(false);
    }
    else
    {
      m_play.SetCheckState(0);
      m_load.SetEnabled(true);
    }

    m_pause.SetEnabled(true);
    m_rewind.SetEnabled(true);
    m_fastforward.SetEnabled(true);
    m_play.SetEnabled(true);
    m_stop.SetEnabled(true);
  }
  else
  {
    m_title.SetText("");
    m_artist.SetText("");
    m_time.SetText("");
    m_info.SetText("");

    m_pause.SetEnabled(false);
    m_rewind.SetEnabled(false);
    m_fastforward.SetEnabled(false);
    m_play.SetEnabled(false);
    m_stop.SetEnabled(false);

    m_ebur128.SetText("");
    m_ebur128.SetVisible(false);
  }

  RSI_DataBankSlot *dbs = g_databank->GetSlot(m_sltidx);
  if (trk && dbs)
  {
    if (dbs->IsEBUR128Enabled())
    {
      int ebur128mode = dbs->GetEBUR128Mode();

      m_strbuf.SetFormatted(512, "Reference: %.0f LUFS", dbs->GetEBUR128Reference());

      if (WDL_DefinitelyGreaterThan(dbs->GetEBUR128Gain(), -150.0))
      {
        m_strbuf.AppendFormatted(512, ", Gain: %.2f dB\n\n", dbs->GetEBUR128Gain());
      }

      RECT r;
      m_ebur128.GetPosition(&r);

      POINT pt;
      GetCursorPos(&pt);
      ScreenToClient(m_hwnd, &pt);

      if (pt.x >= r.left && pt.x <= r.right && pt.y >= r.top && pt.y <= r.bottom)
      {
        if ((ebur128mode & EBUR128_MODE_M) == EBUR128_MODE_M)
        {
          if (WDL_DefinitelyGreaterThan(dbs->GetEBUR128Momentary(), -150.0))
          {
            m_strbuf.AppendFormatted(512, " Momentary (M): %.2f LUFS\n", dbs->GetEBUR128Momentary());
          }
        }

        if ((ebur128mode & EBUR128_MODE_S) == EBUR128_MODE_S)
        {
          if (WDL_DefinitelyGreaterThan(dbs->GetEBUR128Shortterm(), -150.0))
          {
            m_strbuf.AppendFormatted(512, " Shortterm (S): %.2f LUFS\n", dbs->GetEBUR128Shortterm());
          }
        }

        if ((ebur128mode & EBUR128_MODE_I) == EBUR128_MODE_I)
        {
          if (WDL_DefinitelyGreaterThan(dbs->GetEBUR128Integrated(), -150.0))
          {
            m_strbuf.AppendFormatted(512, " Integrated (I): %.2f LUFS\n", dbs->GetEBUR128Integrated());
          }
        }

        if ((ebur128mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA)
        {
          if (WDL_DefinitelyGreaterThan(dbs->GetEBUR128Range(), -150.0))
          {
            m_strbuf.AppendFormatted(512, " LRA: %.2f LU\n", dbs->GetEBUR128Range());
          }
        }

        if ((ebur128mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK)
        {
          m_strbuf.AppendFormatted(512, " Sample peak: %.6f (%.2f dBFS)\n", dbs->GetEBUR128SamplePeak(),
            VAL2DB(dbs->GetEBUR128SamplePeak()));
        }

        if ((ebur128mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK)
        {
          m_strbuf.AppendFormatted(512, " True peak: %.6f (%.2f dBTP)\n", dbs->GetEBUR128TruePeak(),
            VAL2DB(dbs->GetEBUR128TruePeak()));
        }

        m_strbuf.AppendFormatted(512, " Normalization: %s\n",
          dbs->IsEBUR128DowndwardOnly() ? "Downward-only" : "Downward/upward");

        m_ebur128.SetText(m_strbuf.Get());
        m_ebur128.SetVisible(true);
      }
      else
      {
        m_ebur128.SetText(m_strbuf.Get());
        m_ebur128.SetVisible(true);
      }
    }
  }

  for (int i = 0; i < m_vwnd.GetNumChildren(); i++)
  {
    m_vwnd.EnumChildren(i)->RequestRedraw(NULL);
  }
}

void RSI_TrackWnd::OnInitDialog()
{
#ifndef _WIN32
  SWELL_SetClassName(m_hwnd, "track_superhost");

  HWND ha = CreateDialog(NULL, 0, m_hwnd, (DLGPROC)RSI_WaveformAWnd::WaveformAWndProc);
  if (ha)
  {
    SWELL_SetClassName(ha, "waveforma_superhost");
    ShowWindow(ha, SW_SHOWNA);
  }

  HWND hb = CreateDialog(NULL, 0, m_hwnd, (DLGPROC)RSI_WaveformBWnd::WaveformBWndProc);
  if (hb)
  {
    SWELL_SetClassName(hb, "waveformb_superhost");
    ShowWindow(hb, SW_SHOWNA);
  }

  m_wfa = ha;
  m_wfb = hb;
#endif

#if _WIN32
  m_wfa = GetDlgItem(m_hwnd, IDC_CUSTOM1);
  m_wfb = GetDlgItem(m_hwnd, IDC_CUSTOM2);
#endif

  WDL_ASSERT(g_trackwnds.GetSize());
  m_trkidx = g_trackwnds.GetSize();
  m_sltidx = g_trackwnds.GetSize() - 1;

  SetWindowPos(m_hwnd, NULL, 0, 0, 230, 230, SWP_NOZORDER | SWP_NOACTIVATE);

  RECT r, wa, wb;
  GetClientRect(m_hwnd, &r);

  if (!m_font) m_font = new LICE_CachedFont;
  if (!m_font->GetHFont())
  {
    m_font->SetFromHFont(CreateFontIndirect(&lf), LICE_FONT_FLAG_OWNS_HFONT);
    m_font->SetTextColor(LICE_RGBA(0, 255, 255, 255));
    m_font->SetBkMode(TRANSPARENT);
  }

  m_contextmenu = (HMENU)GetSubMenu(LoadMenu(g_inst, MAKEINTRESOURCE(IDR_TRACK_CONTEXT)), 0);

  if (!m_bigfont) m_bigfont = new LICE_CachedFont;
  if (!m_bigfont->GetHFont())
  {
    m_bigfont->SetFromHFont(CreateFontIndirect(&biglf), LICE_FONT_FLAG_OWNS_HFONT);
    m_bigfont->SetTextColor(LICE_RGBA(0, 255, 255, 255));
    m_bigfont->SetBkMode(TRANSPARENT);
  }

  if (!m_buttonfont) m_buttonfont = new LICE_CachedFont;
  if (!m_buttonfont->GetHFont())
  {
    m_buttonfont->SetFromHFont(CreateFontIndirect(&lf), LICE_FONT_FLAG_OWNS_HFONT);
    m_buttonfont->SetTextColor(LICE_RGBA(0, 255, 255, 255));
    m_buttonfont->SetBkMode(TRANSPARENT);
  }

  m_strbuf.SetFormatted(512, "F%d", m_trkidx);

  int h = 50, tbm = 20, lrm = 5; // height, top-bottom margin, left-right margin

  RECT reclbl;
  reclbl.left = 0 + lrm;
  reclbl.top = lrm;
  reclbl.right = r.right / 2 - (lrm * 2);
  reclbl.bottom = reclbl.top + tbm;
  m_label.SetPosition(&reclbl);
  m_label.SetFont(m_bigfont);
  m_label.SetID(TRK_LABEL);
  m_label.SetRealParent(m_hwnd);
  m_label.SetColors(LICE_RGBA(128, 128, 255, 255));
  m_label.SetText(m_strbuf.Get());

  RECT rectit;
  rectit.left = 0 + lrm;
  rectit.top = reclbl.bottom;
  rectit.right = r.right - lrm;
  rectit.bottom = rectit.top + tbm;
  m_title.SetPosition(&rectit);
  m_title.SetFont(m_font);
  m_title.SetID(TRK_TITLE);
  m_title.SetRealParent(m_hwnd);
  m_title.SetColors(LICE_RGBA(210, 210, 210, 255));
  m_title.SetText("");

  RECT recart;
  recart.left = 0 + lrm;
  recart.top = rectit.bottom;
  recart.right = r.right - lrm;
  recart.bottom = recart.top + tbm;
  m_artist.SetPosition(&recart);
  m_artist.SetFont(m_font);
  m_artist.SetID(TRK_ARTIST);
  m_artist.SetRealParent(m_hwnd);
  m_artist.SetColors(LICE_RGBA(210, 210, 210, 255));
  m_artist.SetText("");

  RECT rectime;
  rectime.left = 0 + lrm;
  rectime.top = recart.bottom;
  rectime.right = r.right;
  rectime.bottom = rectime.top + tbm;
  m_time.SetPosition(&rectime);
  m_time.SetFont(m_bigfont);
  m_time.SetID(TRK_TIME);
  m_time.SetRealParent(m_hwnd);
  m_time.SetColors(LICE_RGBA(210, 210, 210, 255));
  m_time.SetAlign(0);
  m_time.SetText("");

  GetWindowRect(m_wfa, &wa);
  ScreenToClient(m_hwnd, (LPPOINT)&wa);
  ScreenToClient(m_hwnd, ((LPPOINT)&wa)+1);
  GetWindowRect(m_wfb, &wb);
  ScreenToClient(m_hwnd, (LPPOINT)&wb);
  ScreenToClient(m_hwnd, ((LPPOINT)&wb)+1);

  RECT reca;
  reca.left = r.left + lrm;
  reca.top = rectime.bottom;
  reca.right = r.right - r.left - (lrm * 2);
  reca.bottom = reca.top + h;
  SetWindowPos(m_wfa, NULL, reca.left, reca.top,
    reca.right - reca.left, reca.bottom - reca.top,
    SWP_NOZORDER | SWP_NOACTIVATE);

  RECT recb;
  recb.left = r.left + lrm;
  recb.top = reca.bottom;
  recb.right = r.right - r.left - (lrm * 2);
  recb.bottom = recb.top + h;
  SetWindowPos(m_wfb, NULL, recb.left, recb.top,
    recb.right - recb.left, recb.bottom - recb.top,
    SWP_NOZORDER | SWP_NOACTIVATE);

  RECT recinf;
  recinf.left = 0 + lrm;
  recinf.top = recb.bottom;
  recinf.right = r.right;
  recinf.bottom = recinf.top + tbm;
  m_info.SetPosition(&recinf);
  m_info.SetFont(m_font);
  m_info.SetID(TRK_INFO);
  m_info.SetRealParent(m_hwnd);
  m_info.SetColors(LICE_RGBA(210, 210, 210, 255));
  m_info.SetAlign(0);
  m_info.SetText("");

  RECT recebur128;
  recebur128.left = 0 + lrm;
  recebur128.top = recinf.bottom;
  recebur128.right = r.right;
  recebur128.bottom = recebur128.top + tbm;
  m_ebur128.SetPosition(&recebur128);
  m_ebur128.SetFont(m_font);
  m_ebur128.SetID(TRK_EBUR128);
  m_ebur128.SetRealParent(m_hwnd);
  m_ebur128.SetColors(LICE_RGBA(210, 210, 210, 255));
  m_ebur128.SetAlign(0);
  m_ebur128.SetText("");

  RECT recp;
  recp.left = r.left + lrm;
  recp.top = recebur128.bottom + tbm;
  recp.right = recp.left + 40;
  recp.bottom = recp.top + 20;
  m_pause.SetPosition(&recp);
  m_pause.SetFont(m_buttonfont);
  m_pause.SetID(TRK_PAUSE);
  m_pause.SetRealParent(m_hwnd);
  m_pause.SetTextLabel("PAUSE");
  m_pause.SetForceText(true, LICE_RGBA(210, 210, 210, 255));
  m_pause.SetIcon(&g_skin.icon_button_skin_config);
  m_pause.SetForceBorder(true);
  //m_pause.SetVisible(true);
  //m_pause.SetEnabled(true);
  //m_pause.SetIsButton(true);

  RECT recrwd;
  recrwd.left = recp.right + lrm;
  recrwd.top = recp.top;
  recrwd.right = recrwd.left + 40;
  recrwd.bottom = recrwd.top + 20;
  m_rewind.SetPosition(&recrwd);
  m_rewind.SetFont(m_buttonfont);
  m_rewind.SetID(TRK_REWIND);
  m_rewind.SetRealParent(m_hwnd);
  m_rewind.SetTextLabel("<");
  m_rewind.SetForceText(true, LICE_RGBA(210, 210, 210, 255));
  m_rewind.SetIcon(&g_skin.icon_button_skin_config);
  m_rewind.SetForceBorder(true);

  RECT recfwd;
  recfwd.left = recrwd.right + lrm;
  recfwd.top = recrwd.top;
  recfwd.right = recfwd.left + 40;
  recfwd.bottom = recfwd.top + 20;
  m_fastforward.SetPosition(&recfwd);
  m_fastforward.SetFont(m_buttonfont);
  m_fastforward.SetID(TRK_FASTFORWARD);
  m_fastforward.SetRealParent(m_hwnd);
  m_fastforward.SetTextLabel(">");
  m_fastforward.SetForceText(true, LICE_RGBA(210, 210, 210, 255));
  m_fastforward.SetIcon(&g_skin.icon_button_skin_config);
  m_fastforward.SetForceBorder(true);

  RECT recply;
  recply.left = recfwd.right + lrm;
  recply.top = recfwd.top;
  recply.right = recply.left + 40;
  recply.bottom = recply.top + 20;
  m_play.SetPosition(&recply);
  m_play.SetFont(m_buttonfont);
  m_play.SetID(TRK_PLAY);
  m_play.SetRealParent(m_hwnd);
  m_play.SetTextLabel("PLAY");
  m_play.SetForceText(true, LICE_RGBA(210, 210, 210, 255));
  m_play.SetIcon(&g_skin.icon_button_skin_config);
  m_play.SetForceBorder(true);

  RECT recstp;
  recstp.left = recply.right + lrm;
  recstp.top = recply.top;
  recstp.right = recstp.left + 40;
  recstp.bottom = recstp.top + 20;
  m_stop.SetPosition(&recstp);
  m_stop.SetFont(m_buttonfont);
  m_stop.SetID(TRK_STOP);
  m_stop.SetRealParent(m_hwnd);
  m_stop.SetTextLabel("STOP");
  m_stop.SetForceText(true, LICE_RGBA(210, 210, 210, 255));
  m_stop.SetIcon(&g_skin.icon_button_skin_config);
  m_stop.SetForceBorder(true);

  RECT recld;
  recld.right = r.right - lrm;
  recld.left = recld.right - 40;
  recld.top = recstp.top;
  recld.bottom = recld.top + 20;
  m_load.SetPosition(&recld);
  m_load.SetFont(m_buttonfont);
  m_load.SetID(TRK_STOP);
  m_load.SetRealParent(m_hwnd);
  m_load.SetTextLabel("LOAD");
  m_load.SetForceText(true, LICE_RGBA(210, 210, 210, 255));
  m_load.SetIcon(&g_skin.icon_button_skin_config);
  m_load.SetForceBorder(true);

  m_vwnd.SetPosition(&r);
  m_vwnd.SetRealParent(m_hwnd);
  m_vwnd.AddChild(&m_label);
  m_vwnd.AddChild(&m_title);
  m_vwnd.AddChild(&m_artist);
  m_vwnd.AddChild(&m_time);
  m_vwnd.AddChild(&m_info);
  m_vwnd.AddChild(&m_ebur128);
  m_vwnd.AddChild(&m_pause);
  m_vwnd.AddChild(&m_rewind);
  m_vwnd.AddChild(&m_fastforward);
  m_vwnd.AddChild(&m_play);
  m_vwnd.AddChild(&m_stop);
  m_vwnd.AddChild(&m_load);
  m_vwnd.RequestRedraw(NULL);

  InvalidateRect(m_hwnd, NULL, FALSE);

  m_resize.init(m_hwnd);
  m_resize.init_itemhwnd(m_wfa, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemhwnd(m_wfb, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemvirt(&m_vwnd, 0.0f, 0.0f, 1.0f, 1.0f);
  m_resize.init_itemvirt(&m_label, 0.0f, 0.0f, 0.5f, 0.0f);
  m_resize.init_itemvirt(&m_title, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemvirt(&m_artist, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemvirt(&m_time, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemvirt(&m_info, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemvirt(&m_ebur128, 0.0f, 0.0f, 1.0f, 0.0f);
  m_resize.init_itemvirt(&m_pause, 0.0f, 0.0f, 0.0f, 0.0f);
  m_resize.init_itemvirt(&m_rewind, 0.0f, 0.0f, 0.0f, 0.0f);
  m_resize.init_itemvirt(&m_fastforward, 0.0f, 0.0f, 0.0f, 0.0f);
  m_resize.init_itemvirt(&m_play, 0.0f, 0.0f, 0.0f, 0.0f);
  m_resize.init_itemvirt(&m_stop, 0.0f, 0.0f, 0.0f, 0.0f);
  m_resize.init_itemvirt(&m_load, 1.0f, 0.0f, 1.0f, 0.0f);
}

void RSI_TrackWnd::OnDestroy()
{
  m_vwnd.RemoveAllChildren(false);
}

void RSI_TrackWnd::OnPaint()
{
  RECT r;
  GetClientRect(m_hwnd, &r);
  //m_vwnd.SetPosition(&r);

  int w = r.right - r.left;
  int h = r.bottom - r.top;

  int xo, yo;
  m_painter.PaintBegin(m_hwnd, RGB(51, 51, 51));
  m_bm = m_painter.GetBuffer(&xo, &yo);
  //LICE_Clear(m_bm, LICE_RGBA(255, 255, 255, 255));
  //LICE_Clear(m_bm, LICE_RGBA((m_bgc >> 16) & 0xFF,
  //  (m_bgc >> 8) & 0xFF, m_bgc & 0xFF, 255));
  //LICE_Line(m_bm, w/2, 0, w/2, h, LICE_RGBA(255, 255, 128, 255), 1.0f, LICE_BLIT_MODE_ADD);
  //LICE_Line(m_bm, 0, h/2, w, h/2, LICE_RGBA(255, 255, 128, 255), 1.0f, LICE_BLIT_MODE_ADD);

  //WDL_VirtualWnd_ScaledBlitBG(m_bm, &g_skin.trackwnd_bg_config,
  //  0, 0, w, h, 0, 0, w, h, 1.0f, LICE_BLIT_MODE_COPY);
  m_painter.PaintVirtWnd(&m_vwnd);
  m_painter.PaintEnd();
}

void RSI_TrackWnd::OnLButtonDown()
{
  g_rendersystem->SetActiveTrack(m_trkidx);
  if (m_vwnd.OnMouseDown(GET_X_LPARAM(m_lparam), GET_Y_LPARAM(m_lparam)) > 0)
  {
    SetCapture(m_hwnd);
  }
}

void RSI_TrackWnd::OnLButtonUp()
{
  if (GetCapture() == m_hwnd)
  {
    m_vwnd.OnMouseUp(GET_X_LPARAM(m_lparam), GET_Y_LPARAM(m_lparam));
    ReleaseCapture();
  }
}

void RSI_TrackWnd::OnLButtonDblClk()
{
  m_vwnd.OnMouseDblClick(GET_X_LPARAM(m_lparam), GET_Y_LPARAM(m_lparam));
}

void RSI_TrackWnd::OnMouseMove()
{
  m_vwnd.OnMouseMove(GET_X_LPARAM(m_lparam), GET_Y_LPARAM(m_lparam));
}

void RSI_TrackWnd::OnSize()
{
  if (m_wparam != SIZE_MINIMIZED)
  {
    m_resize.onResize();

    RECT recstp, recld;
    m_stop.GetPosition(&recstp);
    m_load.GetPosition(&recld);
    if (recld.left <= recstp.right)
    {
      m_load.SetVisible(false);
    }
    else { m_load.SetVisible(true); }

    if (g_mainwnd->IsLibraryFull())
    {
      RECT reclbl, rectit, rectim;
      m_label.GetPosition(&reclbl);
      m_title.GetPosition(&rectit);
      m_time.GetPosition(&rectim);
      rectim.left = reclbl.right;
      rectim.top = reclbl.top;
      rectim.right = rectit.right;
      rectim.bottom = reclbl.bottom;
      m_time.SetPosition(&rectim);
      m_time.SetAlign(1);
    }
    else { m_time.SetAlign(0); }
  }

  //InvalidateRect(m_hwnd, NULL, FALSE);
}

void RSI_TrackWnd::OnTimer()
{}

void RSI_TrackWnd::OnContextMenu()
{
  POINT pt;
  pt.x = GET_X_LPARAM(m_lparam);
  pt.y = GET_Y_LPARAM(m_lparam);
  ScreenToClient(m_hwnd, &pt);

  RECT r;
  m_label.GetPosition(&r);

  if (pt.x >= r.left && pt.y >= r.top && pt.x <= r.right && pt.y <= r.bottom)
  {
    GetCursorPos(&pt);
  }
  else
  {
    return;
  }

  g_rendersystem->SetActiveTrack(m_trkidx);

  RSI_Track *trk = g_tracks.Get(m_trkidx);
  if (trk && !trk->IsActive())
  {
    EnableMenuItem(m_contextmenu, ID_TRACK_EJECTTRACK, MF_ENABLED);
  }
  else
  {
    EnableMenuItem(m_contextmenu, ID_TRACK_EJECTTRACK, MF_GRAYED);
  }

  if (trk && trk->IsActive())
  {
    EnableMenuItem(m_contextmenu, ID_TRACK_LOADTRACK, MF_GRAYED);
  }
  else
  {
    EnableMenuItem(m_contextmenu, ID_TRACK_LOADTRACK, MF_ENABLED);
  }

  if (trk)
  {
    EnableMenuItem(m_contextmenu, ID_TRACK_PAUSE, MF_ENABLED);
    EnableMenuItem(m_contextmenu, ID_TRACK_PLAY, MF_ENABLED);
    EnableMenuItem(m_contextmenu, ID_TRACK_STOP, MF_ENABLED);
    EnableMenuItem(m_contextmenu, ID_TRACK_HEADPHONES, MF_ENABLED);
    EnableMenuItem(m_contextmenu, ID_TRACK_PLAYREVERSE, MF_ENABLED);
    EnableMenuItem(m_contextmenu, ID_TRACK_REVERSEPLAYBACK, MF_ENABLED);
  }
  else
  {
    EnableMenuItem(m_contextmenu, ID_TRACK_PAUSE, MF_GRAYED);
    EnableMenuItem(m_contextmenu, ID_TRACK_PLAY, MF_GRAYED);
    EnableMenuItem(m_contextmenu, ID_TRACK_STOP, MF_GRAYED);
    EnableMenuItem(m_contextmenu, ID_TRACK_HEADPHONES, MF_GRAYED);
    EnableMenuItem(m_contextmenu, ID_TRACK_PLAYREVERSE, MF_GRAYED);
    EnableMenuItem(m_contextmenu, ID_TRACK_REVERSEPLAYBACK, MF_GRAYED);
  }

  TrackPopupMenu(m_contextmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, m_hwnd, NULL);
}

void RSI_TrackWnd::OnCommand()
{
  switch (LOWORD(m_wparam))
  {
    case ID_TRACK_LOADTRACK: SendMessage(g_mainwnd->Handle(), WM_COMMAND, ID_FILE_LOADTRACK, 0); break;
    case ID_TRACK_EJECTTRACK: g_rendersystem->EjectTrack(m_trkidx); break;
    case ID_TRACK_PAUSE: g_rendersystem->Pause(); break;
    case ID_TRACK_PLAY: g_rendersystem->Play(); break;
    case ID_TRACK_STOP: g_rendersystem->Stop(); break;
    case ID_TRACK_HEADPHONES: g_rendersystem->ToggleHeadphone(); break;
    case ID_TRACK_PLAYREVERSE: g_rendersystem->PlayReverse(); break;
    case ID_TRACK_REVERSEPLAYBACK: g_rendersystem->ToggleReversePlayback(); break;
  }
}

WDL_DLGRET RSI_TrackWnd::TrackWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  RSI_TrackWnd *self = (RSI_TrackWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA);

  if (!self && msg == WM_INITDIALOG)
  {
    SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
    self = (RSI_TrackWnd *)lparam;
    self->m_hwnd = hwnd;
  }

  if (WDL_likely(self))
  {
    return self->TrackWndLoop(msg, wparam, lparam);
  }
  else
  {
    return 0;
  }
}

WDL_DLGRET RSI_TrackWnd::TrackWndLoop(UINT msg, WPARAM wparam, LPARAM lparam)
{
  m_msg = msg;
  m_wparam = wparam;
  m_lparam = lparam;

  switch (msg)
  {
    case WM_INITDIALOG: OnInitDialog(); break;
    case WM_DESTROY: OnDestroy(); break;
    case WM_PAINT: OnPaint(); break;
    case WM_LBUTTONDOWN: OnLButtonDown(); break;
    case WM_LBUTTONUP: OnLButtonUp(); break;
    case WM_LBUTTONDBLCLK: OnLButtonDblClk(); break;
    case WM_MOUSEMOVE: OnMouseMove(); break;
    case WM_SIZE: OnSize(); break;
    case WM_TIMER: OnTimer(); break;
    case WM_ERASEBKGND: return 1; break;
    case WM_CONTEXTMENU: OnContextMenu(); break;
    case WM_COMMAND: OnCommand(); break;
  }

  return 0;
}
