// Filename: winStatsLabel.cxx
// Created by:  drose (07Jan04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "winStatsLabel.h"
#include "winStatsMonitor.h"
#include "winStatsGraph.h"

int WinStatsLabel::_left_margin = 2;
int WinStatsLabel::_right_margin = 2;
int WinStatsLabel::_top_margin = 2;
int WinStatsLabel::_bottom_margin = 2;

bool WinStatsLabel::_window_class_registered = false;
const char * const WinStatsLabel::_window_class_name = "label";

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
WinStatsLabel::
WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph,
              int thread_index, int collector_index, bool use_fullname) :
  _monitor(monitor),
  _graph(graph),
  _thread_index(thread_index),
  _collector_index(collector_index)
{
  _window = 0;
  if (use_fullname) {
    _text = _monitor->get_client_data()->get_collector_fullname(_collector_index);
  } else {
    _text = _monitor->get_client_data()->get_collector_name(_collector_index);
  }

  RGBColorf rgb = _monitor->get_collector_color(_collector_index);
  int r = (int)(rgb[0] * 255.0f);
  int g = (int)(rgb[1] * 255.0f);
  int b = (int)(rgb[2] * 255.0f);
  _bg_color = RGB(r, g, b);
  _bg_brush = CreateSolidBrush(RGB(r, g, b));

  // Should our foreground be black or white?
  float bright =
    rgb[0] * 0.299 +
    rgb[1] * 0.587 +
    rgb[2] * 0.114;

  if (bright >= 0.5) {
    _fg_color = RGB(0, 0, 0);
    _highlight_brush = (HBRUSH)GetStockObject(BLACK_BRUSH);
  } else {
    _fg_color = RGB(255, 255, 255);
    _highlight_brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
  }

  _x = 0;
  _y = 0;
  _width = 0;
  _height = 0;
  _ideal_width = 0;
  _highlight = false;
  _mouse_within = false;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::Destructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
WinStatsLabel::
~WinStatsLabel() {
  if (_window) {
    DestroyWindow(_window);
    _window = 0;
  }
  DeleteObject(_bg_brush);
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::setup
//       Access: Public
//  Description: Creates the actual window.
////////////////////////////////////////////////////////////////////
void WinStatsLabel::
setup(HWND parent_window) {
  if (_window) {
    DestroyWindow(_window);
    _window = 0;
  }

  create_window(parent_window);

  HDC hdc = GetDC(_window);
  HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); 
  SelectObject(hdc, hfnt);

  SIZE size;
  GetTextExtentPoint32(hdc, _text.data(), _text.length(), &size);
  _height = size.cy + _top_margin + _bottom_margin;
  _ideal_width = size.cx + _left_margin + _right_margin;

  ReleaseDC(_window, hdc);
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::set_pos
//       Access: Public
//  Description: Sets the position of the label on its parent.  The
//               position describes the lower-left corner of the
//               rectangle, not the upper-left.
////////////////////////////////////////////////////////////////////
void WinStatsLabel::
set_pos(int x, int y, int width) {
  _x = x;
  _y = y;
  _width = width;
  SetWindowPos(_window, 0, x, y - _height, _width, _height, 
               SWP_NOZORDER | SWP_SHOWWINDOW);
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_x
//       Access: Public
//  Description: Returns the x position of the label on its parent.
////////////////////////////////////////////////////////////////////
int WinStatsLabel::
get_x() const {
  return _x;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_y
//       Access: Public
//  Description: Returns the y position of the label on its parent.
////////////////////////////////////////////////////////////////////
int WinStatsLabel::
get_y() const {
  return _y;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_width
//       Access: Public
//  Description: Returns the width of the label as we requested it.
////////////////////////////////////////////////////////////////////
int WinStatsLabel::
get_width() const {
  return _width;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_height
//       Access: Public
//  Description: Returns the height of the label as we requested it.
////////////////////////////////////////////////////////////////////
int WinStatsLabel::
get_height() const {
  return _height;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_ideal_width
//       Access: Public
//  Description: Returns the width the label would really prefer to be.
////////////////////////////////////////////////////////////////////
int WinStatsLabel::
get_ideal_width() const {
  return _ideal_width;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_collector_index
//       Access: Public
//  Description: Returns the collector this label represents.
////////////////////////////////////////////////////////////////////
int WinStatsLabel::
get_collector_index() const {
  return _collector_index;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::set_highlight
//       Access: Public
//  Description: Enables or disables the visual highlight for this
//               label.
////////////////////////////////////////////////////////////////////
void WinStatsLabel::
set_highlight(bool highlight) {
  if (_highlight != highlight) {
    _highlight = highlight;
    InvalidateRect(_window, NULL, TRUE);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::get_highlight
//       Access: Public
//  Description: Returns true if the visual highlight for this
//               label is enabled.
////////////////////////////////////////////////////////////////////
bool WinStatsLabel::
get_highlight() const {
  return _highlight;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::set_mouse_within
//       Access: Private
//  Description: Used internally to indicate whether the mouse is
//               within the label's window.
////////////////////////////////////////////////////////////////////
void WinStatsLabel::
set_mouse_within(bool mouse_within) {
  if (_mouse_within != mouse_within) {
    _mouse_within = mouse_within;
    InvalidateRect(_window, NULL, TRUE);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::create_window
//       Access: Private
//  Description: Creates the window for this label.
////////////////////////////////////////////////////////////////////
void WinStatsLabel::
create_window(HWND parent_window) {
  if (_window) {
    return;
  }

  HINSTANCE application = GetModuleHandle(NULL);
  register_window_class(application);

  _window = 
    CreateWindow(_window_class_name, _text.c_str(), WS_CHILD | WS_CLIPSIBLINGS,
                 0, 0, 0, 0,
                 parent_window, NULL, application, 0);
  if (!_window) {
    nout << "Could not create Label window!\n";
    exit(1);
  }

  SetWindowLongPtr(_window, 0, (LONG_PTR)this);
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::register_window_class
//       Access: Private, Static
//  Description: Registers the window class for the label window, if
//               it has not already been registered.
////////////////////////////////////////////////////////////////////
void WinStatsLabel::
register_window_class(HINSTANCE application) {
  if (_window_class_registered) {
    return;
  }

  WNDCLASS wc;

  ZeroMemory(&wc, sizeof(WNDCLASS));
  wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = (WNDPROC)static_window_proc;
  wc.hInstance = application;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = NULL;
  wc.lpszMenuName = NULL;
  wc.lpszClassName = _window_class_name;

  // Reserve space to associate the this pointer with the window.
  wc.cbWndExtra = sizeof(WinStatsLabel *);
  
  if (!RegisterClass(&wc)) {
    nout << "Could not register Label window class!\n";
    exit(1);
  }

  _window_class_registered = true;
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::static_window_proc
//       Access: Private, Static
//  Description: 
////////////////////////////////////////////////////////////////////
LONG WINAPI WinStatsLabel::
static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  WinStatsLabel *self = (WinStatsLabel *)GetWindowLongPtr(hwnd, 0);
  if (self != (WinStatsLabel *)NULL && self->_window == hwnd) {
    return self->window_proc(hwnd, msg, wparam, lparam);
  } else {
    return DefWindowProc(hwnd, msg, wparam, lparam);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: WinStatsLabel::window_proc
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
LONG WinStatsLabel::
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  switch (msg) {
  case WM_LBUTTONDBLCLK:
    _graph->clicked_label(_collector_index);
    return 0;
    
  case WM_MOUSEMOVE: 
    {
      // When the mouse enters the label area, highlight the label.
      set_mouse_within(true);
      
      // Now we want to get a WM_MOUSELEAVE when the mouse leaves the
      // label.
      TRACKMOUSEEVENT tme = {
        sizeof(TRACKMOUSEEVENT),
        TME_LEAVE,
        _window,
        0
      };
      TrackMouseEvent(&tme);
    }
    break;

  case WM_MOUSELEAVE: 
    set_mouse_within(false);
    break;

  case WM_PAINT:
    {
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);

      RECT rect = { 0, 0, _width, _height };
      FillRect(hdc, &rect, _bg_brush);

      if (_highlight || _mouse_within) {
        FrameRect(hdc, &rect, _highlight_brush);
      }

      HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); 
      SelectObject(hdc, hfnt);
      SetTextAlign(hdc, TA_RIGHT | TA_TOP);

      SetBkColor(hdc, _bg_color);
      SetBkMode(hdc, OPAQUE);
      SetTextColor(hdc, _fg_color);

      TextOut(hdc, _width - _right_margin, _top_margin,
              _text.data(), _text.length()); 
      EndPaint(hwnd, &ps);
      return 0;
    }

  default:
    break;
  }

  return DefWindowProc(hwnd, msg, wparam, lparam);
}
