/*
Copyright (C) 2002, 2003 Brian Victor Fernandes

This file is part of XMMS mp3cue.

XMMS mp3cue 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; either version 2 of the License, or
(at your option) any later version.

XMMS mp3cue 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
along with XMMS mp3cue; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

XMMS mp3cue: http://brianvictor.tripod.com/mp3cue.htm
Email: brianv@phreaker.net

Brian Fernandes
503, Miaami Apts. II,
St. Vincents Street,
Pune 411001
INDIA
*/

#include <gtk/gtk.h>
#include <stdio.h>
#include <plugin.h>
#include "xmmsctrl.h"
#include "defs.h"
#include <malloc.h>
#include <string.h>

#include <string>

using namespace std;

#define MAXX 1024
typedef unsigned char BOOL;

extern void quick_message(gchar *message);
extern void fixtimes(gint c_row);
extern void fixcolors(gint redrow, gint blackrow);
extern BOOL LoadID3(const char *fname);
extern gint findsong(int time);


extern GeneralPlugin mp3cue;
extern gchar *album_name_s;
extern gchar *album_artist_s;
extern GtkWidget *sub_playlist;
extern gint p_row, rows;


 typedef enum {
  CTFILE,
  TITLE,
  PERFORMER,
  TRACK,  
  INDEX,
  PREGAP,
  POSTGAP,
  SONGWRITER,
  FLAGS,
  ISRC,
  REM,
  CDTEXTFILE,
  CATALOG
} CUE_TAGS;


gchar *get_cue_name(gchar *o_fname);
int tagnum(const gchar *tag);
void free_n_fname(GtkWidget *save_cue);

void save_cue_sheet(GtkWidget *save_cue);
int find_cue_sheet(gint pos);
void parse_cue_sheet(FILE *cue_sheet, char *cue_mem = NULL);
void export_cue_sheet();
void import_cue_sheet();
void load_cue_sheet(GtkWidget *open_cue, gpointer data);
string Make_Cue_String();

void addrow(gchar *s_name, gchar *a_name, gchar *timed);
void get_q_data(const gchar *instring, gchar *outstring);
int tagnum(const gchar *tag);



gchar *o_fname, *n_fname;



gchar *get_cue_name(gchar *o_fname) 
{
  gchar *cue_name, *temp;

  //find the extension
  for(temp = o_fname+strlen(o_fname)-1; *temp != '.' && temp != o_fname; temp--);
  
  //has it got a 3 letter extension ?, if so , replace the extension with .cue
  if(temp != o_fname && strlen(temp) == 4) {
    cue_name = (gchar *)malloc(strlen(o_fname)+1);
    strcpy(cue_name, o_fname);
    strcpy(cue_name + strlen(cue_name)-4, ".cue");
  }

  else {
    cue_name = (gchar *)malloc(strlen(o_fname)+5);
    strcpy(cue_name,o_fname);
    strcat(cue_name,".cue");
  }
  return cue_name;
}

gchar *get_cue_name1(const gchar *o_fname) 
{
  gchar *cue_name;

  if(!(strcasecmp(o_fname+strlen(o_fname)-4,".mp3"))) {
    cue_name = (gchar *)malloc(strlen(o_fname)+1);
    strcpy(cue_name, o_fname);
    strcpy(cue_name+strlen(cue_name)-4,".cue");
  }
  else {
    cue_name = (gchar *)malloc(strlen(o_fname)+5);
    strcpy(cue_name,o_fname);
    strcat(cue_name,".cue");
  }
  return cue_name;
}




int tagnum(const gchar *tag) 
{
  if(!strcasecmp(tag,"FILE")) return CTFILE;
  else if(!strcasecmp(tag,"TRACK")) return TRACK;
  else if(!strcasecmp(tag,"TITLE")) return TITLE;
  else if(!strcasecmp(tag,"PERFORMER")) return PERFORMER;  
  else if(!strcasecmp(tag,"TRACK")) return TRACK;
  else if(!strcasecmp(tag,"INDEX")) return INDEX;
  else if(!strcasecmp(tag,"PREGAP")) return PREGAP;  
  else if(!strcasecmp(tag,"POSTGAP")) return POSTGAP;
  else if(!strcasecmp(tag,"SONGWRITER")) return SONGWRITER;
  else if(!strcasecmp(tag,"FLAGS")) return FLAGS;  
  else if(!strcasecmp(tag,"ISRC")) return ISRC;
  else if(!strcasecmp(tag,"REM")) return REM;
  else if(!strcasecmp(tag,"CDTEXTFILE")) return CDTEXTFILE;
  else if(!strcasecmp(tag,"CATALOG")) return CATALOG;  
  else return -1;
}

