/******************************************************************************/
/*                                                                            */
/*  IMAGE8 - All basic routines for the Image8 class                          */
/*                                                                            */
/******************************************************************************/

#define STRICT
#include <windows.h>
#include <commctrl.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include "const.h"
#include "classes.h"
#include "funcdefs.h"
#include "vortex.rh"
#include "extern.h"

/*
--------------------------------------------------------------------------------

   Default constructor and destructor

--------------------------------------------------------------------------------
*/

Image8::Image8 ()   // Never called
{
   if (nr < 0)     // If constructor's malloc failed
      return ;     // There is nothing more to do
   img = NULL ;
}

Image8::~Image8 ()
{
   if (nr < 0)     // If constructor's malloc failed
      return ;     // There is nothing more to do
   if (img != NULL)
      free ( img ) ;
   if (dib_image != NULL)
      delete dib_image ;
}


/*
--------------------------------------------------------------------------------

   Usual constructor

--------------------------------------------------------------------------------
*/

Image8::Image8 ( VortexParams *params )
      : Image ( "Vortex" )
{
#if MAX_THREADS == 0
   int irow, icol ;
   double window_row, window_col, value ;
   unsigned char *rowptr ;
#endif

   if (nr < 0)     // If constructor's malloc failed
      return ;     // There is nothing more to do

   nr = params->nrows ;
   nc = params->ncols ;
   nbytes = 1 ;


/*
   Allocate memory
*/

   img = (unsigned char *) malloc ( nr * nc ) ;

   if (img == NULL) {
      nr = nc = -1 ;   // Flag failure
      return ;
      }

   begin_wait_message ( "Creating vortex image" , 1 ) ;
   user_pressed_escape () ;  // Spin the message loop to display message


/*
   Create vortex image
   DIBIMAGE.CPP makes the window top-to-bottom, so we start with positive row
*/

#if MAX_THREADS == 0
   for (irow=0 ; irow<nr ; irow++) {
      setpos_wait_message ( (double) (irow+1) / (double) vortex_params.nrows ) ;
      if (user_pressed_escape()) {
         end_wait_message () ;
         error_message ( "User pressed ESCape" ) ;
         nr = nc = -1 ;   // Flag failure
         free ( img ) ;
         img = NULL ;  // Not necessary, but cheap insurance
         return ;
         }
      rowptr = img + irow * nc ;    // Row in computed image
      window_row = params->max_row - (params->max_row - params->min_row) * irow / (nr - 1.0) ;
      for (icol=0 ; icol<nc ; icol++) {
         window_col = params->max_col - (params->max_col - params->min_col) * icol / (nc - 1.0) ;
         if (params->pattern == 1)
            value = compute_checker ( window_row , window_col ) ;
         else if (params->pattern == 2)
            value = compute_spiral ( window_row , window_col ) ;
         assert ( value >= 0.0  &&  value <= 1.0 ) ;
         rowptr[icol] = (unsigned char) (255.99 * value) ;
         }
      }
#else
   if (compute_threaded ( img )) {
      end_wait_message () ;
      error_message ( "Catastrophic error computing image" ) ;
      nr = nc = -1 ;   // Flag failure
      free ( img ) ;
      img = NULL ;  // Not necessary, but cheap insurance
      return ;
      }
#endif

   dib_image = new DIBimage ( nr , nc , 1 , img ) ;
   end_wait_message () ;

   if (dib_image == NULL) {
      insufficient_memory ( "to create DIB image" ) ;
      nr = nc = -1 ;   // Flag failure
      free ( img ) ;
      img = NULL ;  // Not necessary, but cheap insurance
      return ;
      }
}


/*
--------------------------------------------------------------------------------

   Write BMP file

--------------------------------------------------------------------------------
*/

