/*
 *  abc2ps: a program to typeset tunes written in abc format using PostScript
 *  Copyright (C) 1996-2007  Michael Methfessel
 *
 *  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; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  The author can be contacted as follows:
 *  
 *  Michael Methfessel
 *  methfessel at ihp-ffo dot de
 *  IHP
 *  Im Technologiepark 25
 *  D-15236 Frankfurt (Oder), Germany
 */

/* Main program abc2ps.c */

#include <string.h>

#include "abc2ps.h"
#include "subs.h"
#include "format.h"
#include "util.h"
#include "parse.h"
#include "music.h"
#include "playback.h"


/* ----- definitions of global variables ----- */

int db=DEBUG_LV;                  /* debug control */
int maxSyms,maxVc;                /* for malloc */
struct ISTRUCT info,default_info; /* information fields */
char lvoiceid[233];               /* string from last V: line */
int  nvoice,mvoice;               /* number of voices defd, nonempty */
int  ivc;                         /* current voice */
int  ivc0;                        /* top nonempty voice */
int ixpfree;                      /* first free element in xp array */
struct METERSTR default_meter;    /* data to specify the meter */
struct KEYSTR default_key;        /* data to specify the key */
int    force_ktype;               /* for flag -S */
int    force_add_pitch;       

int    frc_ktype[MAXSVC];            /* for flag -S */
int    frc_add_pitch[MAXSVC];       


/* things to alloc: */
struct SYMBOL  *sym;              /* symbol list */
struct SYMBOL  **symv;            /* symbols for voices */
struct XPOS    *xp;               /* shared horizontal positions */
struct VCESPEC *voice;            /* characteristics of a voice */
struct SYMBOL  **sym_st;          /* symbols a staff start */
int            *nsym_st; 

float f0p,f5p,f1p,f0x,f5x,f1x;            /*   mapping fct */
float lnnp,bnnp,fnnp,lnnx,bnnx,fnnx;      /*   note-note spacing */
float lbnp,bbnp,rbnp,lbnx,bbnx,rbnx;      /*   bar-note spacing */
float lnbp,bnbp,rnbp,lnbx,bnbx,rnbx;      /*   note-bar spacing */


char wpool[NWPOOL];            /* pool for vocal strings */
int nwpool,nwline;             /* globals to handle wpool */

struct SYMBOL zsym;            /* symbol containing zeros */

struct FORMAT sfmt;                    /* format after initialization */
struct FORMAT dfmt;                    /* format at start of tune */
struct FORMAT cfmt;                    /* current format for output */

char fontnames[50][STRLFMT];           /* list of needed fonts */
int  nfontnames;

char txt[MAXNTEXT][MAXWLEN];           /* for output of text */
int  ntxt; 

char vcselstr[101];            /* string for voice selection */
char mbf[501];                 /* mini-buffer for one line */
char buf[BUFFSZ];              /* output buffer.. should hold one tune */
int nbuf;                      /* number of bytes buffered */
float bposy;                   /* current position in buffered data */
int   ln_num;                  /* number of lines in buffer */
float ln_pos[BUFFLN];          /* vertical positions of buffered lines */
int   ln_buf[BUFFLN];          /* buffer location of buffered lines */
int   use_buffer;              /* 1 if lines are being accumulated */

char text [NTEXT][STRL];       /* pool for history, words, etc. lines */
int text_type[NTEXT];          /* type of each text line */
int ntext;                     /* number of text lines */
char page_init[201];           /* initialization string after page break */
int do_mode;                   /* control whether to do index or output */
char escseq[81];               /* escape sequence string */
int linenum;                   /* current line number in input file */
int tunenum;                   /* number of current tune */
int tnum1,tnum2;
int numtitle;                  /* how many titles were read */
int mline;                     /* number music lines in current tune */
int nsym;                      /* number of symbols in line */
int nsym0;                     /* nsym at start of parsing a line */
int pagenum;                   /* current page in output file */
int writenum;                  /* calls to write_buffer for each one tune */
int xrefnum;                   /* xref number of current tune */
int do_meter, do_indent;       /* how to start next block */

int index_pagenum;             /* for index file */
float index_posx, index_posy;
int index_initialized;

char gch[201];                   /* guitar chord string */
int bagpipe;                     /* switch for HP mode */
int within_tune, within_block;   /* where we are in the file */
int do_this_tune;                /* are we typesetting the current one ? */
float posx,posy;                 /* overall scale, position on page */
int barinit;                     /* carryover bar number between parts */

char *p, *p0;                    /* global pointers for parsing music line */

int word,slur;                   /* variables used for parsing... */
int last_note,last_real_note;
int pplet,qplet,rplet;
int carryover;                   /* for interpreting > and < chars */
int ntinext,tinext[MAXHD];       /* for chord ties */

struct ENDINGS ending[20];       /* where to draw endings */           
int num_ending;                  /* number of endings to draw */
int mes1,mes2;                   /* to count measures in an ending */

int slur1[20],slur2[20];         /* needed for drawing slurs */
int overfull;                    /* flag if staff overfull */
int do_words;                    /* flag if staff has words under it */

