/* exportabc.cpp
 * Functions for exporting what Denemo's working on to an ABC file (adapted
 * somewhat from exportmudela.c)
 *
 * for Denemo, a gtk+ frontend to GNU Lilypond
 * (c) 2001-2005 Eric Galluzzo
 */

#include <config.h>
#include <denemo/denemo.h>
#include "twoints.h"
#include "utils.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>


/**
 * Determine key signature from denemo value
 *
 */
static void
determinebasekey (gint number, gchar ** basekey)
{
  switch (number)
    {
    case -7:
      *basekey = "Cb";
      break;
    case -6:
      *basekey = "Gb";
      break;
    case -5:
      *basekey = "Db";
      break;
    case -4:
      *basekey = "Ab";
      break;
    case -3:
      *basekey = "Eb";
      break;
    case -2:
      *basekey = "Bb";
      break;
    case -1:
      *basekey = "F";
      break;
    case 0:
      *basekey = "C";
      break;
    case 1:
      *basekey = "G";
      break;
    case 2:
      *basekey = "D";
      break;
    case 3:
      *basekey = "A";
      break;
    case 4:
      *basekey = "E";
      break;
    case 5:
      *basekey = "B";
      break;
    case 6:
      *basekey = "F#";
      break;
    case 7:
      *basekey = "C#";
      break;
    case 8:
      *basekey = "G#";
      break;
    case 9:
      *basekey = "D#";
      break;
    case 10:
      *basekey = "A#";
      break;
    default:
      *basekey = _("%{error. defaulting to%}C");
      break;
    }
}

/**
 * Determine clef from denemo value
 *
 */
static void
determineclef (gint type, gchar ** clefname, gint * octaveshift)
{
  switch (type)
    {
    case DENEMO_TREBLE_CLEF:
      *clefname = "treble";
      *octaveshift = 0;
      break;
    case DENEMO_BASS_CLEF:
      *clefname = "bass";
      *octaveshift = 2;
      break;
    case DENEMO_ALTO_CLEF:
      *clefname = "alto";
      *octaveshift = 1;
      break;
    case DENEMO_G_8_CLEF:
      *clefname = "treble8";
      *octaveshift = 1;
      break;
    case DENEMO_TENOR_CLEF:
      *clefname = "tenor";	/* FIXME: highly nonstandard */
      *octaveshift = 1;		/* FIXME: ditto */
      break;
    case DENEMO_SOPRANO_CLEF:
      *clefname = "soprano";	/* FIXME: highly nonstandard */
      *octaveshift = 0;		/* FIXME: ditto */
      break;
    default:
      *clefname = _("%{error. defaulting to%}treble");
      *octaveshift = 0;
      break;
    }
}

/** 
 * determine the timesig denominator
 *
 */
static gint
determinedefaultlength (struct twoints *timesig)
{
  /* For 1/x and 2/x time signatures, return x*2; for all others, return x. */

  if (timesig->a < 3)
    return timesig->b << 1;
  else
    return timesig->b;
}


/**
 * Determine the length as it should be written to the ABC file as a fraction.
 * defaultlength is a denominator (e.g. "8"), the current default length
 * (e.g. "L:1/8").  duration and numdots are the Denemo duration and number of
 * dots, and the ABC length is returned in length.
 */
static void
determinelength (gint duration, gint numdots, gint defaultlength,
		 struct twoints *length)
{
  /* ABC duration is:

   *                        numdots + 1
   *     defaultlength * (2             - 1)
   *     -----------------------------------
   *               numdots + duration
   *             2                          <--   = 1 << (numdots + duration)
   *
   * simplified as far as possible (defaultlength is always a multiple of 2).
   */

  //gint i;
  gint num = 1;			/* so far */
  gint denom = 1 << (numdots + duration);

  while (denom > 1 && defaultlength > 1)
    {
      denom >>= 1;
      defaultlength >>= 1;
    }
  while (defaultlength > 1)
    {
      num <<= 1;
      defaultlength >>= 1;
    }
  num *= ((1 << (numdots + 1)) - 1);

  length->a = num;
  length->b = denom;
}


/**
 * Print out the given length (numerator/denominator), omitting 1's. 
 */
static void
printlength (FILE * fp, struct twoints *length)
{
  if (length->a > 1)
    fprintf (fp, "%d", length->a);
  if (length->b > 1)
    {
      fprintf (fp, "/");
      if (length->b > 2)
	fprintf (fp, "%d", length->b);
    }
}


/**
 * Print out the given chord, given the current default length. 
 */