int Image8::write_bmp ( char *filename )
{
   int i, row_bytes, head_size, img_size, top, left, nrows, ncols ;
   unsigned char *pixels ;
   FILE *fp ;
   BITMAPINFO *bmi ;
   BITMAPINFOHEADER *bmih ;
   BITMAPFILEHEADER bmifh ;
   RGBQUAD *rgb ;

/*
   get the location and dimensions of the full screen image to write
*/

   top = left = 0 ;
   nrows = nr ;
   ncols = nc ;

/*
   Allocate memory
   Each scan line must start on a LONG boundary.
   So we allocate enough memory for any padding necessary at the end of each.
*/

   head_size = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD) ;
   bmi = (BITMAPINFO *) malloc ( head_size ) ;

   row_bytes = (ncols + sizeof(LONG) - 1) / sizeof(LONG) * sizeof(LONG) ;

   img_size = nrows * row_bytes ;
   pixels = (unsigned char *) malloc ( img_size ) ;

   if ((bmi == NULL)  ||  (pixels == NULL)) {
      MessageBox ( hwndFrame , "ERROR: Cannot write image to BMP file" ,
                     "Insufficient memory" , MB_ICONEXCLAMATION | MB_OK) ;
      if (bmi != NULL)
         free ( bmi ) ;
      if (pixels != NULL)
         free ( pixels ) ;
      return 1 ;
      }

/*
   Fill in BITMAPINFOHEADER
*/

   bmih = (BITMAPINFOHEADER *) bmi ;

   memset ( bmih , 0 , sizeof(BITMAPINFOHEADER) ) ;

   bmih->biSize = sizeof(BITMAPINFOHEADER) ;
   bmih->biWidth = ncols ;
   bmih->biHeight = nrows ;  // Positive makes it bottom-to-top
   bmih->biPlanes = 1 ;
   bmih->biBitCount = (char) 8 ;
   bmih->biCompression = BI_RGB ;
   bmih->biSizeImage = 0 ;  // OK for BI_RGB
   bmih->biXPelsPerMeter = 0 ;
   bmih->biYPelsPerMeter = 0 ;
   bmih->biClrUsed = 0 ;
   bmih->biClrImportant = 0 ;  // Uses full RGB table

/*
   Fill in color map, which is simple black-and-white
*/

   rgb = (RGBQUAD *) ((char *) bmi + sizeof(BITMAPINFOHEADER));

   for (i=0 ; i<256 ; i++) {
      rgb[i].rgbBlue = rgb[i].rgbGreen = rgb[i].rgbRed = (char) i ;
      rgb[i].rgbReserved = 0 ;
      }

/*
   Copy the image, recalling that each row is padded (if necessary) to LONG
*/

   begin_wait_message ( "Writing stereogram image" , 0 ) ;
   memset ( pixels , 0 , img_size ) ;

   for (i=0 ; i<nrows ; i++)
      memcpy ( pixels + (nrows - i - 1) * row_bytes ,
               img + (i + top) * nc + left , ncols ) ;

/*
   Fill in the BITMAPFILEHEADER
*/

   bmifh.bfType = 0x4d42 ;   // "BM" reversed
   bmifh.bfSize = sizeof(BITMAPFILEHEADER) + head_size + img_size ;
   bmifh.bfReserved1 = 0 ;
   bmifh.bfReserved2 = 0 ;
   bmifh.bfOffBits = sizeof(BITMAPFILEHEADER) + head_size ;

/*
   Write the file
*/

   if (fopen_s ( &fp , filename , "wb" )) {
      MessageBox ( hwndFrame , "ERROR: Cannot write image to BMP file" ,
                  "Cannot open file for writing" ,MB_ICONEXCLAMATION | MB_OK) ;
      free ( bmi ) ;
      free ( pixels ) ;
      end_wait_message () ;
      return 1 ;
      }

   fwrite ( &bmifh , sizeof(BITMAPFILEHEADER) , 1 , fp ) ;
   if (! ferror ( fp ))
      fwrite ( bmi , head_size , 1 , fp ) ;
   if (! ferror ( fp ))
      fwrite ( pixels , img_size , 1 , fp ) ;

   free ( bmi ) ;
   free ( pixels ) ;
   end_wait_message () ;

   if (ferror ( fp )) {
      MessageBox ( hwndFrame , "ERROR: Cannot write image to BMP file" ,
                  "Cannot write file" ,MB_ICONEXCLAMATION | MB_OK) ;
      fclose ( fp ) ;
      return 1 ;
      }

   fclose ( fp ) ;
   return 0 ;
}

/*
--------------------------------------------------------------------------------

   Draw

--------------------------------------------------------------------------------
*/

int Image8::draw ( HWND hwnd , int printing )
{
   dib_image->draw ( hwnd , printing ) ;
   return 0 ;
}
