/* 
   C-Version of gdplaytimefilter.pl
   gdplaytimefilter: Giant Disc play time filter
   reads play time state from mpg123 (stdin) and writes it to the corresponding
   playerstate-record in the database.


   we need to read the mpg123 status char by char. \r must be interpreted
   as line end (translation \r -> \n in a pipe with 'tr' is not possible,
   because tr writes buffered to stdout. But we need undelayed response.)
   Better ideas?


   The program takes the following parameters
     1) char *gdhost	 IP address or hostname of database host. 
                         if gdhost=="test", then test mode without db updates
     2) int playerid	 player-ID
     3) int audiochannel sound channel of player-id
     4) int length       total length of track in seconds
     5) char *playerapp  application, of which the output is to be analyzed.
                         Currently existing filters for: mpg123, ogg123, mplayer


                         44100 samples/sec
   38.281 frames/sec =   ------------------
                         1152 samples/frame
 */

#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
//#include <sys/types.h>

#include "mysql/mysql.h"

#define app_mpg123    0
#define app_ogg123    1
#define app_mplayer   2
#define app_bluemp3   3

#define forthsec      10      /*nb frames in a forth of a second (roughly)*/

main (int argc, char **argv)
{
  char line[500], sqlcmd[500], *pos2, *pos3, *playerid, *audiochannel, *gdhost;
  int  chr, chrcnt, linelen, lineok, frames, framestotal, frameslastupdate, format_m_s_t;
  int  ftype, tracklength;
  int  min, sec, tsec, hsec;	// ogg, mplayer time calculations
  int  test_mode;
  MYSQL mysql;

  if(argc != 6){
    printf("gdplaytimefilter needs 5 parameters (you gave %i): gdhost, playerid, audiochannel, tracklength (seconds), player-application (mpg123, ogg123, mplayer...) \n", argc);
    exit (-1);
  }
  gdhost   = argv[1];
  playerid = argv[2];
  audiochannel = argv[3];
  tracklength  = atoi(argv[4]);
  if (strcmp(argv[5], "mpg123")==0) ftype = app_mpg123;
  if (strcmp(argv[5], "ogg123")==0) ftype = app_ogg123;
  if (strcmp(argv[5], "mplayer")==0) ftype = app_mplayer;
  if (strcmp(argv[5], "l2send")==0) ftype = app_bluemp3;
  test_mode = (strcmp(gdhost, "test")==0);
  if(   test_mode )printf ("\ngdplaytimefilter in test mode\n\n");
  

  //*** Open Database connection
  mysql_init(&mysql);
  if(   test_mode 
     || mysql_real_connect(&mysql, gdhost, NULL/*user*/, NULL/*pwd*/,
  	  "GiantDisc", 0/*port*/, NULL/*socket*/, 0/*client-flag*/))
  {
    //*** Init
    if (test_mode){
      printf ("Play Time Filter started (test mode). params %s %s %s app=%i\n", gdhost, playerid, audiochannel, ftype);
    }
    else{
      sprintf(sqlcmd,
        "UPDATE playerstate \
          SET framesplayed=%i, framestotal=%i \
          WHERE playerid=%s AND audiochannel=%s",
          0/*frames*/, 0/*framestotal*/, playerid, audiochannel
        );
      mysql_query(&mysql, sqlcmd);
    }
    frameslastupdate = 0;
    
    //*** Get 'mpg123' verbose output
    //*   Example of an output line:
    //*   Frame#   210 [14226], Time: 00:05.48 [06:11.61],


    //*** Get 'ogg123' verbose output
    //*   Example of an output line:
    //*   Track number: 3
    //*   Album: the revolution
    //*   Genre: 43
    //*   Time: 00:03.78 [05:36.04] of 05:39.83  ( 96.9 kbps)  Output Buffer  96.9%
    //*   Time: 00:04.06 [05:35.76] of 05:39.83  (104.9 kbps)  Output Buffer  96.9%


    //*** Get 'mplayer' verbose output
    //*   Example of an output line:
    //*   
    //*   [... a lot of text ...]    
    //*   Building audio filter chain for 44100Hz/2ch/16bit -> 44100Hz/2ch/16bit...
    //*   Video: no video
    //*   Starting playback...
    //*   
    //*   A:         9.9  3.8% 0%   ^M
    //*   A:         9.9  3.8% 0%   ^M
    //*   A:         9.9  3.8% 0%   ^M
    //*   A:         9.9  3.8% 0%   ^M
    //*   A:         9.9  3.8% 0%   ^M
    //*   A:         9.9  3.8% 0%   ^M
    //*   A:        10.0  3.8% 0%   ^M
    //*   A:        10.0  3.8% 0%   ^M
    //*   A:        10.0  3.8% 0%   ^M
    //*   A:        10.1  3.8% 0%   ^M
    //*[...]
    //*   A:      2:10.1 33.7% 0%   ^M
    //*   A:      2:10.1 33.7% 0%   ^M
    
    
    //*** Get 'l2send' (BlueMP3) Player output
    //*   l2send - V1.3, (c) 2003-2004 Till Harbaum <till@harbaum.org>
    //*   Connected on PSM 11. Device ID: "BlueMP3 SPP/L2CAP V1.1"
    //*   Playing 00/tr0x7609bb0a-1.mp3 ...
    //*     [----------------------------------------] 0:00, 5376 kbit/s               
    //*     [----------------------------------------] 0:00, 3584 kbit/s               
    //*     [----------------------------------------] 0:00, 5376 kbit/s               
    //*     [#---------------------------------------] 0:04, 152 kbit/s               
    //*     [##--------------------------------------] 0:13, 125 kbit/s               
    //*     [##--------------------------------------] 0:14, 124 kbit/s               



    while(1){
      linelen = 0;
      lineok  = 0;
      chrcnt  = 0;
      if (ftype == app_mpg123){
        while ((chr=getchar()) != EOF){
          if (chrcnt==0 && chr == 'F') lineok=1;  // lines start with keyw "Frame#"
          if (chr == '\r' || chr == '\n') break;
          if (isdigit(chr) || chr == ' '){
        	line[linelen++] = chr;
          }
          chrcnt++;
        }
      }
      
      if (ftype == app_ogg123){
        while ((chr=getchar()) != EOF){
          if (chrcnt==0 && chr == 'T') lineok=1;  // lines start with keyw "Time:"
          if (chrcnt==1 && chr != 'i') lineok=0;
          if (chrcnt==2 && chr != 'm') lineok=0;
          if (chr == '\r' || chr == '\n') break;
          if (isdigit(chr)){ line[linelen++] = chr;}
          else{              line[linelen++] = ' ';}
          chrcnt++;
        }
      }
      
      if (ftype == app_mplayer){
        format_m_s_t = 0;
        while ((chr=getchar()) != EOF){
          if (chrcnt==0 && chr == 'A') lineok=1;  // lines start with keyw "A:"
          if (chrcnt==1 && chr != ':') lineok=0;
          if (chr == '\r' || chr == '\n') break;
          if (chrcnt >2 && chr == ':') format_m_s_t=1;  // mplayer has 2 time formats: m:s.ts, s.ts
          if (isdigit(chr)){ line[linelen++] = chr;}
          else{              line[linelen++] = ' ';}
          chrcnt++;
        }
      }
      
      if (ftype == app_bluemp3){
        while ((chr=getchar()) != EOF){
          if (chrcnt==0 && chr == ' ') lineok=1;  // lines start with keyw "  ["
          if (chrcnt==1 && chr != ' ') lineok=0;
          if (chrcnt==2 && chr != '[') lineok=0;
          if (chr == '\r' || chr == '\n') break;
          if (isdigit(chr)){ line[linelen++] = chr;}
          else{              line[linelen++] = ' ';}
          chrcnt++;
        }
      }
      
      
      
      line[linelen++] = '\0';
      if (chr == EOF) break;
  
      frames = 0;
      framestotal = 0;
      if (lineok){
        //printf("line recieved: %s \n", line);
        if (ftype == app_mpg123){
          frames      = strtol(line, &pos2, 10); // pos2 points to first char after first number
          // framestotal = strtol(pos2, &pos3, 10)/*frames-ramining*/+frames;
          // take the parameter to set the total playtime. this is required for vbr-mp3 tracks, where mpg123 cannot calculate a reliable total time
          framestotal = (tracklength * 44100) / 1152;  // this is not always reliable - but mplayer doesn't print the total time
        }
        if (ftype == app_ogg123){
          min  = strtol(line, &pos2, 10); 
          sec  = strtol(pos2, &pos3, 10); 
          hsec = strtol(pos3, &pos2, 10); 
          frames = (((min*6000)+(sec*100)+hsec) * 441) / 1152;

          min  = strtol(pos2, &pos3, 10); // skip 3 numbers
          min  = strtol(pos3, &pos2, 10);
          min  = strtol(pos2, &pos3, 10);

          min  = strtol(pos3, &pos2, 10); 
          sec  = strtol(pos2, &pos3, 10); 
          hsec = strtol(pos3, &pos2, 10); 
          framestotal = (((min*6000)+(sec*100)+hsec) * 441) / 1152;
        }
        if (ftype == app_mplayer){
          if (format_m_s_t){  // format "min:sec.tsec"
            min  = strtol(line, &pos2, 10); 
            sec  = strtol(pos2, &pos3, 10); 
            tsec = strtol(pos3, &pos2, 10); 
            //printf("min=%i, sec=%i, tsec=%i\n", min, sec, tsec);
          }
          else{               // format "sec.tsec"
            min  = 0;
            sec  = strtol(line, &pos2, 10); 
            tsec = strtol(pos2, &pos3, 10); 
            //printf("no min, sec=%i, tsec=%i\n", sec, tsec);
          }
          frames = (((min*600)+(sec*10)+tsec) * 4410) / 1152;

          framestotal = (tracklength * 44100) / 1152;  // this is not always reliable - but mplayer doesn't print the total time
        }
        if (ftype == app_bluemp3){
          min  = strtol(line, &pos2, 10); 
          sec  = strtol(pos2, &pos3, 10); 
          frames = (((min*60)+sec) * 44100) / 1152;

          framestotal = (tracklength * 44100) / 1152;  // this is not always reliable - but l2send doesn't print the total time
        }

        
        if (frames > (frameslastupdate+forthsec)){ // not more than 4 updates/second
          sprintf(sqlcmd,
    	     "UPDATE playerstate SET framesplayed=%i, framestotal=%i \
              WHERE playerid=%s AND audiochannel=%s",
              frames, framestotal, playerid, audiochannel
    	    );
          if (test_mode){
            printf("QUERY: %s\n", sqlcmd);
          }
          else{
            //printf("QUERY: %s\n", sqlcmd);
            mysql_query(&mysql, sqlcmd);
          }
          frameslastupdate = frames;
        }
      }
    }
  }
  else{
    printf("ERROR: gdplaytimefilter can't open database connection\n");
    printf("       %s\n", mysql_error(&mysql));
  }
  //printf ("Terminated: Playtime filter\n");
}

