/******************************************************************************/
/*                                                                            */
/*  COMPUTE - All routines for computing the blocks                           */
/*                                                                            */
/******************************************************************************/

#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 "blocks.rh"
#include "extern.h"


int draw_block ( int start_row , int start_col , unsigned char *img ,
                  int *row_diff1 , int *col_diff1 , int *row_diff2 , int *col_diff2 ) ;


int compute ( unsigned char *img )
{
   int nr, nc, i, j, rcent, ccent, row_diff1, col_diff1, row_diff2, col_diff2, dummy ;
   unsigned char *ptr ;


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

   nr = blocks_params.nrows ;
   nc = blocks_params.ncols ;
   memset ( img , 255 , nr * nc ) ;

   // Draw the center block
   draw_block ( nr/2 , nc/2 , img , &row_diff1 , &col_diff1 , &row_diff2 , &col_diff2 ) ;

   assert ( col_diff2 > 0.0 ) ;
   assert ( row_diff1 > 0.0 ) ;
   assert ( col_diff1 > 0.0 ) ;

/*
   Do a quadrant
*/

   for (i=1 ; ; i++) {
      rcent = nr/2 + i * row_diff2 ;
      ccent = nc/2 + i * col_diff2 ;
      if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
         break ;
      for (j=0 ; ; j++) {
         draw_block ( rcent , ccent , img , &dummy , &dummy , &dummy , &dummy ) ;
         rcent -= row_diff1 ;
         ccent -= col_diff1 ;
         if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
            break ;
         }
      }
   setpos_wait_message ( 0.25 ) ;
   user_pressed_escape () ;

/*
   Do a quadrant
*/

   for (i=1 ; ; i++) {
      rcent = nr/2 + i * row_diff2 ;
      ccent = nc/2 + i * col_diff2 ;
      if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
         break ;
      for (j=0 ; ; j++) {
         rcent += row_diff1 ;
         ccent += col_diff1 ;
         if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
            break ;
         draw_block ( rcent , ccent , img , &dummy , &dummy , &dummy , &dummy ) ;
         }
      }
   setpos_wait_message ( 0.5 ) ;
   user_pressed_escape () ;

/*
   Do a quadrant
*/

   for (i=0 ; ; i++) {
      rcent = nr/2 - i * row_diff2 ;
      ccent = nc/2 - i * col_diff2 ;
      if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
         break ;
      for (j=0 ; ; j++) {
         if (i == 0  &&  j == 0)  // Do not duplicate center block
            continue ;
         draw_block ( rcent , ccent , img , &dummy , &dummy , &dummy , &dummy ) ;
         rcent -= row_diff1 ;
         ccent -= col_diff1 ;
         if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
            break ;
         }
      }

   setpos_wait_message ( 0.75 ) ;
   user_pressed_escape () ;

/*
   Do a quadrant
*/

   for (i=0 ; ; i++) {
      rcent = nr/2 - i * row_diff2 ;
      ccent = nc/2 - i * col_diff2 ;
      if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
         break ;
      for (j=0 ; ; j++) {
         if (i == 0  &&  j == 0)  // Do not duplicate center block
            continue ;
         draw_block ( rcent , ccent , img , &dummy , &dummy , &dummy , &dummy ) ;
         rcent += row_diff1 ;
         ccent += col_diff1 ;
         if (rcent < -2*nr  ||  rcent > 2*nr  ||  ccent < -6*nc  ||  ccent > 6*nc) // Crude and excessive (usually)
            break ;
         }
      }

/*
   Fill in any white pixels that are due to edges not meeting perfectly
*/

   for (i=0 ; i<nr ; i++) {
      ptr = img + i * nc ;     // Point to this row
      if (ptr[0] == (unsigned char) 255)
         ptr[0] = ptr[1] ;
      for (j=1 ; j<nc ; j++) {
         if (ptr[j] == (unsigned char) 255)
            ptr[j] = ptr[j-1] ;
         }
      }

   end_wait_message () ;
   return 0;
}


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

   Local worker draws a single block
   It returns 1 if at least one pixel was written, else 0.

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

