/**
 * @file  MyroC-image-file.c
 * @brief Implementation file for saving/loading jpeg images to/from a file
 *
 * Revision History
 *
 * Version 1.0 based on a C++ package by April O'Neill, David Cowden,
 *     Dilan Ustek, Erik Opavsky, and Henry M. Walker
 *
 * Developers of the C package for Linux:
 *  Creators Version 2.0 (C functions for utilities,general,sensors,movement):
 *    Spencer Liberto
 *    Dilan Ustek
 *    Jordan Yuan
 *    Henry M. Walker
 *  Contributors Version 2.2-2.3: (C functions for image processing)
 *    Anita DeWitt
 *    Jason Liu
 *    Nick Knoebber
 *    Vasilisa Bashlovkina
 * Revision for Version 2.4:  (image row/column made to match matrix notation)
 *    Henry M. Walker
 *
 * Revisions for Version 3.0 
 *    Henry M. Walker
 *
 *  C ported to Macintosh
 *     Linux/Mac differences required for connections — otherwise same code
 *  OpenGL used to display images, replacing ImageMagick
 *     same [new] code used for both Linux and Macintosh
 *     1 process for robot control
 *     1 process needed for each titled window (not each image, as in 2.2-2.4)
 *  Blocking options (negative duration parameter)
 *     utilize separate thread timer
 *  MyroC implementation files organized by user function as follows:
 *
 * Revisions for Version 3.1 
 *    Henry M. Walker
 *
 *    Picture struct and image functions revised to allow 
 *       192 by  256 images from origial Fluke camera
 *       266 x 427 low-resolution images from Fluke 2
 *         (high-resolution (800 x 1280) too large for more than 2-4 
 *          variables on run-time stack)
 *       storage , retrieval, and display of any images up to 266 x 427 
 *
 *  This program and all MyroC software is licensed under the Creative Commons
 *  Attribution-Noncommercial-Share Alike 3.0 United States License.
 *  Details available at http://creativecommons.org/licenses/by-nc-sa/3.0/us/
 *
 * Revisions for Version 3.2
 *    Henry M. Walker
 *    
 *    Practical range of rBeep duration identified as <= 3.0014 seconds
 *    Image display and processing resolves several matters and adds functionality
 *        function rDisplayPicture completely rewritten
 *                over time OpenGL rountines had encountered troubles
 *                on Linux, specifically glutHideWindow() hid images, but could not be
 *                          restored, with difficulties depending on the graphics card
 *                on Mac, High Sierra generated compile warnings and 
 *                          restricted threads that could display images
 *    function rDisplayImageProcessingErrors added
 *    function rWaitTimedImageDisplay updated substantially
 *
 * Revisions for Version 3.3 
 *    Henry M. Walker
 *
 *    MyroC ported to Windows 10, in addition to Linux and Mac OS X
 *    Additional refinements
 *       text parameter for rGetLightTxt expanded to "Middle" as well as "Center"
 *
 *  This program and all MyroC software is licensed under the Creative Commons
 *  Attribution-Noncommercial-Share Alike 3.0 United States License.
 *  Details may be found at http://creativecommons.org/licenses/by-nc-sa/3.0/us/
 *
 *  
 ********************** implementation overview *************************

> indicates functions implemented in this file

 0. LOW-LEVEL UTILITY (defined in MyroC-utilities.h
                       implemented in MyroC-utilities.c)
    rSend
    rReceive
    rSetOpeningExchange
    rSetBluetoothEcho
    rSetEchoMode
    rCheckHardwareVersionSetCameraSize

 1. GENERAL (mostly MyroC-general.c) 2. SENSOR  (MyroC-sensors.c)
    rConnect  (* MyroC-connect.c)     a.Scribbler Sensors
    rDisconnect  (* MyroC-connect.c)    rGetLightsAll
    rSetConnection                      rGetLightTxt
    rFinishProcessing                   rGetIRAll
    rSetVolume                          rGetIRTxt
    rBeep                               rGetLine
    rBeep2                               
    rSetName                          b.Fluke Sensors
    rGetName                            rGetObstacleAll
    rSetForwardness                     rGetObstacleTxt
    rGetForwardness                     rGetBrightAll
    rSetLEDFront                        rGetBrightTxt
    rSetLEDBack                         rSetIRPower
    rGetBattery                       
    rGetStall                          
    
                     
      Note:
      *  MyroC-connect requires special
         knowledge of a Mac or Linux
         environment, yielding two versions:
         MyroC-connect-mac.c
         MyroC-connect-linux.c

 3. MOVEMENT (MyroC-movement.c)      4. PICTURES (files as indicated)
    rTurnLeft                           rGetCameraSize (MyroC-camera.c)
    rTurnRight                          rTakePicture (MyroC-camera.c)
    rTurnSpeed                          rDisplayPicture (MyroC-display.c)
    rForward                            rWaitTimedImageDisplay(MyroC-display.c)
    rFastForward                      > rSavePicture (MyroC-image-file.c)
    rBackward                         > rLoadPicture (MyroC-image-file.c)
    rMotors                           
    rStop
    rHardStop

 ************************************************************************
 */

