/*****************************************************
 * timecode -- Program receives a timecode position  *
 *             and an fps (frames per second) value. *
 *             It outputs the frame number that      *
 *             exists at the timecode position for   *
 *             the given fps.                        *
 *                                                   *
 *     Author: Bumblehead                            *
 *                                                   *
 *    Purpose: Yields data to be used in projects    *
 *             that require synchronized video to    *
 *             audio. Given a timecode position of   *
 *             30:00 (30 sec) and an fps of 15, we   *
 *             find that a synchronized video        *
 *             playing at 15fps will chronologically *
 *             align with 30 seconds at frame 450.   *
 *                                                   *
 *      Usage: Program queries user for a timecode   *
 *             position and an fps value and then    *
 *             returns a frame number.               *
 *                                                   *
 *             The timecode should be input in       *
 *             standard timecode format,             *
 *             ie. "##:##:##:##". The fps value must *
 *             be input as an integer number.        *
 *****************************************************/

#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>

/*****************************************************
 * tcformat_validate                                 *
 *          -- validates that char array 'timecode'  *
 *             contains only chars found in a valid  *  
 *             timecode format, ie. integer and ':'  *
 *             values. Also checks that ':' does not *
 *             appear consecutively.                 *
 *                                                   *
 * Parameters:                                       *
 *     timecode -- a timecode value                  *
 *                                                   *
 *    Returns:                                       *
 *        0, if timecode is valid                    *
 *        greater, if timecode is not valid          *
 *****************************************************/
int tcformat_validate(char timecode[15]) {
  int trig = 0;
  int x;
  int len = strlen(timecode);
  for(x=0;x<len;x++) {
       if ((timecode[x] == ':') || (isdigit(timecode[x]))) {
         trig = trig; /* digits and semicolons pass */
       } 
       else if ((x == (len-1)) && (isspace(timecode[x]))) {
         trig = trig;
       } 
       else {
         trig++;
       }
       if ((x > 0) && (timecode[x] == ':') && (timecode[(x-1)] == ':')) {
         trig++; /*consecutive ':' does not pass */
       }
  }
  return trig;  
}

/*****************************************************
 * isnumber                                          *
 *          -- validates that char array 'framerate' *
 *             only contains integer elements        *
 *                                                   *
 * Parameters:                                       *
 *     framerate -- an integer framerate value       *
 *                                                   *
 *    Returns:                                       *
 *        0, if timecode is valid                    *
 *        greater, if timecode is not valid          *
 *****************************************************/
int isnumber(char framerate[10]) {
  int trig = 0;
  int len = strlen(framerate);
  int x;

  for(x=0;x<len;x++) {
       if (isdigit(framerate[x])) {
         trig = trig;
       } 
       else if ((x == (len-1)) && (isspace(framerate[x]))) {
         trig = trig;
       } 
       else {
         trig++;
       }
  }
  return trig;
}

/*****************************************************
 * tcformat_righting                                 *
 *          -- Assumes that the last item in array   *
 *             *time_val[] represents a value of     *
 *             milliseconds. Items in array          *
 *             *time_val[] are moved to the end of   *
 *             the array so that the items will      *
 *             represent the below configuration,    *
 *                                                   *
 *             time_val[0] == hours,                 *
 *             time_val[1] == minutes,               *
 *             time_val[2] == seconds,               *
 *             time_val[3] == milliseconds,          *
 *                                                   *
 *             Finally, if a number corresponding    *
 *             to minutes, seconds, or milliseconds  *
 *             exceeds '60', then the timecode is    *
 *             reformatted to yield a matching       *
 *             timecode where those values are less  *
 *             than 60                               *
 *                                                   *
 * Parameters:                                       *
 *     *time_val -- an array representing a timecode *
 *                  for example,                     *
 *                  1:13:24, is represented by       *
 *                  time_val[0] == 1,                *
 *                  time_val[1] == 13,               *
 *                  time_val[2] == 24,               *
 *                  time_val[3] == 0                 *
 *                                                   *
 *             x -- the number of elements found in  *
 *                  time_val[]                       *
 *                                                   *
 *    Returns:                                       *
 *        void, but directly reformats the time_val  *
 *        array so that items correspond to expected *
 *        time values                                *
 *****************************************************/