static void
printchord (FILE * fp, chord * chordptr, gint octaveshift, gint defaultlength)
{
  gint i;
  struct twoints length;

  determinelength (chordptr->baseduration, chordptr->numdots, defaultlength,
		   &length);

  if (chordptr->notes)		/* it's a note or chord, not a rest */
    {
      /* If there's only one note, it's a note, not a chord. */

      gboolean ischord = chordptr->notes->next != NULL;
      GList *curnotenode, *tmp;
      note *curnote;
      gchar notename;
      gint octave;

      /* Print out the decorations and beginning of the slur (if any). */

      if (chordptr->slur_begin_p)
	fprintf (fp, "(");
      for (tmp = chordptr->ornamentlist; tmp; tmp = tmp->next)
	{
	  if (*(enum ornament *) tmp->data == (enum ornament) STACCATO)
	    fprintf (fp, ".");
	  if (*(enum ornament *) tmp->data == (enum ornament) D_ACCENT)
	    fprintf (fp, "k");	/* FIXME: nonstandard */
	  if (*(enum ornament *) tmp->data == (enum ornament) FERMATA)
	    fprintf (fp, "H");	/* FIXME: nonstandard */
	  if (*(enum ornament *) tmp->data == (enum ornament) TENUTO)
	    fprintf (fp, "M");	/* FIXME: nonstandard */
	  if (*(enum ornament *) tmp->data == (enum ornament) TRILL)
	    fprintf (fp, "T");	/* FIXME: somewhat nonstandard */
	  /* FIXME: I believe turns are not supported in ABC at the moment. */
	  if (*(enum ornament *) tmp->data == (enum ornament) MORDENT)
	    fprintf (fp, "~");
	  if (*(enum ornament *) tmp->data == (enum ornament) STACCATISSIMO)
	    fprintf (fp, "K");	/* FIXME: nonstandard */
	}
      if (ischord)
	fprintf (fp, "[");

      for (curnotenode = chordptr->notes;
	   curnotenode != NULL; curnotenode = curnotenode->next)
	{
	  curnote = (note *) curnotenode->data;
	  notename = mid_c_offsettoname (curnote->mid_c_offset);
	  octave =
	    mid_c_offsettooctave (curnote->mid_c_offset) + octaveshift - 2;

	  /* Print accidentals. */

	  if (curnote->showaccidental)
	    {
	      if (curnote->enshift > 0)
		{
		  for (i = 0; i < curnote->enshift; i++)
		    fprintf (fp, "^");
		}
	      else if (curnote->enshift < 0)
		{
		  for (i = 0; i > curnote->enshift; i--)
		    fprintf (fp, "_");
		}
	      else
		{
		  fprintf (fp, "=");
		}
	    }

	  /* Print the note name. */

	  if (octave < 0)
	    {
	      octave += 1;
	      notename = toupper (notename);
	    }
	  fprintf (fp, "%c", notename);

	  /* Print the correct number of "'" or "," marks. */

	  if (octave > 0)
	    {
	      for (i = 0; i < octave; i++)
		fprintf (fp, "'");
	    }
	  else
	    {
	      for (i = 0; i > octave; i--)
		fprintf (fp, ",");
	    }

	  printlength (fp, &length);
	}

      if (ischord)
	fprintf (fp, "]");

      if (chordptr->slur_end_p)
	fprintf (fp, ")");
      if (chordptr->is_tied)
	fprintf (fp, "-");
    }
  else				/* it's a rest */
    {
      fprintf (fp, "z");
      printlength (fp, &length);
    }
}


/**
 * Export the ABC.  This works as follows:
 *   1. Write out all the headers (X:, T:, C:, Q:, and K:).
 *   2. Write out each voice in turn.
 */

