/* program PICK to pick square out of an image

to run, type:

pick [-x<file size> -l<scale factor> -d( no dither ) -s<square size>
                                                    <input file> <output file>] 

or just pick and use the menu */

#include <graph.h>
#include <stdio.h>
#include "plane.h"
#include "general.h"

#define X_SIDE  100             /* left side of disp. square */
#define Y_BASE  5               /* base for display square */
#define MIN_SIDE 5              /* minimun side for square */

char dith_flag;      /* true if dither required */
char menu_flag;      /* true if main menu active */
char move_flag;      /* true if text line cleared when cursor moved */
char larg_flag;      /* true if expanded display required */
char out_flag;       /* true if output file given */
char new_flag;       /* true if new square required */
char side_flag;      /* true if fixed side square required */
 
unsigned short fontbuf[FONTSIZE];	/* buffer for largest expected font */
char *fontname = "fmacs:font.visual";
int l, h, w;

int xl_menu;              /* left x for menu region */
int xr_menu;              /* right x for menu region */
int y_menu;               /* top y for menu region */
int nop;                  /* no. of options for menu */

FILE *fp = NULL;
FILE *fq = NULL;

int size_file = 0;       /* size of file */
int size = 0;            /* size of image */
int scale_size = 1;      /* enlargement factor for displaying image, reduction
                            factor when writing out image */
int grey;                /* current grey level for text & information regions */
int col;                 /* current colour - red or cyam */
char store_filename[ 40 ];   /* stores output filename if any */

char int_array[ 256 * 256 ];  /* intensity array */

extern char read_file(), write_file();

void region(), pick(), limit_image(), draw_square(), clear_square();
void write_size(), clear_size();
int max();
char near(), in_image();
char sel_side();
void shift(), write_square();
void menu(), draw_menu();

main(argc,argv)
char **argv;
int argc;
{
   char header_flag = TRUE; /* true if file contains header */
   char arg_flag = FALSE;    /* true if command line arguments given */
   char i_flag = FALSE;      /* true if intensity file given */
   char c, *p, *p1;
   int square_size;


   dith_flag = TRUE;         /* FLAG DEFAULTS */
   out_flag = FALSE;
   larg_flag = FALSE;
   new_flag = TRUE;
   side_flag = FALSE;

   if ( argc > 1 ) { /* command line arguments given */
      argv++;
      arg_flag = TRUE;

      while( ( argc > 0 ) && ( (*argv)[0] == '-' ) )
      {
         switch( (*argv)[1] ) {

            case 'x':
               sscanf( *argv + 2, "%d", &size_file);
               header_flag = FALSE;
               break;

            case 'd':      /* no dither */
               dith_flag = FALSE;
               break;

            case 'l': 
               larg_flag = TRUE;
               sscanf( *argv+2, "%d", &scale_size );
               break;

            case 's': 
               side_flag = TRUE;
               sscanf( *argv+2, "%d", &square_size );
               break;
            default:
               error( "bad option");
               break;
         } 
  
         argc--;
         argv++;
      }               
   }       

   if ( --argc> 0 ) /* open input file if any */
   {
      if ( ( fp = fopen( *(argv++), "r" ) ) == NULL )
      {
         putchar( BEEP );
         error( "can't open picture file" );
      }
      else
         i_flag = TRUE;
   }

   if ( --argc > 0 ) /* open output file if any */
   {
      if ( ( fq = fopen( *(argv++), "w" ) ) == NULL )
      {
         putchar( BEEP );
         error("can't open output file");
      }
      else
      {
         out_flag = TRUE;
         p = *(argv);
         p1 = store_filename;
         while ( *p1++ = *p++ ); /* copy string */
      }
   }

   l = readfont(fontname, fontbuf, FONTSIZE, &h, &w);

   grey = P_WHITE;
   col = P_RED;

   offset(0,0);
   sterm( 5 );

   setup_map();
   clear_screen();
   setup_text();
   setup_menu();

   /* default -  menu on */

                          /*   DEFAULT VALUES  */
   menu_flag = TRUE;  
   draw_menu();

   if ( i_flag ) {        /* display image */
      if ( header_flag )
         size_file = getc( fp ) & 0377;
         
      if ( size_check() )
         display( X_SIDE, Y_BASE, int_array, TRUE );
      else
         exit( 0 );
   }

   if ( arg_flag ) {   /* to avoid error with command line arg  */
      write_text( "press return to continue " ); /* if command file not used */
      if ( ( c = getchar() ) == EOF )
         clearerr( stdin );  /* clear buffer */
      clear_text();
   }

   if ( side_flag ) /* write size */
   {
      enable( col );
      colour( P_COL );
      write_size( square_size );
   }

   region( square_size );
}



