/*
======================================================================
status.c

Ernie Wright  17 Apr 05
MSVC 4.0

Support for the status bar at the bottom of the main window.  Includes
functions for showing a progress meter and for updating the caption,
zoom level, mouse position, and pixel value displays.
====================================================================== */

#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "qv.h"


/* subwindow widths are calculated from redge[] */

static HWND hwnd_stat;
static int redge[] = { 0, 32, 94, 222, 294 };
static int pelshow_type = SHOWPEL_BYTE;


/*
----------------------------------------------------------------------
The progress meter is a filled rectangle with a length proportional to
the progress of a slow operation.  The rectangle is drawn in a bitmap
selected into an off-screen device context, then blitted into one of
the subwindows on the status bar.  The DC, bitmap, and proportions are
initialized in new_progress(), called by status_wmsize() whenever the
parent window is resized.
---------------------------------------------------------------------- */

static HDC hdcmem;
static HBITMAP hbm, hbmold;
static int current, total, gleft, gtop, gwidth, gheight;


static void free_progress( void )
{
   if ( hdcmem ) {
      SelectObject( hdcmem, hbmold );
      DeleteDC( hdcmem );
      hdcmem = NULL;
   }
   if ( hbm ) {
      DeleteObject( hbm );
      hbm = NULL;
   }
}


static void new_progress( void )
{
   HDC hdc;
   RECT r;

   free_progress();

   SendMessage( hwnd_stat, SB_GETRECT, 4, ( LPARAM ) &r );
   gleft   = r.left + 2;
   gtop    = r.top + 3;
   gwidth  = r.right - ( r.left + 4 );
   gheight = r.bottom - ( r.top + 6 );

   hdc = GetDC( hwnd_stat );
   hdcmem = CreateCompatibleDC( hdc );
   ReleaseDC( hwnd_stat, hdc );
   hbm = CreateCompatibleBitmap( hdcmem, gwidth, gheight );
   hbmold = SelectObject( hdcmem, hbm );
}


static void draw_gauge( void )
{
   HDC hdc;
   int x;

   hdc = GetDC( hwnd_stat );
   if ( !hdc ) return;
   SetTextColor( hdc, 0x000080L );

   x = total ? (( gwidth ) * current ) / total : 0;
   PatBlt( hdcmem, 0, 0, gwidth, gheight, WHITENESS );
   PatBlt( hdcmem, 0, 0, x, gheight, DSTINVERT );
   BitBlt( hdc, gleft, gtop, gwidth, gheight, hdcmem, 0, 0, SRCCOPY );

   ReleaseDC( hwnd_stat, hdc );
}


/*
======================================================================
init_progress()

Start the progress meter.
====================================================================== */

void init_progress( int atotal )
{
   current = 0;
   total = atotal;
   draw_gauge();
}


/*
======================================================================
show_progress()

Draw the progress meter.  The progress is current/total, the argument
to show_progress() divided by the argument to init_progress().  If
this is >= 1.0, the meter is cleared.
====================================================================== */

void show_progress( int acurrent )
{
   current = acurrent;
   if ( current < total )
      draw_gauge();
   else {
      SendMessage( hwnd_stat, SB_SETTEXT, 4, ( LPARAM ) " " );
      UpdateWindow( hwnd_stat );
   }
}


/*
======================================================================
image_progress()

A special form of the init_progress() and show_progress() system for
image processing and other operations with small, high-count loops.
The progress meter is only redrawn if enough time (50 milliseconds)
has elapsed since the last redraw.

The first time this is called, the argument is the total number of
operations, typically the number of scanlines in an image.  Later
calls set the argument to 0.  image_progress() keeps an internal count
of the number of calls and resets the progress meter when it reaches
the total.
====================================================================== */

void image_progress( int y )
{
   static DWORD t1;
   static ty, cy;
   DWORD t2;

   if ( y ) {
      init_progress( y );
      ty = y;
      cy = 0;
      t1 = GetTickCount();
   }
   else {
      ++cy;
      t2 = GetTickCount();
      if (( t2 < t1 ) || ( t2 - t1 > 50 ) || ( cy == ty )) {
         show_progress( cy );
         t1 = t2;
      }
   }
}


/*
======================================================================
statusCreate()

Create the status window.
====================================================================== */

HWND statusCreate( HWND hwnd )
{
   hwnd_stat = CreateStatusWindow(
      WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP,
      "Ready", hwnd, IDW_STATWND );

   return hwnd_stat;
}


/*
======================================================================
statusFree()

Free resources used by the status window.  Call this when the parent
window closes.
====================================================================== */

void statusFree( void )
{
   free_progress();
}


/*
======================================================================
statusResize()

Respond to a WM_SIZE sent to the parent window.  We need to refigure
the positions of the subwindow dividers.
====================================================================== */

int statusResize( int parent_w, int parent_h )
{
   RECT r;
   int i, h, dx, edge[ 5 ];

   GetWindowRect( hwnd_stat, &r );
   h = r.bottom - r.top;
   MoveWindow( hwnd_stat, 0, parent_h - h, parent_w, h, TRUE );

   dx = parent_w - ( redge[ 4 ] + 16 );
   for ( i = 0; i < 5; i++ )
      edge[ i ] = redge[ i ] + dx;

   SendMessage( hwnd_stat, SB_SETPARTS, 5, ( LPARAM ) edge );
   new_progress();

   return h;
}


/*
======================================================================
statusHeight()

Return the height of the status window.
====================================================================== */

int statusHeight( void )
{
   RECT r;
   GetWindowRect( hwnd_stat, &r );
   return r.bottom - r.top;
}


