/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2007
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* camera.c :: Midnight Code UVC camera example program			*/


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <errno.h>

#include <libmidnightcode.h>


int
main(int argc, char **argv) {
  struct midnightcode_camera_head_t * cameras;
  struct midnightcode_camera_element_t * camera;
  char image_file[32];
  void * tmp_void_p;
  char * buffer;
  int cfg_fps;
  int cfg_single;
  int cfg_silent;
  int cfg_time;
  int cfg_width;
  int cfg_height;
  int cfg_help;
  int buffer_size;
  int total_frames;
  int frame;
  int cam;
  FILE * fp;
  int ret_i;


  midnightcode_list_init(camera_struct_head, midnightcode_camera_element_t);
  tmp_void_p = (void *)&camera_struct_head;
  cameras = tmp_void_p;

  buffer = NULL;
  buffer_size = 0;
  cam = 0;
  fp = 0;


  struct midnightcode_switch_t switches[] = {
    { "f", "fps", "Images/frames per second (15)",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_fps },
    { "o", "one", "One file per image/frame",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_single },
    { "s", "silent", "Do not display unneeded output",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_silent },
    { "t", "time", "Record n seconds worth of frames (60)",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_time },
    { "x", "width", "Image/frame width (320)",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_width },
    { "y", "height", "Image/frame height (240)",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_height },

    { "h", "help", "Display this help/usage screen",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_help },
    { 0 }
  };


  cfg_fps = 15;
  cfg_single = 0;
  cfg_silent = 0;
  cfg_time = 60;
  cfg_width = 320;
  cfg_height = 240;
  cfg_help = 0;


  if((ret_i = midnightcode_switch_process(argc, argv, switches)) != 0) {
    fprintf(stdout, "\n");
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON,
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    fprintf(stdout, "    Error in switches, switch \"%s\" is unkown"
      " at parameter number %d.\n\n", argv[ret_i], ret_i);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT, argv[0],
      switches);
    exit(1);
  }
  if(cfg_help) {
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON,
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT, argv[0],
      switches);
    exit(1);
  }
  fflush(stdout);


  if(cfg_time < 1)
    cfg_time = 60;
  if(cfg_fps != 5 &&
    cfg_fps != 10 && cfg_fps != 15 &&
    cfg_fps != 20 && cfg_fps != 25 && 
    cfg_fps != 30)
    cfg_fps = 15;
  if(!((cfg_width == 160 && cfg_height == 120) ||
    (cfg_width == 320 && cfg_height == 240) ||
    (cfg_width == 640 && cfg_height == 480))) {
    cfg_width = 320;
    cfg_height = 240;
  }
  total_frames = cfg_fps * cfg_time;


  midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
  midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
    MIDNIGHTCODE_LICENSE_TEXT_ALL);
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL, "UVC Camera Example: %s", argv[0]);

  fprintf(stdout, "\n");
  fflush(stdout);


  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL,
    "Probing camera devices");
  if(midnightcode_camera_list(cameras) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
      MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
      "Total of [%d] cameras found", midnightcode_list_count(cameras));
    midnightcode_list_for_each(camera, cameras) {
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
        MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
        "CAM[%s] Found, enabled=%d", camera->device, camera->enabled);
    }
  }

  if(midnightcode_list_count(cameras) == 0) {
    fprintf(stdout, "\n");
    fflush(stdout);
    exit(1);
  }

  midnightcode_list_for_each(camera, cameras) {

    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "CAM[%s]: Open", camera->device);
    if(midnightcode_camera_open(camera,
      cfg_width, cfg_height, cfg_fps, V4L2_PIX_FMT_MJPEG, V4L2_MEMORY_MMAP)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      continue;
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }

    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
      MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
      "CAM[%s] Params, width=%d height=%d fps=%d",
      camera->device, cfg_width, cfg_height, cfg_fps);

    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "CAM[%s]: Action ResetPanTilt", camera->device);
    if(midnightcode_camera_action_resetpantilt(camera, 3)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }

    sleep(3);

    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "CAM[%s]: Activate", camera->device);
    if(midnightcode_camera_activate(camera)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }

    if(cfg_silent)
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
        MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
        "CAM[%s] Capture, frames=%d time=%d",
        camera->device, total_frames, cfg_time);
    for(frame = 0; frame < total_frames; frame++) {

      /* NOTE: single file name results in a concatonated file */
      memset(image_file, 0, sizeof(image_file));
      if(cfg_single) {
        snprintf(image_file, sizeof(image_file), "%02u-%08u.mjpg", cam, 0);
      } else {
        snprintf(image_file, sizeof(image_file), "%02u-%08u.mjpg", cam, frame);
      }

      if(!cfg_silent)
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
          MIDNIGHTCODE_STATUS_STYLE_NORMAL,
          "CAM[%s]: File Open", camera->device);
      if((fp = fopen(image_file, "a")) == NULL) {
        if(!cfg_silent)
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
        exit(1);
      } else {
        if(!cfg_silent)
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }

      if(!cfg_silent)
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
          MIDNIGHTCODE_STATUS_STYLE_NORMAL,
          "CAM[%s]: Read", camera->device);
      if(midnightcode_camera_read(camera, &buffer, &buffer_size)) {
        if(!cfg_silent)
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        if(!cfg_silent)
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
        if(buffer_size > 0 && buffer != NULL) {
          if(!cfg_silent)
            midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
              MIDNIGHTCODE_STATUS_STYLE_NORMAL,
              "CAM[%s]: File Write", camera->device);
          if(fwrite(buffer, buffer_size, 1, fp) != 1) {
            if(!cfg_silent)
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
          } else {
            if(!cfg_silent)
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
          }
          free(buffer);
        }
      }
      buffer = NULL;
      buffer_size = 0;

      if(!cfg_silent)
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
          MIDNIGHTCODE_STATUS_STYLE_NORMAL,
          "CAM[%s]: File Close", camera->device);
      if(fclose(fp)) {
        if(!cfg_silent)
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        if(!cfg_silent)
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }
    }
    cam++;

    sleep(1);

    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "CAM[%s]: Deactivate", camera->device);
    if(midnightcode_camera_deactivate(camera)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }

    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "CAM[%s]: Close", camera->device);
    if(midnightcode_camera_close(camera)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }

  }


  fprintf(stdout, "\n");
  fflush(stdout);


  exit(0);
}