void region( square_size ) /*  middle Mouse Button ( MB ) selects option */
int square_size;
{
   int option;
   int x = 50;  /* initial position for cursor */
   int y = 360;
   char exit_region_flag = FALSE;

   int x_ll, y_ll, side;  /* lower left coordinates & size of square */

   if ( side_flag ) /* side given in command line */
      side = square_size - 1;

   while ( !exit_region_flag )  { 

      enable( P_GREEN );
      cursor( 0, &x, &y);
      if ( (mouse->b & 0377) == 2 ) {

         if ( in_image( x, y ) )      /* select square */
         {
            pick( 2, &x, &y, &x_ll, &y_ll, &side );

         }
         else if ( ( menu_flag ) && ( option = in_box( x, y) ) ) {
            
            menu( option, &x_ll, &y_ll, &side );       

            /* draw cursor - while button held */

            enable( P_GREEN );
            draw_cross( x, y); 

         }     
      } 
      while ( mouse->b == 2 ) /* pause until button released */
         ;
   } /* end of while */  
}  /* end of function */

void pick( c, px, py, px_ll, py_ll, pside ) /* while c = mouse>b
                                moves cursor & returns current position (px,py) 
                                & returns square */
int c, *px, *py, *px_ll, *py_ll, *pside;
{
   static int delta = 3;

   int old_x, old_y, x_offset, y_offset;
   int x_max, y_max, new_side;

   x_max = X_SIDE + size; /* upper limit for display image */
   y_max = Y_BASE + size;

   old_x = *px;   /* stores current position */
   old_y = *py;

   /* calculate offset between mouse position and current position */
   x_offset = SCALE( mouse->x ) - old_x;
   y_offset = SCALE( mouse->y ) - old_y;

   while ( (mouse->b & 0377) == c ) {

      *px = SCALE( mouse->x ) - x_offset;        /* new position */
      *py = SCALE( mouse->y ) - y_offset; 
      limit_image( px, py);               /* restricts cursor to image */

      enable( P_GREEN );
      draw_cross( *px, *py);              /* draw cursor at new position */

      if ( new_flag )
      {
         if ( !side_flag ) /* size not fixed */
            *pside = MIN_SIDE;

         if ( ( *px <= (x_max - 1 - *pside) )
                                          && ( *py <= (y_max - 1 - *pside) ) )
         {
            /* start new square */
 
            *px_ll = *px;    /* lower left hand corner for square */
            *py_ll = *py;
            draw_square( *px_ll, *py_ll, *pside );
            write_size( *pside + 1 );
            new_flag = FALSE;
            delta = max( *pside >> 2, 3 );
         }
         else
         {
            putchar( BEEP );
            write_text("cursor too close to image boundary" );
            move_flag = TRUE;
         }
      }
      else 
      {
         if ( near( *px, *py, *px_ll, *py_ll, delta ) &&
              ( (*px + *pside) < x_max ) &&
              ( (*py + *pside) < y_max ) )
         {
            /* move square - not over boundary */

            clear_square( *px_ll, *py_ll, *pside );
            *px_ll = *px;
            *py_ll = *py;
            draw_square( *px_ll, *py_ll, *pside );
         }
         else if ( !side_flag && 
                   near( *px, *py, *px_ll + *pside, *py_ll + *pside, delta ) )
         {
            /* change side - not growing over boundary */
 
            new_side = max( *px - *px_ll, *py - *py_ll );

            /* to prevent square shrinking to nothing */

            new_side = max( new_side, MIN_SIDE );

            if ( ( (*px_ll + new_side) < x_max ) && 
                 ( (*py_ll + new_side) < y_max ) &&
                 ( *pside != new_side ) )
            {
               clear_square( *px_ll, *py_ll, *pside );
               *pside = new_side;
               draw_square( *px_ll, *py_ll, *pside );
               write_size( *pside + 1 );
               delta = max( *pside >> 2, 3 );
            }
         }
         else 
            draw_square( *px_ll, *py_ll, *pside );
      } 
          

      if ( (*px != old_x) || (*py != old_y) ) { /* erase old cursor position */


         if ( move_flag ) { /* clear text */
            clear_text();
            move_flag = FALSE;
         }

         enable( P_GREEN );
         clear_cross( old_x, old_y);
         old_x = *px;
         old_y = *py;
      }
   }
}