/*
======================================================================
statusCaption()

Write the text in the caption subwindow.
====================================================================== */

void statusCaption( char *caption )
{
   SendMessage( hwnd_stat, SB_SETTEXT, 0, ( LPARAM ) caption );
}


/*
======================================================================
statusZoom()

Update the display of the zoom factor.
====================================================================== */

void statusZoom( int izoom )
{
   char buf[ 8 ];

   if ( izoom < IZOOM_ONE )
      sprintf( buf, "1/%dx", IZOOM_ONE - izoom + 1 );
   else
      sprintf( buf, "%dx", izoom - IZOOM_ONE + 1 );
   SendMessage( hwnd_stat, SB_SETTEXT, 1, ( LPARAM ) buf );
   UpdateWindow( hwnd_stat );
}


void setPixelDisplayType( int type )
{
   pelshow_type = type;
}


/*
======================================================================
fmtLevelFloat()

Create the string representation of a floating-point channel level.
The status pane for the pixel values has room for six characters per
value, and we try to display something useful in that small space for
all possible float values.  This also guards against buffer overflow
in functions that call fmtLevelFloat(), since they can count on the
output to be no longer than 6 characters (or 7, if the input is
negative).

If the (absolute) value is less than 99.9995, we write the number with
three decimal places.  (Note that the test can't be p < 100.0, since
values 99.9995 <= p < 100.0 round up to 100.0, producing "100.000".)
At higher values up to 999999.5, we chop off decimal places, and above
that we write our own compact version of printf's %e output.  For
completeness, we also guard against NAN and INF.

This was added 20 Jun 06, after a report that really large pixel
values caused QV/LW to crash.  Previously I'd just written float
pixels using "%.3f".  For large values, this was overflowing the
string where the values are written.
====================================================================== */

static char *fmtLevelFloat( float p )
{
   static char buf[ 16 ];
   unsigned long n, e, m;
   float ap, x;

   /* check for NAN and INF */

   n = *(( unsigned long * ) &p );
   e = ( n & 0x7F800000 ) >> 23;
   m = n & 0x007FFFFF;
   if ( e == 255 )
      return m ? "NAN" : "INF";

   /* partition based on absolute value */

   ap = ( float ) fabs( p );

   if ( ap < 99.9995f )                   // [0.0, 99.9995)
      sprintf( buf, "%.3f", p );
   else if ( ap < 999.995f )              // [99.9995, 999.995)
      sprintf( buf, "%.2f", p );
   else if ( ap < 9999.95f )              // [999.995, 9999.95)
      sprintf( buf, "%.1f", p );
   else if ( ap < 999999.5f )             // [9999.95, 999999.5)
      sprintf( buf, "%d", ( int )( p + 0.5f ));
   else {
      e = ( int ) log10( p );
      x = ( float )( p / pow( 10.0, e ));
      if ( x > 9.995f ) {
         x = 1.0f;
         ++e;
      }
      if ( ap < 9.95e9f )                 // [999999.5, 9.95e9)
         sprintf( buf, "%.2fe%d", x, e );
      else                                // [9.95e9, 3.4e38]
         sprintf( buf, "%.1fe%d", x, e );
   }

   return buf;
}


/*
======================================================================
fmtPixelFloat()

Write the floating-point representation of an RGB or alpha value.
Added 20 Jun 06 (see fmtLevelFloat()).
====================================================================== */

static void fmtPixelFloat( char *buf, int channel, float p[] )
{
   if ( channel == CH_IMAGE ) {
      sprintf( buf, "%s, ", fmtLevelFloat( p[ 0 ] ));
      strcat( buf, fmtLevelFloat( p[ 1 ] ));
      strcat( buf, ", " );
      strcat( buf, fmtLevelFloat( p[ 2 ] ));
   }
   else {
      sprintf( buf, "%s", fmtLevelFloat( p[ 0 ] ));
   }
}


/*
======================================================================
statusPixel()

Update the display of the mouse coordinates and pixel values.  If x is
less than 0, the mouse coordinate and pixel value panes are erased if
they aren't already blank.
====================================================================== */

void statusPixel( Displayable *disp, int x, int y, int channel )
{
   static int isblank = FALSE;
   char buf[ 32 ];

   if ( x < 0 && !isblank ) {
      isblank = TRUE;
      strcpy( buf, " " );
      SendMessage( hwnd_stat, SB_SETTEXT, 2, ( LPARAM ) buf );
      SendMessage( hwnd_stat, SB_SETTEXT, 3, ( LPARAM ) buf );
      UpdateWindow( hwnd_stat );
   }
   else if ( x >= 0 ) {
      isblank = FALSE;
      sprintf( buf, "%d, %d", x, y );
      SendMessage( hwnd_stat, SB_SETTEXT, 2, ( LPARAM ) buf );

      if ( pelshow_type == SHOWPEL_BYTE ) {
         unsigned char p[ 3 ];
         getPixelByte( disp, x, y, channel, p );
         if ( channel == CH_IMAGE )
            sprintf( buf, "%d, %d, %d", p[ 0 ], p[ 1 ], p[ 2 ] );
         else
            sprintf( buf, "%d", p[ 0 ] );
      }
      else {
         float p[ 3 ];
         getPixelFloat( disp, x, y, channel, p );
         fmtPixelFloat( buf, channel, p );
      }
      SendMessage( hwnd_stat, SB_SETTEXT, 3, ( LPARAM ) buf );
      UpdateWindow( hwnd_stat );
   }
}