void
exportabc (gchar * thefilename, DenemoGUI * gui, gint start, gint end)
{
  DenemoScore * si = gui->si;
  gchar *clefname;
  gchar *basekeyname;
  FILE *fp;
  GString *filename = g_string_new (thefilename);
  /* FIXME: The following assumes that there is at least one staff. */
  DenemoStaff *firststaffstruct = (DenemoStaff *) si->thescore->data;
  staffnode *curstaff;
  DenemoStaff *curstaffstruct;
  gint defaultlength;
  gint curvoicenum;
  gint octaveshift;
  measurenode *curmeasure;
  gint externalmeasurenum;
  gint internalmeasurenum;
  gboolean emptymeasure;
  objnode *curobjnode;
  DenemoObject *curobj;
  struct twoints curtime;
  gboolean curisnonprimary;
  gboolean nextisnonprimary;
  objnode *tupleobjnode;
  DenemoObject *tupleobj;
  gint notesintuple;
  gboolean ishomogenoustuple;
  gint prevduration;
  gint prevnumdots;
  gdouble fraction = 0;
  enum clefs type = DENEMO_TREBLE_CLEF;
  /* Append .abc onto the filename if necessary */
  if (strcmp (filename->str + filename->len - 4, ".abc"))
    g_string_append (filename, ".abc");

  /* Now open the file */
  fp = fopen (filename->str, "w");

  /* And cut off the filename extension for use in the file itself */
  g_string_truncate (filename, filename->len - 4);

  fprintf (fp, _("%% ABC file generated by Denemo version "));
  fprintf (fp, VERSION "\n\n");
  fprintf (fp, "%% http://www.denemo.org\n\n");

  /* We export the following headers in this order:
   *   - X: (tune number)
   *   - T: (title)
   *   - C: (composer)
   *   - Q: (tempo)
   *   - M: (initial time signature)
   *   - L: (initial default note length)
   *   - %%staves (for the benefit of abcm2ps)
   *   - K: (key signature)
   */

  determinebasekey (firststaffstruct->keysig.isminor ?
		    firststaffstruct->keysig.number + 3 :
		    firststaffstruct->keysig.number, &basekeyname);
  curtime.a = firststaffstruct->timesig.time1;
  curtime.b = firststaffstruct->timesig.time2;
  defaultlength = determinedefaultlength (&curtime);

  fprintf (fp, "X:1\n");
  fprintf (fp, "T:%s\n","No title");
  fprintf (fp, "Q:1/4=%d\n", si->tempo);
  fprintf (fp, "M:%d/%d\n", firststaffstruct->timesig.time1,
	   firststaffstruct->timesig.time2);
  fprintf (fp, "L:1/%d\n", defaultlength);

  fraction = 1.0 / (gdouble) g_list_length (si->thescore);
  fraction /= 2;
  g_print ("Fraction %lf\n", fraction);
  /* Figure out the %%staves comment.  It should look something like:

   *     %%staves [{(1 2) 3} 4 (5 6)] [7 8] 9
   *
   * where {} denotes curly braces (e.g. a piano, harp, or organ staff),
   *       [] denotes square braces,
   *   and () denotes two voices on the same staff.
   */
  fprintf (fp, "%%%%staves [");
  for (curstaff = si->thescore, curvoicenum = 1;
       curstaff != NULL; curstaff = curstaff->next, curvoicenum++)
    {
      curstaffstruct = (DenemoStaff *) curstaff->data;

      nextisnonprimary =
	(curstaff->next != NULL &&
	 ((DenemoStaff *) curstaff->next->data)->voicenumber > 1);
      curisnonprimary = (curstaffstruct->voicenumber > 1);

      if (curvoicenum != 1)
	fprintf (fp, " ");
      if (!curisnonprimary && nextisnonprimary)
	fprintf (fp, "(");
      fprintf (fp, "%d", curvoicenum);
      if (curisnonprimary && !nextisnonprimary)
	fprintf (fp, ")");
    }
  fprintf (fp, "]\n");

  fprintf (fp, "K:%s%s\n", basekeyname,
	   firststaffstruct->keysig.isminor ? "m" : "");

  /* Now we output each voice in turn. */

  for (curstaff = si->thescore, curvoicenum = 1;
       curstaff != NULL; curstaff = curstaff->next, curvoicenum++)
    {
      curstaffstruct = (DenemoStaff *) curstaff->data;
      determineclef (curstaffstruct->clef.type, &clefname, &octaveshift);
      determinebasekey (curstaffstruct->keysig.isminor ?
			curstaffstruct->keysig.number + 3 :
			curstaffstruct->keysig.number, &basekeyname);
      curtime.a = curstaffstruct->timesig.time1;
      curtime.b = curstaffstruct->timesig.time2;
      defaultlength = determinedefaultlength (&curtime);

      /* First, the V: header. FIXME: Output name="..." too. */

      fprintf (fp, "%%\n%%\nV:%d clef=%s\n", curvoicenum, clefname);
      fprintf (fp, "I:octave=%d\n", -octaveshift);	/* FIXME: nonstandard */
      fprintf (fp, "M:%d/%d\n", curstaffstruct->timesig.time1,
	       curstaffstruct->timesig.time2);
      fprintf (fp, "L:1/%d\n", defaultlength);
      fprintf (fp, "K:%s%s\n", basekeyname,
	       curstaffstruct->keysig.isminor ? "m" : "");

      /* Then the actual notes, measure for measure (sorry, excuse the bad
       * Shakespeare pun). */

      for (curmeasure = curstaffstruct->measures,
	   internalmeasurenum = MAX (start, 1),
	   externalmeasurenum = 1;
	   curmeasure != NULL && (end == 0 || internalmeasurenum <= end);
	   curmeasure = curmeasure->next,
	   externalmeasurenum++, internalmeasurenum++)
	{
	  /* Print the measure number every 5 measures. */

	  if (externalmeasurenum % 5 == 0)
	    fprintf (fp, "%%%d\n", externalmeasurenum);

	  emptymeasure = TRUE;

	  /* Print out everything in this measure. */

	  for (curobjnode = (objnode *) curmeasure->data; curobjnode;
	       curobjnode = curobjnode->next)
	    {
	      curobj = (DenemoObject *) curobjnode->data;

	      switch (curobj->type)
		{
		case CHORD:
		  emptymeasure = FALSE;
		  printchord (fp, (chord *) curobj->object, octaveshift,
			      defaultlength);
		  if (curobj->isend_beamgroup)
		    fprintf (fp, " ");
		  break;

		case CLEF:
		  type = ((clef *) curobj->object)->type;
		  determineclef (type, &clefname, &octaveshift);
		  fprintf (fp, "[K:%s][I:octave=%d]", clefname, -octaveshift);
		  break;

		case KEYSIG:
		  determinebasekey (((keysig *) curobj->object)->isminor ?
				    ((keysig *) curobj->object)->number + 3 :
				    ((keysig *) curobj->object)->number,
				    &basekeyname);
		  fprintf (fp, "[K:%s%s]", basekeyname,
			   ((keysig *) curobj->object)->isminor ? "m" : "");
		  break;

		case TIMESIG:
		  curtime.a = ((timesig *) curobj->object)->time1;
		  curtime.b = ((timesig *) curobj->object)->time2;
		  defaultlength = determinedefaultlength (&curtime);
		  fprintf (fp, "[M:%d/%d][L:1/%d]", curtime.a, curtime.b,
			   defaultlength);
		  break;

		case TUPOPEN:
		  /* Count the number of chords in the tuplet, and determine
		   * whether they are all of the same length.
		   *
		   * Note: This could eventually cross measures, maybe, and
		   *       the current algorithm doesn't take that into
		   *       account. */

		  notesintuple = 0;
		  ishomogenoustuple = TRUE;
		  prevduration = G_MAXINT;
		  prevnumdots = G_MAXINT;
		  for (tupleobjnode = curobjnode->next;
		       tupleobjnode != NULL;
		       tupleobjnode = tupleobjnode->next)
		    {
		      tupleobj = (DenemoObject *) tupleobjnode->data;
		      if (tupleobj->type == TUPCLOSE)
			break;
		      if (tupleobj->type == CHORD)
			{
			  notesintuple++;
			  if (ishomogenoustuple)
			    {
			      if ((prevduration != G_MAXINT &&
				   prevduration !=
				   ((chord *) tupleobj->object)->baseduration)
				  ||
				  (prevnumdots != G_MAXINT &&
				   prevnumdots !=
				   ((chord *) tupleobj->object)->numdots))
				{
				  ishomogenoustuple = FALSE;
				}
			      else
				{
				  prevduration =
				    ((chord *) tupleobj->object)->
				    baseduration;
				  prevnumdots =
				    ((chord *) tupleobj->object)->numdots;
				}
			    }	/* End if ishomogenoustuple */
			}	/* End if tuple object is a chord */
		    }		/* End for each object in tuple */

		  if (notesintuple == 3 && ishomogenoustuple && ((tupopen *) curobj->object)->numerator == 2 && ((tupopen *) curobj->object)->denominator == 3)	/* a simple triplet */
		    fprintf (fp, "(3");
		  else
		    fprintf (fp, "(%d:%d:%d",
			     ((tupopen *) curobj->object)->denominator,
			     ((tupopen *) curobj->object)->numerator,
			     notesintuple);
		  break;

		case GRACE_START:
		  fprintf (fp, "{");
		  break;

		case GRACE_END:
		  fprintf (fp, "}");
		  break;

		case TUPCLOSE:
		case STEMDIRECTIVE:
		case DYNAMIC:
		  /* Do nothing at present.

		   * FIXME: I should probably do the !pp! thing for dynamics
		   *        at some point, when it becomes mainstream in ABC
		   *        parsers.
		   */
		  break;

		default:
		  /* Danger, Will Robinson! */
		  g_warning ("Warning: unknown DenemoObject type \"%d\" "
			     "found, ignoring", curobj->type);
		  break;
		}		/* End switch on object type */
	    }			/* End for each note */

	  /* Print out (e.g.) "x3" for an empty measure. */

	  if (emptymeasure)
	    fprintf (fp, "x%d ", curtime.a * (defaultlength / curtime.b));

	  /* And finally, of course, the barline. */

	  if (curmeasure->next)
	    fprintf (fp, "|\n");
	  else
	    fprintf (fp, "|]\n");
	}			/* End for each measure */

    }				/* End for each staff (voice) */

  /* Clean up and go home. */

  fclose (fp);
  g_string_free (filename, FALSE);
}