void get_q_data(const gchar *instring, gchar *outstring)
{
  //find the data enclosed by the quotes
  gchar *qstart = strchr(instring,'\"');
  gchar *qend = strrchr(instring,'\"');
  if(qstart && qend) {
    if(qend-qstart-1 < MAXX && qend-qstart-1 > 0) {
      strncpy(outstring, qstart+1, (qend-qstart-1));
      outstring[qend-qstart-1] = 0;
    }
    
    else {
      strncpy(outstring, qstart+1, MAXX-1);
      outstring[MAXX-1] = 0;
    }
  }
  else *outstring = 0;
}

void addrow(gchar *s_name, gchar *a_name, gchar *timed)
{
  static gchar *rdata[2];
  static gchar data[MAXX];
  static gint min, sec, frame;

  rows++;

  //check for completeness of previous data
  sprintf(data, "%s ~ %s", a_name, s_name);
  *s_name = *a_name = 0;

  rdata[0] = data;
  if(*timed) {
    SCANTIME(timed,min,sec,frame);
    PTIME(timed, min, sec, frame);
  }
  else PTIME(timed, 0, 0, 0);
  
  rdata[1] = timed;
  gtk_clist_append(GTK_CLIST(sub_playlist), rdata);
  
  *timed = 0;

}

void parse_cue_sheet(FILE *cue_sheet,char *cue_mem) 
{
  gboolean track_started = FALSE;

  gchar lined[MAXX];
  gchar tag[MAXX];
  gchar s_name[MAXX], a_name[MAXX];
  gchar timed[10];
  
  rows = 0;    

  *a_name = 0;
  *s_name = 0;
  *timed = 0;

  char *d_ptr=cue_mem, *d_end=NULL;
  if(cue_mem) 
    d_end = cue_mem + strlen(cue_mem);
  
  while(1) {
    if(!cue_mem) {
      //if we are reading from the cue file 
      if(!fgets(lined, MAXX, cue_sheet)) break;
    }
    else {
      //if we are reading from memory
      if(d_ptr >= d_end) break;
      if(!sscanf(d_ptr, "%[^\r\n]s",lined)) {
#ifdef DEBUG
	printf("Invalid Input ...\n%s\n",lined);
#endif
	d_ptr++;
      }

      else {
#ifdef DEBUG
	printf("Line scanned \n%s\n",lined);
#endif
	d_ptr += strlen(lined) + 2;
      }
    }
   
      
    *tag = 0;
    sscanf(lined, "%s", tag);
#ifdef DEBUG
    printf("Tag is %s\n",tag);
#endif
    
    switch(tagnum(tag)) {
      
    case PERFORMER:
      get_q_data(lined, a_name);
      if(!track_started) {
	album_artist_s = (gchar *)malloc(strlen(a_name)+1);
	strcpy(album_artist_s, a_name);
	*a_name = 0;
      }
      break;

    case TITLE:
      get_q_data(lined, s_name);
      if(!track_started) {
	album_name_s = (gchar *)malloc(strlen(s_name)+1);
	strcpy(album_name_s, s_name);
	*s_name = 0;
      }
      break;
      
    case INDEX:
      sscanf(lined,"%*s %*s %s",timed);
#ifdef DEBUG
      printf("Time is %s\n",timed);
#endif
      break;
      
      //when we reach this tag, we know that the previous track has ended and a new track has begun, so store the old track
    case TRACK:
      if(!track_started) track_started = TRUE;

      else {
	if(*a_name || *s_name || *timed) addrow(s_name, a_name, timed);
      }
      
      break;

#ifdef DEBUG
    default:
      printf("No matching data\n");
#endif

    }
  }
  //if we still have some info left over, add it also
  
  if(*s_name || *a_name || *timed) addrow(s_name, a_name, timed);
  //now fix the times
  p_row = findsong(xmms_remote_get_output_time(mp3cue.xmms_session));
  fixtimes(p_row);
  fixcolors(p_row, -1);

}

int find_cue_sheet(gint pos) 
{
  gchar *o_name, *n_name;
  FILE *cue_file;
  o_name = xmms_remote_get_playlist_file(mp3cue.xmms_session,pos);
  o_fname = o_name; 
  if(o_name) {
    n_name = get_cue_name(o_name);
#ifdef DEBUG
    printf("Locating Cue file: %s\n",n_name);
#endif
    if( (cue_file=fopen(n_name,"rt")) ) {
      parse_cue_sheet(cue_file);
      free(n_name);
      fclose(cue_file);
    }
    
    else {
#ifdef DEBUG
      printf("Cue file not found, checking mp3\n");
#endif
      LoadID3(o_name);
    }
    
    return 1;
  }
  return 0;
}


void free_n_fname(GtkWidget *save_cue)
{
  if(n_fname) free(n_fname);
}


