/******************************************************************************/
/*                                                                            */
/*  EXECUTE - Create an image and display it                                  */
/*                                                                            */
/******************************************************************************/

#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 <malloc.h>
#include <new.h>
#include <float.h>

#include "const.h"
#include "classes.h"
#include "extern.h"
#include "funcdefs.h"
#include "julia.rh"

int execute ()
{
   int val, ret_val ;
   int caps_bitspixel, caps_planes, caps_sizepalette ;
   int caps_numreserved, caps_colorres ;
   int ras_bitblt, ras_bitmap64, ras_di_bitmap ;
   int ras_palette, ras_stretchblt, ras_stretchdib ;
   double *loop_real, *loop_imag, *quat_dist, delta ;
   char msg[256], tempmsg[256] ;
   COMPLEX c_const ;
   QUATERNION jparam, *quat_orbit ;
   HDC hdc ;

   MEMTEXT ( "EXECUTE" ) ;

   if (dwells != NULL) {
      FREE ( dwells ) ;
      dwells = NULL ;
      }

   if (raw_image != NULL) {
      FREE ( raw_image ) ;
      raw_image = NULL ;
      }

   if (dib != NULL) {   // If a device-independent-bitmap already exists,
      delete dib ;      // Delete it
      dib = NULL ;
      }

   DisplayedObjectType = 0 ;


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

   Verify that the display device is ok for image display

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

   hdc = GetDC ( hwndMain ) ;
   assert ( hdc != NULL ) ;

   caps_bitspixel = GetDeviceCaps ( hdc , BITSPIXEL ) ;
   caps_planes = GetDeviceCaps ( hdc , PLANES ) ;
   caps_sizepalette = GetDeviceCaps ( hdc , SIZEPALETTE ) ;
   caps_numreserved = GetDeviceCaps ( hdc , NUMRESERVED ) ;
   caps_colorres = GetDeviceCaps ( hdc , COLORRES ) ;
   sprintf_s ( msg , "bits=%d planes=%d szpal=%d res=%d cols=%d",
              caps_bitspixel, caps_planes, caps_sizepalette,
              caps_numreserved, caps_colorres ) ;
   MEMTEXT ( msg ) ;
   val = GetDeviceCaps ( hdc , RASTERCAPS ) ;
   ras_bitblt = val & RC_BITBLT ;
   ras_bitmap64 = val & RC_BITMAP64 ;
   ras_di_bitmap = val & RC_DI_BITMAP ;
   ras_palette = val & RC_PALETTE ;
   ras_stretchblt = val & RC_STRETCHBLT ;
   ras_stretchdib = val & RC_STRETCHDIB ;
   sprintf_s ( tempmsg ,
               "bitblt=%d bitmap64=%d DIbitmap=%d pal=%d StrBlt=%d StrDIB=%d",
               ras_bitblt, ras_bitmap64, ras_di_bitmap,
               ras_palette, ras_stretchblt, ras_stretchdib ) ;
   MEMTEXT ( tempmsg ) ;

   ReleaseDC ( hwndMain , hdc ) ;

   if ((caps_bitspixel < 8)  ||  (caps_planes > 1)) {
      MessageBox ( hwndMain , "Incompatible video driver" , "ERROR" , MB_OK ) ;
      MessageBox ( hwndMain , msg , "Failure Information" , MB_OK) ;
      MessageBox ( hwndMain , tempmsg , "More Failure Information" , MB_OK) ;
      return ERROR_BAD_VIDEO ;
      }

//-------------------------------------------------------

   // These four are work areas, allocated and freed in this module
   loop_real = NULL ;
   loop_imag = NULL ;
   quat_dist = NULL ;
   quat_orbit = NULL ;

   ret_val = 0 ;
   Executing = 1 ;
   begin_progress_message ( "Computing..." ) ;


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

   Do Mandel and Julia

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

   if (ObjectType <= 2) {

      // Allocate memory

      loop_real = (double *) MALLOC ( MAX_THREADS * DwellLimit * sizeof(double) ) ;
      loop_imag = (double *) MALLOC ( MAX_THREADS * DwellLimit * sizeof(double) ) ;
      dwells = (unsigned int *) MALLOC ( RowResolution * ColResolution * sizeof(unsigned int) ) ;
      raw_image = (unsigned char *) MALLOC ( 3 * RowResolution * ColResolution ) ;

      if (loop_real == NULL  ||  loop_imag == NULL  ||  dwells == NULL  ||  raw_image == NULL) {
         if (loop_real != NULL)
            FREE ( loop_real ) ;
         if (loop_imag != NULL)
            FREE ( loop_imag ) ;
         if (dwells != NULL)
            FREE ( dwells ) ;
         if (raw_image != NULL)
            FREE ( raw_image ) ;
         loop_real = loop_imag = NULL ;
         dwells = NULL ;
         raw_image = NULL ;
         return ERROR_INSUFFICIENT_MEMORY ;
         }

      c_const.real = JuliaR ;  // Needed only for Julia
      c_const.imag = JuliaI ;

#if MAX_THREADS == 1
      int irow, icol ;
      double real_inc, imag_inc ;
      unsigned int *dwell_ptr ;
      COMPLEX z_start, end ;

      out_by_escape = out_by_max = out_by_conv = out_by_loop = 0 ;  // Performance stats in COUNTIT.CPP
      real_inc = (MaxR - MinR) / (ColResolution - 1) ;
      imag_inc = (MinI - MaxI) / (RowResolution - 1) ;

      for (irow=0 ; irow<RowResolution ; irow++) {
         setpos_progress_message ( (double) irow / (double) RowResolution ) ;
         if (user_pressed_escape ()) {
            ret_val = ERROR_ESCAPE ;
            goto FINISH ;
            }
         dwell_ptr = dwells + irow * ColResolution ;
         z_start.imag = MaxI + imag_inc * (double) irow ;
         for (icol=0 ; icol<ColResolution ; icol++) {
            z_start.real = MinR + real_inc * (double) icol ;
            if (DwellVsBinary) {
               if (ObjectType == 2)
                  *dwell_ptr++ = countit ( &c_const , &z_start , &end , loop_real, loop_imag ) ;
               else
                  *dwell_ptr++ = countit ( &z_start , &z_start , &end, loop_real, loop_imag) ;
               if (BinaryDecomp  &&  (*(dwell_ptr-1) < DwellLimit)) {
                  *(dwell_ptr-1) &= 0xFFFFFFFE ;  // This assumes dwell is four bytes
                  if (end.imag  >  0.0)
                     ++*(dwell_ptr-1) ;
                  }
               }
            else {
               delta = BinaryThreshold * 0.5 * (real_inc - imag_inc) ;
               if (ObjectType == 2) // Julia
                  *dwell_ptr++ = msetdist ( &c_const , &z_start , DwellLimit , 1 , loop_real , loop_imag ) < delta ;
               else
                  *dwell_ptr++ = msetdist ( &z_start , &z_start , DwellLimit , 0 , loop_real , loop_imag ) < delta ;
               }
            } // For all cols
         } // For all rows

#else
      if (mand_thr ( MinR , MaxR , MinI , MaxI , DwellLimit , BinaryDecomp , ObjectType ,
                     DwellVsBinary , BinaryThreshold ,
                     c_const , RowResolution , ColResolution , dwells ,
                     loop_real , loop_imag ))
         goto FINISH ;
#endif

      if (CmapNcolors)
         colormap () ; // Build the image by applying the color map to the dwells
      else
         bw_map () ;

#if MAX_THREADS == 1
      audit ( "Convergence counters:" ) ;  // These are valid only if not multi-threaded
      sprintf_s ( msg, "  Escape = %d", out_by_escape ) ;
      audit ( msg ) ;
      sprintf_s ( msg, "  Dwell limit = %d", out_by_max ) ;
      audit ( msg ) ;
      sprintf_s ( msg, "  Convergence = %d", out_by_conv ) ;
      audit ( msg ) ;
      sprintf_s ( msg, "  Looping = %d", out_by_loop ) ;
      audit ( msg ) ;
#endif
      } // If Mandelbrot or Julia



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

   Do Quaternion

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

   else {

      // Allocate memory

      if (QuatSmooth)
         quat_dist = (double *) MALLOC ( 2 * RowResolution * ColResolution * sizeof(double)) ;
      else
         quat_dist = (double *) MALLOC ( RowResolution * ColResolution * sizeof(double)) ;
      quat_orbit = (QUATERNION *) MALLOC ( MAX_THREADS * DwellLimit * sizeof(QUATERNION)) ; // Work only
      raw_image = (unsigned char *) MALLOC ( 3 * RowResolution * ColResolution ) ;

      if (quat_dist == NULL  ||  quat_orbit == NULL  ||  raw_image == NULL) {
         if (quat_dist != NULL)
            FREE ( quat_dist ) ;
         if (quat_orbit != NULL)
            FREE ( quat_orbit ) ;
         if (raw_image != NULL)
            FREE ( raw_image ) ;
         quat_dist = NULL ;
         quat_orbit = NULL ;
         raw_image = NULL ;
         return ERROR_INSUFFICIENT_MEMORY ;
         }

      jparam.r = JuliaR ;
      jparam.i = JuliaI ;
      jparam.j = 0. ;
      jparam.k = 0. ;
      rcentRT = 0.5 * (MinR + MaxR) ;
      icentRT = 0.5 * (MinI + MaxI) ;
      jcentRT = 0. ;
      kcentRT = 0. ;
      magnifRT = 0.5 * (MagR + MagI) ;

#if MAX_THREADS == 1
      int irow;

      for (irow=0 ; irow<RowResolution ; irow++) {
         setpos_progress_message ( (double) irow / (double) RowResolution ) ;
         if (user_pressed_escape ()) {
            ret_val = ERROR_ESCAPE ;
            goto FINISH ;
            }
         tracquat ( rcentRT , icentRT , jcentRT , kcentRT , magnifRT , Collapse ,
                    &jparam , Elevation , Azimuth ,
                    ((double) RowResolution/2. - (double) irow - .5 ) / (double) (RowResolution-1) ,
                    ColResolution , quat_dist + irow * ColResolution , quat_orbit ) ;
         }

#else
      if (quat_thr ( rcentRT , icentRT , jcentRT , kcentRT , magnifRT , Collapse ,
                     &jparam , Elevation , Azimuth ,
                     RowResolution , ColResolution , quat_dist , quat_orbit ))
         goto FINISH ;
#endif

      if (QuatSmooth)
         smooth_quat ( RowResolution , ColResolution , quat_dist ) ;

      dispRT ( RowResolution , ColResolution , quat_dist , raw_image , Lighting , magnifRT ) ;
      } // Else Quaternion


   DisplayedObjectType = ObjectType ;  // Failure goes straight to FINISH


FINISH:
   if (loop_real != NULL)
      FREE ( loop_real ) ;
   if (loop_imag != NULL)
      FREE ( loop_imag ) ;

   if (quat_dist != NULL)
      FREE ( quat_dist ) ;
   if (quat_orbit != NULL)
      FREE ( quat_orbit ) ;

   // Create the device independent bitmap
   dib = new DIBimage ( RowResolution , ColResolution , 3 , raw_image ) ;
   InvalidateRect ( hwndMain , NULL , TRUE ) ;

   end_progress_message () ;
   Executing = 0 ;
   return ret_val ;
}