void limit_image( px, py) /* restricts px & py to image */
int *px, *py;
{
   if ( *px < X_SIDE ) *px = X_SIDE;
   if ( *px > X_SIDE + size - 1 ) *px = X_SIDE + size - 1;
   if ( *py < Y_BASE ) *py = Y_BASE;
   if ( *py > Y_BASE + size - 1 ) *py = Y_BASE + size - 1;
}

char near( x, y, x1, y1, delta ) /* returns TRUE if (x,y) nearer than delta 
                                    but not coincident with (x1,y1)
                                    else returns FALSE */
int x, y, x1, y1, delta;
{
   int dx, dy;

   dx = abs( x - x1 );
   dy = abs( y - y1 );

   if ( ( dx <= delta ) && ( dy <= delta ) && 
        ( ( dx > 0 ) || ( dy > 0 ) ) )
      return( TRUE );
   else
      return( FALSE );
}

int max( x, y ) /* returns maximun of x & y */
int x, y;
{
   if ( x >= y )
      return( x );
   else
      return( y );
}

void draw_square( x1, y1, side ) /* draws square with lower left hand corner
                                    (x1, y1) & side size */
int x1, y1, side;
{
   enable( col );
   colour( P_COL );
   hline( x1, x1 + side, y1);
   hline( x1, x1 + side, y1 + side);
   line( x1, y1, x1, y1 + side );
   line( x1 + side, y1, x1 + side, y1 + side );
}

void clear_square( x1, y1, side ) /* clears square with lower left hand corner
                                    (x1, y1) & side size */
int x1, y1, side;
{
   enable( col );
   colour( BLACK );
   hline( x1, x1 + side, y1);
   hline( x1, x1 + side, y1 + side);
   line( x1, y1, x1, y1 + side );
   line( x1 + side, y1, x1 + side, y1 + side );
}


char in_image( x, y) /* returns TRUE if (x,y) inside image
                                                        else returns FALSE */ 
int x, y;
{

   /* check if mouse inside image */

   if ( ( x >= X_SIDE ) && ( x < ( X_SIDE + size ) ) && 
        ( y >= Y_BASE ) && ( y < Y_BASE + size ) ) {
      return( TRUE );
   }
   else
      return( FALSE );
}

void write_size( size ) /* writes size - should only be invoked when 
                                         size changed */
int size;
{
   static char size_text[ 20 ] = ""; /* space to store text */
   static char *text;
   static int x_text;
   static int y_text;

   x_text = xl_menu + 26;  /* 15 + w */
   y_text = y_menu - 420;  /* y_menu - nop * 50 - 70 */

   /* erase old text */

   colour( BLACK );

   text = size_text;

   setpos( x_text, y_text );
   while ( *text ) gputc( *text++ );

   /* write new text */

   if ( size < 10 ) 
      sprintf( size_text, "  %d", size );
   else if ( size < 100 )
      sprintf( size_text, " %d", size );
   else
      sprintf( size_text, "%d", size );

   colour( P_COL );

   text = size_text;

   setpos( x_text, y_text );
   while ( *text ) gputc( *text++ );
}

void clear_size() /* clears size */
{
   static int x_text;
   static int y_text;

   x_text = xl_menu + 26;  /* 15 + w */
   y_text = y_menu - 420;  /* y_menu - nop * 50 - 70 */

   /* erase size */

   enable( P_COL );
   colour( BLACK );

   fill( x_text, y_text, x_text + 3 * w, y_text + h );
}

/* ************************** MENU FUNCTIONS ******************************* */