#include "MyroC.h" 
#include "MyroC-utilities.h"

#include "MyroC.h" 
#include <stdio.h> 
#include <stdlib.h> 
#include <ctype.h>
#include <string.h>
#include <fcntl.h>       // needed for open
#include <unistd.h>      // needed for close, read, write
#include <termios.h>     // needed to set Bluetooth communication parameters
#include <jpeglib.h>
#include <setjmp.h>


/*****************************************************************/
/* 0. UTILITY - UTILITY - UTILITY - UTILITY - UTILITY - UTILITY  */
/*****************************************************************/

/*
   jpeg auxillary functions based on libjpeg examples
*/
/* struct for error message for jpeg processing */
struct my_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */
  
  jmp_buf setjmp_buffer;	/* for return to caller */
};

/*
   auxillary typedef for libjpeg handling
*/
typedef struct my_error_mgr * my_error_ptr;

/*
   auxillary function for libjpeg handling
*/
/*
 * Routine to replace the standard error_exit method:
 */
void
my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;
  
  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);
  
  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}

/***********************************************************************/
/* 4.) PICTURES PICTURES PICTURES PICTURES PICTURES PICTURES PICTURES  */
/***********************************************************************/
/**
 * This section contains functions for taking and manipulating images
 * All images are constrained with height <= 266 and width <= 427
 *
 * images from robot cameras have varying sizes, depending on the Fluke
 *     images for the original Fluke are 192 (height) by 256 (width)
 *     low-resolution images for the Fluke 2 are 266 by 427
 *     high-resolution images for the fluke 2 are 800 by 1280 (not supported)
 *
 * Bluetooth communication constrains the time required for the Fluke
 *        to take a picture
 *     Typical times:   original fluke:      2- 4 seconds
 *                      Fluke 2 (low res):   4- 6 seconds
 *                      Fluke 2 (high res): 25-30 seconds
 *
 *     BASED ON TIMINGS AND MEMORY CONSIDERATIONS, Myro C ALLOWS
 *     ONLY LOW RESOLUTION IMAGES
 *
 * the Picture struct allows direct access to Pixel data
 * Pictures can be saved and loaded as .jpeg files
 *
 * @note Following standard mathematical convention for a 2D matrix, 
 *       all references to a pixel are given within an array as [row][col]
 * @note    user-defined images may have any size, as long as
 *          height <= 266 and width <= 427
 * @note Following standard mathematical convention for a 2D matrix, 
 *       all references to a pixel are given within an array as [row][col]
 *
 * @warning The Picture struct is defined to be sufficiently large
 *          to store several low-resolution camera images (340756 bytes each)
 *             Experimentally, an array of up to 94 (not 95) Pictures is allowed
 *             However, the display of images requires that image data
 *                be copied, so display of many images may not work
 *          If a program hangs when working with Picture variables,
 *             the issue may involve lack of space on the runtime stack.
 *             To utilize a modest number of Pictures,   
 *             use "ulimit -s"  command, as needed, in a terminal window
 *             For example, ulimit -s 32768
 *             Sizes above 32768 may not be allowed in Linux or Mac OS X
 */

/**
 * Save a Picture to a .jpeg
 * @param pic      pointer to an RGB picture struct from Scribbler 2 camera
 * @param filename the name of the file
 * @pre            filename ends with .jpeg or .jpg.
 * @post           If the file does not exist, a new file will be created.
 * @post           If the file exists, the file will be overwritten.
 *
 * @credit         this code is a slightly modified version 
 *                 of write_JPEG_file(char *, int)
 *                 from example.c from the libjpeg.v8d library directory.
 */

void rSavePicture(Picture *pic, char * filename)
{
  /* working variables */
  int i_height = pic->height; // the height of the image
  int i_width = pic->width;   // the width of the image
  FILE * outfile;            // file for writing JPEG compression object
  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(1);
  }
  int quality = 100; // a measure of quality of the output image from 0 to 100
  
  JSAMPLE * rgb_array=malloc(sizeof(pic->pix_array));
  int i,j;
  int index=0;
  for(i=0;i<i_height;i++)
    {
      for(j=0;j<i_width;j++)
        {
          rgb_array[index++]=(JSAMPLE) pic->pix_array[i][j].R;
          rgb_array[index++]=(JSAMPLE) pic->pix_array[i][j].G;
          rgb_array[index++]=(JSAMPLE) pic->pix_array[i][j].B;
        }
    }
  
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */
  
  /* Step 1: allocate and initialize JPEG compression object */
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  /* Step 2: specify data destination (eg, a file) */
  jpeg_stdio_dest(&cinfo, outfile);
  
  /* Step 3: set parameters for compression */
  cinfo.image_width = i_width; 	/* image width and height, in pixels */
  cinfo.image_height = i_height;
  cinfo.input_components = 3;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
  
  /* Step 4: Start compressor */
  
  jpeg_start_compress(&cinfo, TRUE);
  
  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */
  row_stride = i_width * 3;	/* JSAMPLEs per row in image_buffer */
  
  while (cinfo.next_scanline < cinfo.image_height) {
    row_pointer[0] = & rgb_array[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }
  
  /* Step 6: Finish compression */
  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
  fclose(outfile);
  /* Step 7: release JPEG compression object */
  jpeg_destroy_compress(&cinfo);
  /* Step 8: deallocate space for local rgb_arry, which is no longer needed */
  free (rgb_array);
}

