#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <MyroC.h>

#include "movie.h"

/* List of characters constituting valid menu options */
const char * OPTIONS = "pcfq"; 

/* Constants for movie acquisition and playback */
const double FRAME_DISPLAY_SECONDS = 2;
const double MOTOR_SPEED = 0.05;

/* Support platform hack for displaying frames correctly */
#ifndef NON_BLOCKING_DISPLAY
#define NON_BLOCKING_DISPLAY 1
#endif

/* Print main menu options.
 *
 * Preconditions: No additional.
 * Postconditions: Formatted menu appears in stdout (i.e., Terminal) */
void
printMainMenu (void)
{
  printf ("Select an option:\n");
  printf ("  p -- play movie\n");
  printf ("  c -- capture and insert image sequence\n");
  printf ("  f -- filter movie subsequence\n");
  printf ("  q -- quit the program\n");
} // printMainMenu


/* Prompt and read a choice from the user.
 *
 * Preconditions: 
 *   stdin (i.e., Terminal) is available for reading
 * Postconditions:
 *   Repeatedly prompts for a choice until a valid character option is provided
 *      with no trailing characters aside from a newline.
 *   Returns the character choice, converted to lower case.
 */
char
getChoice (void)
{
  int choice;           /* getchar returns an int for error checking */
  bool valid = false;

  do {                                /* Input loop */
    printf ("Enter your choice: ");
    choice = tolower (getchar());
    
    if (getchar() != '\n') {          /* Was more than one char entered? */
      while (getchar() != '\n')       /* Read to line end */
        ;
      printf ("Too many inputs. Please try again.\n"); /* Report input error */
      continue; /* Restart the do loop */
    } // if
    
    if (strchr (OPTIONS, choice) != NULL) /* Valid input? */
      valid = true; /* Valid input found */ 
    else
      printf ("Invalid input. Please try again.\n");
  } while (!valid);
  
  return (char)choice;
} // getChoice


/* Display a single picture frame 
 *
 * Preconditions: 
 *   pic points to a non-null and valid Picture struct
 * Postconditions:
 *   The picture is displayed in a window entitled "movie" for approximately
 *     FRAME_DISPLAY_SECONDS
 * Practica:
 *   Some platforms occasionally do not redisplay windows properly with a
 *     blocking call to MyroC's rDisplayPicture. Hence, a slightly extended 
 *     non-blocking call is made in combination with a call to sleep(), as 
 *     a workaround when NON_BLOCKING_DISPLAY is defined as a non-zero value.
 *   The type of the parameter is not "const Picture *" because the movie API
 *     using apply takes only "Picture *".
 */
void
showFrame (Picture * pic)
{
  if (NON_BLOCKING_DISPLAY) {
    rDisplayPicture (pic, -FRAME_DISPLAY_SECONDS - 2, "movie");
    sleep (2);
  } else {
    rDisplayPicture (pic, FRAME_DISPLAY_SECONDS, "movie");
  }
} // showFrame


/* Play a movie at a fixed frame rate by repeatedly showing frames in sequence
 *
 * Preconditions:
 *   movie != NULL
 *   movie points to a valid movie struct
 * Postconditions:
 *   Each movie frame has been displayed in sequence, and 
 *     the final window is closed
 */
void
playMovie ( movie_t * movie) {  apply (movie,showFrame); } // playMovie


/* ADD CLEAR DOCUMENTATION HERE */
void
filterMovie ( movie_t * movie) { /* apply (movie,???); */} // filterMovie


/* Register the quit operation and clear the movie's resources
 *
 * Preconditions:
 *   movie != NULL
 *   movie points to a valid movie struct
 * Postconditions:
 *   The movie has been cleared
 */
void
quitProgram (movie_t * movie) {
  printf ("Movie composer program completed.\n");
  printf ("Cleaning up...");
  clear (movie);
  printf ("done.\n");
 } // quitProgram


/* ADD CLEAR DOCUMENTATION HERE */
void
captureScene (movie_t * movie)
{
  /* ADD CODE HERE */
} // captureScene


/* Dispatch processing of the movie for the given user choice
 *
 * Preconditions:
 *   choice is one of 'p', 'c', 'f', or 'q'
 *   movie != NULL
 *   movie refers to a valid movie struct
 * Postconditions:
 *   The appropriate procedure for choice is dispatched
 */
void
processChoice (char choice, movie_t * movie)
{
  switch (choice) {
  case 'p': playMovie (movie);      break;
  case 'c': captureScene (movie);   break;
  case 'f': filterMovie (movie);    break;
  case 'q': quitProgram (movie);    break;
  default: assert( false ); /* Unreachable */
  }
} // process


/* Main program loop: prompt, input, process */
int
main (void)
{
  rConnect ("/dev/rfcomm0");
  
  movie_t movie = create();

  char choice;

  do {
    printMainMenu();
    choice = getChoice();
    processChoice (choice, &movie);
  } while (choice != 'q');

  rDisconnect();
  printf ("Exiting.\n");
} // main
