/* Functions for handling sentence parents.

   Copyright (C) 2012 Ian Dunn.

   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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include "sen-parent.h"
#include "sentence.h"
#include "sen-data.h"
#include "app.h"
#include "list.h"
#include "process.h"

/* Initializes a sentence parent.
 *  input:
 *    sp - the sentence parent to initialize.
 *    title - the title of the sentence parent's window.
 *    width - the width of the sentence parent's window.
 *    height - the height of the sentence parent's window.
 *    menu_func - initializes the sentence parent's menu bar.
 *    type - the type of sentence parent.
 *  output:
 *    none.
 */
void
sen_parent_init (sen_parent * sp, const char * title,
		 int width, int height,
		 void (* menu_func) (sen_parent *),
		 int type)
{
  sp->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (sp->window), title);
  gtk_widget_set_size_request (sp->window, width, height);

  sp->accel = gtk_accel_group_new ();
  gtk_window_add_accel_group (GTK_WINDOW (sp->window), sp->accel);

  sp->vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (sp->window), sp->vbox);

  // Call menu_func on sp.
  menu_func (sp);
  gtk_box_pack_start (GTK_BOX (sp->vbox), sp->menubar, FALSE, FALSE, 0);

  // Initialize the scrolled window.
  sp->scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
  gtk_box_pack_start (GTK_BOX (sp->vbox), sp->scrolledwindow,
		      TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sp->scrolledwindow),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);

  GtkAdjustment * f_adj;
  f_adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sp->scrolledwindow));

  // Initialize the viewport.
  sp->viewport = gtk_viewport_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (sp->scrolledwindow), sp->viewport);

  // Initialize the main container.
  sp->container = gtk_vbox_new (FALSE, 4);
  gtk_container_add (GTK_CONTAINER (sp->viewport), sp->container);
  gtk_container_set_focus_vadjustment (GTK_CONTAINER (sp->container), f_adj);

  // Initialize the status bar.
  sp->statusbar = gtk_statusbar_new ();
  gtk_box_pack_start (GTK_BOX (sp->vbox), sp->statusbar, FALSE, FALSE, 0);

  sp->everything = init_list ();
  sp->font = FONT_TYPE_SMALL;
  sp->focused = NULL;

  sp->type = type;
}

/* Destroys a sentence parent.
 *  input:
 *    sp - the sentence parent to destroy.
 *  output:
 *    none.
 */
void
sen_parent_destroy (sen_parent * sp)
{
  item_t * ev_itr, * nev_itr;

  for (ev_itr = sp->everything->head; ev_itr; ev_itr = nev_itr)
    {
      nev_itr = ev_itr->next;
      sentence_destroy ((sentence *) ev_itr->value);
      ev_itr->prev = ev_itr->next = ev_itr->value = NULL;
      free (ev_itr);
    }

  free (sp->everything);
  sp->everything = NULL;

  sp->font = -1;
  sp->focused = NULL;
  gtk_widget_destroy (sp->window);
}

/* Sets the font of a sentence parent.
 *  input:
 *    sp - the sentence parent of which to set the font.
 *    new_font - the index in the_app->fonts to which the font is being set.
 *  output:
 *    none.
 */
void
sen_parent_set_font (sen_parent * sp, int new_font)
{
  if (sp->font == new_font && new_font != FONT_TYPE_CUSTOM)
    return;

  item_t * ev_itr;

  for (ev_itr = sp->everything->head; ev_itr; ev_itr = ev_itr->next)
    sentence_set_font (ev_itr->value, new_font);

  sp->font = new_font;
}

/* Inserts a sentence into a sentence parent.
 *  input:
 *    sp - the sentence parent into which the sentence is being inserted.
 *    sd - the sentence data from which to initialize the sentence.
 *    fcs - the item after which to insert the new sentence.
 *    new_order - the new order in the container in which this sentence will be.
 *  output:
 *    the iterator in sp->everything that the sentence will be, or NULL on error.
 */
item_t *
sen_parent_ins_sentence (sen_parent * sp, sen_data * sd,
			 item_t * fcs, int new_order)
{
  item_t * itm;
  sentence * sen;

  sen = sentence_init (sd, sp, fcs);
  if (!sen)
    return NULL;

  gtk_box_pack_start (GTK_BOX (sp->container), sen->panel, FALSE, FALSE, 0);
  gtk_box_reorder_child (GTK_BOX (sp->container), sen->panel, new_order);

  itm = ls_ins_obj (sp->everything, sen, fcs);
  sp->focused = itm;

  gtk_widget_show_all (sen->panel);

  return itm;
}

/* Removes a sentence from a sentence parent.
 *  input:
 *    sp - the sentence parent from which a sentence is being removed.
 *    sen - the sentence that is being removed.
 *  output:
 *    the item that was removed.
 */
item_t *
sen_parent_rem_sentence (sen_parent * sp, sentence * sen)
{
  item_t * ev_itr, * target = NULL;

  target = ls_find (sp->everything, sen);

  // Only need to start this past the target sentence.
  for (ev_itr = target->next; ev_itr; ev_itr = ev_itr->next)
    {
      sentence * ev_sen = ev_itr->value;

      if (ev_sen->refs)
	ls_rem_obj_value (ev_sen->refs, sen);
    }

  if (target == sp->focused)
    {
      item_t * new_focus;
      if (sp->everything->num_stuff == 1)
	new_focus = NULL;
      else if (target == sp->everything->head)
	new_focus = sp->everything->head->next;
      else if (target == sp->everything->tail)
	new_focus = sp->everything->tail->prev;
      else
	new_focus = target->prev;

      if (new_focus)
	sen_parent_set_focus (sp, new_focus);
    }

  ls_rem_obj (sp->everything, target);
  sentence_destroy (sen);

  return target;
}

/* Sets an item as the focus of a sentence parent.
 *  input:
 *    sp - the sentence parent which is having its focus changed.
 *    focus - the new focus.
 *  output:
 *    none.
 */
void
sen_parent_set_focus (sen_parent * sp, item_t * focus)
{
  sp->focused = focus;
  gtk_widget_grab_focus (((sentence *) focus->value)->entry);
}

/* Sets the status bar message of a sentence parent.
 *  input:
 *    sp - the sentence parent for which to set the status bar text.
 *    sb_text - the status bar text being set.
 *  output:
 *    none.
 */
void
sen_parent_set_sb (sen_parent * sp, char * sb_text)
{
  unsigned int context_id;
  context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (sp->statusbar),
					     "feedback");
  gtk_statusbar_push (GTK_STATUSBAR (sp->statusbar),
		      context_id,
		      (const char *) sb_text);
}