/**
 * @brief Load a picture from a .jpeg file.
 * @param filename  the name of the file
 * @pre             file must exist
 * @pre             file extension must be .jpeg or .jpg
 * @pre             file must be no larger than 266 by 427
 * @return          locally-declared Picture
 * @post            calling program should store returned Picture
 *                     in a previously-declared struct 
 */

/* 
   The main elements of this function are based on read_JEPG_file,
   from the libjpeg library and examples to read jpeg files.
 */
Picture rLoadPicture(char * filename)
{

    /* Step 0:  Initial declarations for the eventual picture, its size, and
     *          parameters, pointers needed by the JPEG library
     */
    Picture to_return;
  
  /* cinfo contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* Use the private extension JPEG error handler, defined above in the 
   * utilities section.
   * The jerr struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct my_error_mgr jerr;
  
  FILE * infile;		/* source file */
  JSAMPARRAY buffer;		/* Output row buffer */
  int out_buffer_len;		/* physical row width in output buffer */
  
  /* Open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * fopen () requires the "b" option on some machines to read binary files.
   */
  /* Open the file */
  if ((infile = fopen(filename, "rb")) == NULL) {
    /* if error, return Picture of dimensions 0 by 0 */
    fprintf(stderr, "can't open %s\n", filename);
    to_return.height = to_return.width = 0;
    return to_return;
  }
  
  /* Step 1: allocate and initialize JPEG decompression object */
  // First deal with error handlers
    
    /* We set up the normal JPEG error routines, then override error_exit. */
    cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * Clean up the JPEG object, close the input file, and 
     * return Picture of dimensions 0 by 0.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);

    to_return.height = to_return.width = 0;
    return to_return;
  }
  
  
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);
  
  /* Step 2: specify data source (eg, a file) */
  
  jpeg_stdio_src(&cinfo, infile);
  
  
  /* Step 3: read file parameters with jpeg_read_header() */
  
  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */
  
  
  /* Step 4: Start decompressor */
  (void) jpeg_start_decompress(&cinfo);
  
  /* read headers to determine picture size */

  to_return.height = cinfo.output_height;
  to_return.width  = cinfo.output_width;
  
  /* temp serves as the array for raw RGB values for the picture 
     within libjpeg, temp will contain unsigned chars, not pixels */

  JSAMPLE * temp = malloc(sizeof(JSAMPLE) * to_return.height 
                                          * to_return.width * 3);
  
  /* jpeg_start_decompress() reports the correct scaled output image dimensions
   * available (also stored in to_return), as well as the output colormap
   * if we asked for color quantization.
   * Make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  out_buffer_len = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
    ((j_common_ptr) &cinfo, JPOOL_IMAGE, out_buffer_len, 1);
  
  /* Step 5: while (lines remain to be read) */
  /*                read jpeg lines; */
  
  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter.
   */
  int index_in_temp = 0;

  while (cinfo.output_scanline < cinfo.output_height) {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    
    /* copy data from row_buffer to temp */
    int i = 0;
    for (i = 0; i < out_buffer_len; i++)
      {
        temp[(index_in_temp)++] = buffer[0][i];
      }
  }
  
  
  /* Step 7: Finish decompression */
  
  (void) jpeg_finish_decompress(&cinfo);
  
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */
  
  /* Step 8: Release JPEG decompression object */
  
  /* This is an important step since it will release a good deal of memory. */
  
  jpeg_destroy_decompress(&cinfo);
  
  /* After finish_decompress, close the input file.
   * This could be done earlier, but placing fclose here allows continued
   * detection of JPEG errors, simplifying the setjmp error logic above. */
  fclose(infile);

  /* Step 9: copy pixel data from temp array into to_return picture to_return */
    int i, j;
    int temp_index = 0;
    for (i = 0; i < to_return.height; i++)
      for (j = 0; j < to_return.width; j++)
        {
          to_return.pix_array[i][j].R = temp[temp_index++];
          to_return.pix_array[i][j].G = temp[temp_index++];
          to_return.pix_array[i][j].B = temp[temp_index++];
        }
    free(temp);// deallocate temp

    /* Step 10: return completed Picture */
  return to_return;  
}