void load_cue_sheet(GtkWidget *open_cue, gpointer data)
{
  gchar *nfname;
  FILE *cue_file;
  nfname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(open_cue));
  if( (cue_file=fopen(nfname,"rt")) ) {
    gtk_clist_clear(GTK_CLIST(sub_playlist));
    if(album_name_s) free(album_name_s);
    if(album_artist_s) free(album_artist_s);
    album_name_s = NULL;
    album_artist_s = NULL;
    parse_cue_sheet(cue_file);
    fclose(cue_file);
  }
#ifdef DEBUG
  else printf("Cue file not found\n");
#endif
}

void import_cue_sheet()
{
  //display the dialog box to get the new sheet
  GtkWidget *open_cue;

  //if the extension is mp3, then just change mp3 to cue
  n_fname = get_cue_name(o_fname);

  open_cue =  gtk_file_selection_new("Import Cue Sheet");
  gtk_file_selection_set_filename(GTK_FILE_SELECTION(open_cue), n_fname);
  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(open_cue)->ok_button, "clicked", GTK_SIGNAL_FUNC(load_cue_sheet), (GtkObject *) open_cue);
  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(open_cue)->ok_button, "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (GtkObject *) open_cue);
  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(open_cue)->cancel_button, "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (GtkObject *) open_cue);
  gtk_widget_show(open_cue);
  free(n_fname);
}

void export_cue_sheet() 
{
  GtkWidget *save_cue;

  //if the extension is mp3, then just change mp3 to cue
  n_fname = get_cue_name(o_fname);

  save_cue =  gtk_file_selection_new("Export Cue Sheet");
  gtk_file_selection_set_filename(GTK_FILE_SELECTION(save_cue), n_fname);

  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(save_cue)->ok_button, "clicked", GTK_SIGNAL_FUNC(save_cue_sheet), (GtkObject *) save_cue);
  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(save_cue)->ok_button, "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (GtkObject *) save_cue);

  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(save_cue)->cancel_button, "clicked", GTK_SIGNAL_FUNC(free_n_fname), (GtkObject *) save_cue);
  gtk_signal_connect_object((GtkObject *)GTK_FILE_SELECTION(save_cue)->cancel_button, "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (GtkObject *) save_cue);
  gtk_widget_show(save_cue);
}



void save_cue_sheet(GtkWidget *save_cue)
{
  gchar *nfname;
  FILE *cuefile;

  //if we aren't writing into the mp3, fine the appropriate file name and save in that
  nfname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(save_cue));
  if(strcasecmp(n_fname, nfname)) quick_message("Warning: File Name differs from original\nmp3cue will not be able to locate the cuesheet automatically");
  free(n_fname);
#ifdef DEBUG
  printf("File chosen is %s\n",nfname);
#endif
  cuefile = fopen(nfname, "wt");
  if(!cuefile) {
    quick_message("Error: Cannot create cuefile\nCheck write access");
    return;
  }

  fprintf(cuefile, "%s", Make_Cue_String().data());

  fclose(cuefile);

#ifdef DEBUG
  //print the cue file string
  printf("The cue file contents: \n%s\n", Make_Cue_String().data());
#endif
}


string Make_Cue_String()
{
  //create a string consisting of the essential parts of a cue sheet file
  gint row = 0, whole, second;
  gchar *songart, *tilda;
  gchar *timed;

  string cue_string;
  gchar buffer[512];
  gchar artist_buffer[512];
 
  sprintf(buffer,"FILE \"%s\"\r\n",o_fname);
  cue_string = buffer;
  
  if(album_name_s) sprintf(buffer, "TITLE \"%s\"\r\n",album_name_s);
  else sprintf(buffer, "TITLE \"\"\r\n");
  cue_string += buffer;
  
  if(album_artist_s) sprintf(buffer, "PERFORMER \"%s\"\r\n",album_artist_s);
  else sprintf(buffer, "PERFORMER \"\"\r\n");
  cue_string += buffer;
  
  for(int i=0;i<rows;i++) {
    gtk_clist_get_text(GTK_CLIST(sub_playlist), row++, 1, &timed);
    gtk_clist_get_text(GTK_CLIST(sub_playlist),row-1,0,&songart);
    
    //divide songeart into song and artist
    tilda = strrchr(songart, '~');
    whole = strlen(songart);
    second = strlen(tilda);
    
    strncpy(artist_buffer,songart,(whole-second-1));
    artist_buffer[whole-second-1] = 0;
    sprintf(buffer,"    TRACK %d AUDIO\r\n\tTITLE \"%s\"\r\n\tPERFORMER \"%s\"\r\n\tINDEX 01 %s\r\n\r\n",row,tilda+2,artist_buffer,timed);
    cue_string += buffer;
  }

  return cue_string;
}