int draw_block ( int start_row , int start_col , unsigned char *img ,
                 int *row_diff1 , int *col_diff1 , int *row_diff2 , int *col_diff2 )
{
   int i, j, irow, icol, nr, nc, line_len, vert_len, horz_len, tone ;
   int save_irow1, save_icol1, save_irow2, save_icol2, any_written ;
   double row, rise ;

   nr = blocks_params.nrows ;
   nc = blocks_params.ncols ;
   any_written = 0 ;   // Flags if we ever wrote a pixel


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

   Draw the front surface.  The top-right vertex is the reference point.
   The first row (0) will be at the top of the displayed image.
   A tone of 255 is reserved for background, so 254 is the max.

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

   tone = (int) (254.0 * blocks_params.front_tone + 0.5) ;
   vert_len = (int) (0.01 * blocks_params.height * nr + 0.5) ;
   line_len = (int) (0.01 * blocks_params.width * 0.5 * (nr + nc) + 0.5) ;
   horz_len = (int) (line_len * cos ( blocks_params.angle * PI / 180.0 ) + 0.5) ;
   rise = tan ( blocks_params.angle * PI / 180.0 ) ;

   if (start_row + vert_len < 0)  // Is it entirely oof the screen?
      return 0 ;

   // Draw top edge, right-to-left
   // Upper-right vertex is at center of image
   // Fill in front by dropping down each column

   row = start_row ;
   icol = start_col ;
   irow = 0;  // Ignored; shuts up LINT
   for (i=0 ; i<=horz_len ; i++) {
      irow = (int) (row + 0.5) ;
      if (icol >= 0  &&  icol < nc) {
         for (j=irow ; j<=irow+vert_len ; j++) {
            if (j >= 0  &&  j < nr) {
               img[j*nc+icol] = (unsigned char) tone ;
               any_written = 1 ;
               }
            }
         }
      --icol ;
      row -= rise ; // Image is upside-down
      }

   ++icol ;       // Undo last unused move

   *row_diff1 = start_row + vert_len - irow + 1 ;  // Final value
   *col_diff1 = horz_len + 1 ;                     // Final value
   *row_diff2 = irow + vert_len ;                  // Will be adjusted soon
   *col_diff2 = horz_len + 1 ;                     // Will be adjusted soon


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

   Draw the side surface.
   The top-left vertex is the reference point.

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

   tone = (int) (254.0 * blocks_params.side_tone + 0.5) ;
   vert_len = (int) (0.01 * blocks_params.height * nr + 0.5) ;
   line_len = (int) (0.01 * blocks_params.depth * 0.5 * (nr + nc) + 0.5) ;
   horz_len = (int) (line_len * cos ( blocks_params.angle * PI / 180.0 ) + 0.5) ;
   rise = tan ( blocks_params.angle * PI / 180.0 ) ;

   // Draw top edge, left-to-right
   // Upper-left vertex is at center of image
   // Fill in side by dropping down each column

   row = start_row ;
   icol = start_col + 1 ; // Don't overwrite the right edge of the front

   for (i=0 ; i<=horz_len ; i++) {
      irow = (int) (row + 0.5) ;
      if (icol >= 0  &&  icol < nc) {
         for (j=irow ; j<=irow+vert_len ; j++) {
            if (j >= 0  &&  j < nr) {
               img[j*nc+icol] = (unsigned char) tone ;
               any_written = 1 ;
               }
            }
         }
      ++icol ;
      row -= rise ; // Image is upside-down
      }

   --icol ;       // Undo last unused move

   *row_diff2 = irow + vert_len - *row_diff2 ;  // Final value
   *col_diff2 += horz_len + 1 ;                 // Final value


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

   Draw the perimeter of the top surface.
   The front-right vertex is the reference point.
   This will overwrite the top row of the front and side.

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

   tone = (int) (254.0 * blocks_params.top_tone + 0.5) ;

   // Draw front-left edge, right-to-left
   // Front vertex is at center of image

   line_len = (int) (0.01 * blocks_params.width * 0.5 * (nr + nc) + 0.5) ;
   horz_len = (int) (line_len * cos ( blocks_params.angle * PI / 180.0 ) + 0.5) ;
   rise = tan ( blocks_params.angle * PI / 180.0 ) ;

   row = start_row ;
   icol = start_col ;
   for (i=0 ; i<=horz_len ; i++) {
      irow = (int) (row + 0.5) ;
      if (irow >=0  &&  irow < nr  &&  icol >=0  &&  icol < nc) {
         img[irow * nc + icol] = (unsigned char) tone ;
         any_written = 1 ;
         }
      --icol ;
      row -= rise ; // Image is upside-down
      }

   ++icol ;       // Undo last unused move
   save_irow1 = irow ;
   save_icol1 = icol ;

   // Draw front-right edge, left-to-right
   // Front vertex is at center of image

   line_len = (int) (0.01 * blocks_params.depth * 0.5 * (nr + nc) + 0.5) ;
   horz_len = (int) (line_len * cos ( blocks_params.angle * PI / 180.0 ) + 0.5) ;
   rise = tan ( blocks_params.angle * PI / 180.0 ) ;

   row = start_row ;
   icol = start_col ;
   for (i=0 ; i<=horz_len ; i++) {
      irow = (int) (row + 0.5) ;
      if (irow >=0  &&  irow < nr  &&  icol >=0  &&  icol < nc) {
         img[irow * nc + icol] = (unsigned char) tone ;
         any_written = 1 ;
         }
      ++icol ;
      row -= rise ; // Image is upside-down
      }

   --icol ;       // Undo last unused move
   save_irow2 = irow ;
   save_icol2 = icol ;

   // Draw back-left edge, left-to-right

   line_len = (int) (0.01 * blocks_params.depth * 0.5 * (nr + nc) + 0.5) ;
   horz_len = (int) (line_len * cos ( blocks_params.angle * PI / 180.0 ) + 0.5) ;
   rise = tan ( blocks_params.angle * PI / 180.0 ) ;

   row = save_irow1 ;
   icol = save_icol1 ;
   for (i=0 ; i<=horz_len ; i++) {
      if (icol >= 0  &&  icol < nc) {
         irow = (int) (row + 0.5) ;
         if (irow >= 0  &&  irow < nr) {
            img[irow*nc+icol] = (unsigned char) tone ;
            any_written = 1 ;
            }
         for (j=irow+1 ; j<nr ; j++) {
            if (j>= 0) {
               if (img[j*nc+icol] < (unsigned char) 255)
                  break ;
               img[j*nc+icol] = (unsigned char) tone ;
               any_written = 1 ;
               }
            }
         }
      ++icol ;
      row -= rise ; // Image is upside-down
      }

   // Draw back-right edge, right-to-left

   line_len = (int) (0.01 * blocks_params.width * 0.5 * (nr + nc) + 0.5) ;
   horz_len = (int) (line_len * cos ( blocks_params.angle * PI / 180.0 ) + 0.5) ;
   rise = tan ( blocks_params.angle * PI / 180.0 ) ;

   row = save_irow2 ;
   icol = save_icol2 ;
   for (i=0 ; i<=horz_len ; i++) {
      if (icol >= 0  &&  icol < nc) {
         irow = (int) (row + 0.5) ;
         if (irow >= 0  &&  irow < nr) {
            img[irow*nc+icol] = (unsigned char) tone ;
            any_written = 1 ;
            }
         for (j=irow+1 ; j<nr ; j++) {
            if (j >= 0) {
               if (img[j*nc+icol] < (unsigned char) 255)
                  break ;
               img[j*nc+icol] = (unsigned char) tone ;
               any_written = 1 ;
               }
            }
         }
      --icol ;
      row -= rise ; // Image is upside-down
      }

   ++icol ;       // Undo last unused move (not needed here)
   return any_written;
}