void menu( option, px_ll, py_ll, pside ) /*  selects option */
int option, *px_ll, *py_ll, *pside;
{
static char answer[ 20 ];
char statement[ 40 ];

   switch ( option ) {

      case 1 :                      /* read intensity file */
         if ( read_file() ) {
            display( X_SIDE, Y_BASE, int_array, TRUE );
         }
         move_flag = TRUE;
         break;

      case 2 :                      /* write out to file */
         if ( out_flag )
         {
            sprintf( statement, "writing to file %s", store_filename );
            write_text( statement );
            write_square( *px_ll, *py_ll, *pside );
            out_flag = FALSE;
         }
         else if ( write_file() )
            write_square( *px_ll, *py_ll, *pside );
         move_flag = TRUE;
         break;

      case 3 :                      /* new box */
         if ( sel_side( pside ) )
            new_flag = TRUE;
         move_flag = TRUE;
         break;
      
      case 4 :                      /* clear selected box */  
         clear_square( *px_ll, *py_ll, *pside );
         clear_size();
         new_flag = TRUE;
         break;

      case 5 :                      /* enlarged display */
         larg_flag = TRUE;
         write_text( "scale factor : " );
         read_response( answer );
         sscanf( answer, "%d", &scale_size );
         clear_text();
         break;

      case 6 :                      /* dither on/off */
         dith_flag = !dith_flag;
         if ( dith_flag )
            write_text( "dither on" );
         else
            write_text( "dither off" );
         move_flag = TRUE;
         break;

      case 7 :                      /* quit */
         clear_screen();
         exit( 0 );                     
         break;

      default:
         break;
   }

} 


void draw_menu() /* draws menu */
{
   char *text;
   int x_text;

   nop = 7;
   x_text = xl_menu + 15;

   /* draw and label menu region */

   enable( P_GREY );
   colour( grey );

   line( xr_menu + 10, Y_BASE, xr_menu + 10, y_menu );

   /* label menu */

   lab_menu( " read", "write", " new", "clear", "scale" ,"dither", " quit" );

   /* write size label */

   text = " size";
   setpos( x_text, y_menu - nop * 50 - 50 );
   while ( *text ) gputc( *text++ );

   /* draw line for text region */

   hline( 0, XPIXELS, YPIXELS - h - 3 );
}      

/***************************** GENERAL FUNCTIONS ***************************/

void write_square( x_ll, y_ll, side ) /* writes out contents of square to 
                                                       file at required scale */
int x_ll, y_ll, side;
{
   int x_image, y_image, new_size;
   register int x, y, y1;    
   register char *p;
   char statement[ 40 ];

   /* write header */

   new_size = ( side + 1 ) / scale_size;
   putc( new_size, fq );

   shift( x_ll, y_ll, &x_image, &y_image ); /* convert to image origin */

   /* write out square */

   for ( y = y_image + side; y > y_image - 1; y -= scale_size ) 
   {
      y1 = size - 1 - y;          /* since picture starts from the top */

      p = int_array + size * y1;  /* start of display line */

      for ( x = x_image; x < x_image + side + 1; x += scale_size )
         putc( *( p + x ), fq);
   }
   fclose( fq );
   sprintf( statement, "file written, size = %d", new_size );
   write_text( statement );
}


void shift( x, y, px_image, py_image) /* changes from screen origin to display
                                    image origin */
int x, y;
int *px_image, *py_image;
{
   *px_image = x - X_SIDE;
   *py_image = y - Y_BASE;
}

char sel_side( pside ) /* selects side length for square */
int *pside;
{
   int square_size;
   char answer[ 20 ];

   write_text( "fixed square size ? (y/n) : " );
   read_response( answer );
   if ( ( answer[ 0 ] == 'y' ) || ( answer[ 0 ] == 'Y' ) )
   {
      write_text("size : ");
      read_response( answer );
      sscanf( answer, "%d", &square_size );
      if ( square_size > size )
      {
         putchar( BEEP );
         write_text( "size too large" );
         return( FALSE );
      }
      else if ( square_size < (MIN_SIDE + 1) )
      {
         putchar( BEEP );
         write_text( "size too small" );
         return( FALSE );
      }
      else
      {
         *pside = square_size - 1;
         side_flag = TRUE;
         clear_size();

         if ( col == P_RED ) /* change colour */
            col = P_CYAM;
         else
            col = P_RED;

         enable( col );
         colour( P_COL );
         write_size( square_size );
         write_text( "select square" );
         return( TRUE );
      }
   }  
   else if ( ( answer[ 0 ] == 'n' ) || ( answer[ 0 ] == 'N' ) ) 
   {
      side_flag = FALSE;
      clear_size();
      write_text( "select square" );

      if ( col == P_RED ) /* change colour */
         col = P_CYAM;
      else
         col = P_RED;

      return( TRUE );
   }
   else
   {
      putchar( BEEP );
      return( FALSE );
   }
}                  