void tcformat_righting(int *time_val, int x) {
  int y = 4; /* max len of time_val[] */
  for(x=x;x>=0;x--) {
    time_val[y] = time_val[x];
    y--;
  }
  /* set remainder of time_val[] to 0 */
  while (y >= 0){
    time_val[y] = 0;
    y--;
  }
  for(x=3;x>0;x--) {
    if (time_val[x] >= 60){
      y = floor(time_val[x]/60);
      time_val[x-1] += y;
      time_val[x] -= 60*y;
    }
  }
}

/*****************************************************
 * tcformat_tokenize                                 *
 *          -- tokenizes a string of numeric         *
 *             chars delimited wiith a colon ":" and *
 *             places each token into the integer    *
 *             array *time_val                       *
 *                                                   *
 * Parameters:                                       *
 *     timecode -- a string containing numeric chars *
 *                 and ":" delimiter chars           *
 *                                                   *
 *    *time_val -- an integer array with 4 elements  *
 *                                                   *
 *    Returns:                                       *
 *        void, but directly places numeric value    *
 *        'tokens' found in timecode[] into the      *
 *        elements of *time_val                      *
 *****************************************************/
void tcformat_tokenize(char timecode[15], int *time_val){
  char *time_ptr = NULL;
  int x = 0;

  time_ptr = strtok(timecode, ":");
  while((time_ptr != NULL) && (x < 4)) {
    sscanf(time_ptr, "%d", &time_val[x]);
    time_ptr = strtok(NULL, ":");
    x++;
  }
  tcformat_righting(time_val, x);
}

/*****************************************************
 * sec_in_timecode                                   *
 *          -- Receives an array with elements       *
 *             corresponding to a timecode position. *
 *             Returns the number of seconds         *
 *             found in the given timecode. An array *
 *             representing 1 minute will return     *
 *             60, because 60 seconds are found in   *
 *             one minute.                           *
 *                                                   *
 * Parameters:                                       *
 *     time_val[] -- an array with elements          *
 *                   corresponding to the following  *
 *                   time mesurements,               *
 *                                                   *
 *                   time_val[0] == hours,           *
 *                   time_val[1] == minutes,         *
 *                   time_val[2] == seconds,         *
 *                   time_val[3] == milliseconds     *
 *                                                   *
 *    Returns:                                       *
 *        0, if timecode is valid                    *
 *        greater, if timecode is not valid          *
 *****************************************************/
double sec_in_timecode(int time_val[]) {
  int hour = time_val[0];
  int minute = time_val[1];
  int second = time_val[2];
  int millis = time_val[3];

  double total = 0;
  total += (hour*3600.0000);
  total += (minute*60.0000);
  total += (second*1.0000);
  total += (millis/60.0000);

  return total;
}

int main(void) {
  char timecode[15];               /* timecode value in string */
  int time_val[] = {0, 0, 0, 0};   /* time_val[] = {hour,min,sec,ms} */

  char framerate[10];              /* framerate value in string */
  double frame;                    /* frame number matching the timecode*/
  float fps;                       /* number of frames per second */

  int validated;                   /* validation status, 0 == valid*/
  double total;                    /* total number of seconds in timecode*/

  printf("     timecode: ");
  fgets(timecode, sizeof(timecode), stdin);
  validated = tcformat_validate(timecode);
  
  if (validated > 0) {
    printf("    invalid input. (ex. timecode: 1:23:23:53)\n");
    validated = 2;
  }
  else {
    tcformat_tokenize(timecode, time_val);
    total = sec_in_timecode(time_val);

    printf("   framespeed: ");
    fgets(framerate, sizeof(framerate), stdin);
    validated += isnumber(framerate);
  }

  if (validated == 1) {
    printf("    invalid input. (ex. fps: 15)\n");
  }

  if (validated == 0) {
    sscanf(framerate, "%f", &fps); /* %f -> float/double */
    frame = total*fps;
    printf("        frame: %.2f", frame);
    printf(" (%d:%d:%d:%d)\n", 
    time_val[0], time_val[1], time_val[2], time_val[3]);
  }

  return 0;
}