int vb, verbose;                 /* verbosity, global and within tune */
int in_page=0;

                                 /* switches modified by flags: */
char znote[201];                   /* annotation at top of page */
int gmode;                         /* switch for glue treatment */
int include_xrefs;                 /* to include xref numbers in title */
int one_per_page;                  /* new page for each tune ? */
int pagenumbers;                   /* write page numbers ? */
int write_history;                 /* write history and notes ? */
int invert_chords;                 /* invert acc chords ? */
int help_me;                       /* need help ? */
int select_all;                    /* select all tunes ? */
int respect_case;                  /* does case matter in matching ? */
int epsf;                          /* for EPSF postscript output */
int choose_outname;                /* 1 names outfile w. title/fnam */
int break_continues;               /* ignore continuations ? */
int search_field0;                 /* default search field */
int pretty;                        /* for pretty but sprawling layout */
int bars_per_line;                 /* bars for auto linebreaking */
int continue_lines;                /* flag to continue all lines */
int landscape;                     /* flag for landscape output */
int barnums;                       /* interval for bar numbers */
int barnums_cl;                    /* classical barnum counting ? */
int make_index;                    /* write index file ? */
int make_audio;                    /* write audio playback file ? */
int wav_format;                    /* .wav or .au audio file ? */
int cd_format;                     /* CD format audio file ? */
int make_ps;                       /* write ps output file ? */
float alfa_c;                      /* max compression allowed */
float scalefac;                    /* scale factor for symbol size */
float lmargin;                     /* left margin */
float swidth;                      /* staff width */
float staffsep,dstaffsep;          /* staff separation */
float strict1,strict2;             /* 1stave, mstave strictness */
float playtempo;                   /* tempo for audio output */
char  transpose[MAXTRP][21];       /* target keys for transposition */
char  global_transpose[21];        /* global transposition */
int   global_halftones;            /* global transposition */
int   ntrp;                        /* number of tranpose specs */
char  au_qstr[21];                 /* code for au quality selection */
char  acc_pat[21];                 /* accomp switch, pattern */
int   do_acc;                      /* generate accompaniement ? */
int   au_nomusic;                  /* playback: only gchord accompaniment */
int   pb_start, pb_end;            /* playback: start and end bars */
int   write_text;                  /* write abc text file */

float alfa_last,beta_last;         /* for last short short line.. */

char in_file[MAXINF][STRL1];     /* list of input file names */
int  ninf;                       /* number of input file names */
FILE *fin;                       /* for input file */

char outf[STRL1];                /* output file name */
char outfnam[STRL1];             /* internal file name for open/close */
char styf[STRL1];                /* layout style file name */
char styd[STRL1];                /* layout style directory */
char infostr[STRL1];             /* title string in PS file */

int  file_open;                  /* for output file */
int  file_initialized;           /* for output file */
FILE *fout,*findex,*ftext;       /* for output file */
int nepsf;                       /* counter for epsf output files */

char sel_str[MAXINF][STRL1];     /* list of selector strings */
int  s_field[MAXINF];            /* type of selection for each file */
int  psel[MAXINF];               /* pointers from files to selectors */

int temp_switch;
char lout[BSIZE];

int utf8charset;

#if ABC_USE_GRAPHICS_LIB
char *outputFormat;
int outputWidth;
int outputHeight;
GraphicsContext gcx;
#endif
#if ABC_INPUT_STRING
char *abcInputString;
#endif

