/* contexts.cpp
 * Context finders: functions that find the current clef, key, and time
 * signature contexts for the measures being displayed
 *
 * These functions are also invoked when a staff is created
 * 
 *
 * for Denemo, a gtk+ frontend to GNU Lilypond
 * (c) 1999-2005 Matthew Hiller, Adam Tee
 */

#include "contexts.h"
#include "drawingprims.h"
#include "objops.h"
#include "staffops.h"

/**
 * This function finds the first DenemoObject of type thetype *before* measure
 * curmeasure. It returns NULL if it was unable to find such a DenemoObject 
 * 
 * @param curmeasure the current measure to search
 * @param thetype type of DenemoObject to find
 * @return the first DenemoObject of type the type
 */
static DenemoObject *
find_measure_context (measurenode * curmeasure, DenemoObjType thetype)
{
  objnode *curobj;
  if (curmeasure && curmeasure->prev)
    curmeasure = curmeasure->prev;
  else
    return NULL;

  curobj = lastobjnode (curmeasure);
  while (1)
    {
      while (!curobj)
        {                       /* Cycle back to preceding measure */
          if (!curmeasure->prev)
            return NULL;        /* That is, we've fallen off the beginnig */
          curmeasure = curmeasure->prev;
          curobj = lastobjnode (curmeasure);
        }
      if (((DenemoObject *) curobj->data)->type == thetype)
        return (DenemoObject *) curobj->data;
      else
        curobj = curobj->prev;
    }
}



/**
 * This function finds the first DenemoObject of type thetype before si->currentobject
 * It returns NULL if it was unable to find such a DenemoObject 
 * @param si the DenemoScore with si->currentobject and si->currentmeasure set
 * @param thetype type of DenemoObject to find
 * @return the first DenemoObject of type thetype
 */
static DenemoObject *
find_context_of_object (DenemoScore * si, DenemoObjType thetype)
{
  objnode *curobj = si->currentobject;
  measurenode *curmeasure = si->currentmeasure;
  if (curobj == NULL || curmeasure == NULL)
    return NULL;
  while (curobj)
    {
      if (((DenemoObject *) curobj->data)->type == thetype)
        return (DenemoObject *) curobj->data;
      else
        curobj = curobj->prev;
      if (curobj == NULL)
        {                       /* go back to preceding measure */
          while (curmeasure->prev)
            {
              curmeasure = curmeasure->prev;
              curobj = lastobjnode (curmeasure);
              if (curobj)
                break;
            }
        }
    }
  return NULL;
}


DenemoObject *
get_clef_before_object (objnode * obj)
{
  DenemoObject *ret;
  objnode *curobj = Denemo.gui->si->currentobject;
  Denemo.gui->si->currentobject = obj;
  ret = find_context_of_object (Denemo.gui->si, CLEF);
  Denemo.gui->si->currentobject = curobj;
  return ret;
}

/* find the clef in which the currentobject lies */
gint
find_prevailing_clef (DenemoScore * si)
{
  DenemoStaff *curstaff = ((DenemoStaff *) si->currentstaff->data);
  DenemoObject *obj = find_context_of_object (si, CLEF);
// g_print("prevailing clef %d\n",  obj? ((clef *) obj->object)->type:curstaff->clef.type);
  return obj ? ((clef *) obj->object)->type : curstaff->clef.type;
}

/* returns a pointer to a CLEF TIMESIG or KEYSIG structure which holds the information on the prevailing context for the current object */
gpointer
get_prevailing_context (DenemoObjType type)
{
  DenemoStaff *curstaff = ((DenemoStaff *) Denemo.gui->si->currentstaff->data);
  DenemoObject *obj;
  switch (type)
    {
    case CLEF:
      obj = find_context_of_object (Denemo.gui->si, CLEF);
      if (obj == NULL)
        obj = (DenemoObject *) & curstaff->clef;
      else
        obj = obj->object;
      break;
    case KEYSIG:
      obj = find_context_of_object (Denemo.gui->si, KEYSIG);
      if (obj == NULL)
        obj = (DenemoObject *) & curstaff->keysig;
      else
        obj = obj->object;
      break;
    case TIMESIG:
      obj = find_context_of_object (Denemo.gui->si, TIMESIG);
      if (obj == NULL)
        obj = (DenemoObject *) & curstaff->timesig;
      else
        obj = obj->object;
      break;
    default:
      g_critical ("Wrong type in call to get_prevailing_context");
      obj = NULL;
      break;
    }
  return obj;
}

/**
 * Finds the first occurrences of the clef, keysig and timesig of the 
 * current staff  inserting them into the passed CURSTAFFSTRUCT
 * 
 * @param curstaffstruct the current staff
 * @param si the scoreinfo structure
 * @return none
 */
void
find_leftmost_staffcontext (DenemoStaff * curstaffstruct, DenemoScore * si)
{
  measurenode *leftmeasure = g_list_nth (curstaffstruct->measures,
                                         si->leftmeasurenum - 1);
  DenemoObject *obj;

  if ((obj = find_measure_context (leftmeasure, CLEF)))
    curstaffstruct->leftmost_clefcontext = ((clef *) obj->object);
  else
    curstaffstruct->leftmost_clefcontext = &curstaffstruct->clef;
  if ((obj = find_measure_context (leftmeasure, KEYSIG)))
    curstaffstruct->leftmost_keysig = (keysig *) obj->object;
  else
    curstaffstruct->leftmost_keysig = &curstaffstruct->keysig;

  // initkeyaccs (curstaffstruct->leftmost_keysig->accs,
  //           curstaffstruct->leftmost_keysig->number);

  si->maxkeywidth = MAX (si->maxkeywidth, draw_key (NULL, 0, 0, curstaffstruct->leftmost_keysig->number, 0, 0, FALSE, curstaffstruct->leftmost_keysig));
  if ((obj = find_measure_context (leftmeasure, TIMESIG)))
    {
      curstaffstruct->leftmost_timesig = (timesig *) obj->object;
    }
  else
    {
      curstaffstruct->leftmost_timesig = &curstaffstruct->timesig;
    }
  if ((obj = find_measure_context (leftmeasure, STEMDIRECTIVE)))
    curstaffstruct->leftmost_stem_directive = ((stemdirective *) obj->object)->type;
  else
    curstaffstruct->leftmost_stem_directive = DENEMO_STEMBOTH;
}

/**
 * Finds the first occurrences of the clef, keysig and timesig of the 
 * across the entir score
 * @param si the scoreinfo structure
 * @return none
 */
void
find_leftmost_allcontexts (DenemoScore * si)
{
  staffnode *curstaff = si->thescore;

  si->maxkeywidth = G_MININT;
  for (; curstaff; curstaff = curstaff->next)
    {
      find_leftmost_staffcontext ((DenemoStaff *) curstaff->data, si);

    }
}