/* ----- start of main ------ */
#if ABC_LIBRARY
int abc2ps_main(argc, argv)
#else
int main(argc, argv)
#endif
int argc;
char *argv[];
{
#if ABC_LIBRARY
  in_page=0;
  ivc = 0;
  utf8charset = 0;
//  memset( &maxSyms, 0, (char *)lout-(char *)maxSyms);
  resetRandom();
#endif

  char aaa[501], bbb[501], ext[41];
  char xref_str[STRL1], pat[30][STRL1];
  int isel, j, npat, search_field, retcode;
  
  /* ----- set default options and parse arguments ----- */

#if !TARGET_OS_IPHONE
  maxSyms = 2400;
#else
  maxSyms = 800;
#endif
  maxVc   = 6;

  init_ops (1);
  retcode = parse_args (argc, argv);
#if !ABC_LIBRARY
  if (retcode) exit (1);
#endif

#if TARGET_OS_IPHONE
  //An attempt to fix memory fragmentation which eventually causes a crash
  static int firstTime = 1;
  if ( firstTime )
    alloc_structs ();
  firstTime = 0;
#else
  alloc_structs ();
#endif

  /* ----- set the page format ----- */
  nfontnames=0;
  if (!set_page_format()) exit (3);
  if (help_me == 2) {
    print_format (cfmt);
    exit (0);
  }
  
  /* ----- help printout  ----- */
  if (argc <= 1) help_me = 1;

  if (help_me) {
    if (make_audio) {
      if (help_me == 1) print_au_info (0);
      if (help_me == 3) print_au_info (1);
    }
    else {
      if (help_me == 1) write_help (1);
      if (help_me == 3) write_help (0);
    }
    exit (0);
  }

  if (ninf == 0) rx ("No input file specified", "");
  
  isel = psel[ninf-1];
  search_field0 = s_field[isel];   /* some remnant from interactive mode.. */
  if (epsf) cutext(outf);
  
  /* ----- initialize ----- */
  zero_sym ();
  pagenum = 0;
  tunenum = tnum1 = tnum2=0;
  verbose = 0;
  file_open = file_initialized=0;
  nepsf = 0;
  bposy = 0;
  posx = cfmt.leftmargin;
  posy = cfmt.pageheight-cfmt.topmargin; 

  strcpy (page_init, "");
  strcpy (bbb, "");
  for (j=0;j <ninf; j++) {strcat(bbb,in_file[j]); strcat(bbb," ");}

  if ((do_mode == DO_OUTPUT) && make_index) open_index_file (INDEXFILE);
  if ((do_mode == DO_OUTPUT) && make_audio) open_audio_file (AUDIOFILE);

#if ABC_USE_CAIRO
  cairo_surface_t *surface;
  int scale = 1;
  if ( !outputFormat )
    outputFormat = "png";
  if ( strcmp( outputFormat, "png" ) == 0 )
    scale = 2;
  outputWidth = scale*(cfmt.staffwidth+2*cfmt.leftmargin);
  outputHeight = scale*cfmt.pageheight;
  if ( strcmp( outputFormat, "png" ) == 0 )
    surface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, outputWidth, outputHeight );
  else if ( strcmp( outputFormat, "pdf" ) == 0 )
    surface = cairo_pdf_surface_create( "Out.pdf", outputWidth, outputHeight);
  else if ( strcmp( outputFormat, "ps" ) == 0 )
    surface = cairo_ps_surface_create( "Out.ps", outputWidth, outputHeight);
  else if ( strcmp( outputFormat, "svg" ) == 0 )
    surface = cairo_svg_surface_create( "Out.svg", outputWidth, outputHeight);
  gcx = cairo_create( surface );
  cairo_set_source_rgb(gcx, 1, 1, 1);
  cairo_rectangle(gcx, 0, 0, outputWidth, outputHeight);
  cairo_fill (gcx);
  cairo_scale( gcx, scale, scale );
  cairo_scale( gcx, 1, -1 );
  cairo_set_source_rgb(gcx, 0, 0, 0);
#endif

  /* ----- loop over files in list ---- */
  if (ninf==0) printf ("++++ No input files\n");
  for (j=0;j <ninf; j++) {
    getext (in_file[j], ext);
    /*  skip .ps and .eps files */
    if ((!strcmp(ext,"ps"))||(!strcmp(ext,"eps"))) continue;
    
#if !ABC_INPUT_STRING
    if ((fin = fopen (in_file[j],"r")) == NULL) {      
      if (!strcmp(ext,"")) strext (in_file[j], in_file[j], DEF_EXT, 1);
      if ((fin = fopen (in_file[j],"r")) == NULL) { 
        printf ("++++ Cannot open input file: %s\n", in_file[j]);
        continue;
      }
    }
#endif

    /* open file for abc text output */
    if (write_text) {
      strlcpy (aaa, in_file[j], 450);
      cutext (aaa);
      strcat (aaa,"_out.");
      strcat (aaa, DEF_EXT);
      if ((ftext = fopen(aaa,"w")) == NULL)
        rx ("Could not open output text file: ", aaa);
    }

    isel = psel[j];
    search_field = s_field[isel];
    npat = rehash_selectors (sel_str[isel], xref_str, pat);
    dfmt = sfmt;
    strlcpy (infostr, in_file[j], STRL1-1);
    
    if (do_mode == DO_INDEX) {
      printf ("%s:\n", in_file[j]);
      do_index (fin, xref_str, npat, pat, select_all, search_field);
    }
    
    else {
      if (!epsf) {
        strext (outf, outf, "ps", 1);
        if (choose_outname) strext (outf, in_file[j], "ps", 1);
        open_output_file (outf, in_file[j]);
      }
#if !ABC_LIBRARY
      printf ("%s: ", in_file[j]); 
      if (vb>=3) printf ("\n"); 
#endif
      process_file (fin, fout, xref_str, npat, pat, select_all, search_field);
#if !ABC_LIBRARY
      printf ("\n");
#endif
    }
  }
  
  if (do_mode == DO_INDEX) 
    printf ("Selected %d title%s of %d\n", tnum1, tnum1==1?"":"s", tnum2);
  
  close_output_file ();
  if (write_text) {
    fclose (ftext);
    printf ("Abc code written to %s\n", aaa);
  }


  if ((do_mode == DO_OUTPUT) && make_index) close_index_file ();
  if ((do_mode == DO_OUTPUT) && make_audio) close_audio_file (); 
#if ABC_USE_CAIRO
  if ( strcmp( outputFormat, "png" ) == 0 )
    cairo_surface_write_to_png( surface, "Out.png");
  cairo_surface_finish( surface );
  cairo_surface_destroy( surface );
#endif
#if ABC_LIBRARY
#if !TARGET_OS_IPHONE
  free_structs();
#endif
#else
  exit (0);
#endif
}






