/* This file is part of
* ======================================================
* 
*           LyX, the High Level Word Processor
* 	 
*	    Copyright (C) 1995 Matthias Ettrich
*
*======================================================*/

#include "config.h"
#include "lyxinset.h"
#include "lyxfont.h"
#include "lyxparagraph.h"
#include "definitions.h"
#include "layout.h"

#define INITIAL_SIZE_PAR 10 /*Number of bytes in one paragraph*/
#define STEP_SIZE_PAR 10 /*Number of bytes added when reallocated*/

/* this is a minibuffer */

static char minibuffer_char;
static LyXFont minibuffer_font;
static Inset *minibuffer_inset;

/* the counter for the paragraph id's */ 
static paragraph_id = 0;

/* first few functions needed for cut and paste and paragraph breaking */
void LyXParagraph::CopyIntoMinibuffer(int pos)
{
  minibuffer_char = GetChar(pos);
  minibuffer_font = GetFontSettings(pos);
  if (minibuffer_char == LYX_META_INSET) {
    if (GetInset(pos))
      minibuffer_inset = GetInset(pos)->Clone();
    else
      minibuffer_inset = NULL;
  }
}


void LyXParagraph::CutIntoMinibuffer(int pos)
{
  minibuffer_char = GetChar(pos);
  minibuffer_font = GetFontSettings(pos);
  if (minibuffer_char == LYX_META_INSET) {
    if (GetInset(pos))
      minibuffer_inset = GetInset(pos);
    else
      minibuffer_inset = NULL;
    
    /* this is a little hack since I want exactly the inset,
       bit just a clone. Otherwise the inset would be delete
       when calling Erase(pos) */
    
    /* find the entry */ 
    InsetTable *tmpi = insettable;
    while (tmpi && tmpi->pos != pos) {
      tmpi=tmpi->next;
    }
    if (tmpi) {		       /* this sould always be true */
      tmpi->inset = NULL;
    }
  }
  
  /* Erase(pos); now the caller is responsible for that*/
}


void LyXParagraph::InsertFromMinibuffer(int pos)
{
   InsertChar(pos, minibuffer_char);
   SetFont(pos, minibuffer_font);
   if (minibuffer_char == LYX_META_INSET)
      InsertInset(pos, minibuffer_inset);
}

/* end of minibuffer */ 


LyXParagraph::LyXParagraph()
{
   int i;
   size = INITIAL_SIZE_PAR;
   last = 0;
   text = new char[size];
   for (i=0; i<10; i++) counter[i] = 0;
   enumdepth = 0;
   itemdepth = 0;
   next = NULL;
   previous = NULL;
   fonttable = NULL;
   insettable = NULL;
   footnoteflag = LYX_NO_FOOTNOTE;
   
   align = LYX_ALIGN_BLOCK;
   labelstring = NULL;
   labelwidthstring = NULL;
   /* table stuff -- begin*/ 
   table = NULL;
   /* table stuff -- end*/ 
   id = paragraph_id++;
   Clear();
}


/* this konstruktor inserts the new paragraph in a list */ 
LyXParagraph::LyXParagraph(LyXParagraph *par)
{
   int i;
   size = INITIAL_SIZE_PAR;
   last = 0;
   text = new char[size];
   for (i=0; i<10; i++) counter[i] = 0;
   enumdepth = 0;
   itemdepth = 0;
   next = par->next;
   if (next)
     next->previous = this;
   previous = par;
   previous->next = this;
   fonttable = NULL;
   insettable = NULL;
   footnoteflag = LYX_NO_FOOTNOTE;
   labelstring = NULL;
   labelwidthstring = NULL;
   /* table stuff -- begin*/ 
   table = NULL;
   /* table stuff -- end*/ 
   id = paragraph_id++;
   Clear();
}


void LyXParagraph::Clear()
{
   line_top = 0;
   line_bottom = 0;
   
   fill_top = 0;
   fill_bottom = 0;
   
   pagebreak_top = 0;
   pagebreak_bottom = 0;

   added_space_top = 0;
   added_space_bottom = 0;

   align = LYX_ALIGN_LAYOUT;
   depth = 0;
   noindent = 0;
   
   if (labelstring)
     delete[] labelstring;
   labelstring = NULL;
   if (labelwidthstring)
     delete[] labelwidthstring;
   labelwidthstring = NULL;
   layout = 0;
}


/* the destruktors removes the new paragraph from the list */ 
LyXParagraph::~LyXParagraph()
{
   if (previous)
     previous->next = next;
   if (next)
     next->previous = previous;

   if (text)
     delete[] text;
   
   if (labelwidthstring)
     delete[] labelwidthstring;
   if (labelstring)
     delete[] labelstring;

   InsetTable *tmpinset;
   while (insettable) {
     tmpinset = insettable;
     insettable = insettable->next;
     if (tmpinset->inset)
       delete tmpinset->inset;
     delete tmpinset;
   }

   FontTable *tmpfont;
   while (fonttable) {
     tmpfont = fonttable;
     fonttable = fonttable->next;
     delete tmpfont;
   }

   /* table stuff -- begin*/ 
   if (table)
      delete table;
   /* table stuff -- end*/ 

}


void LyXParagraph::Erase(int pos)
{
   int i;

   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	NextAfterFootnote()->Erase(pos - last - 1);
      else 
      	fprintf(stderr, "LYX_ERROR: position does not exist \n");
      return;
   }
   if (pos < last) {		       /* <= because last is the
					* next unused position, and you can 
					* use it if you want  */
      
      /* if it is an inset, delete the inset entry */ 
      if (text[pos] == LYX_META_INSET) {
	 /* find the entry */ 
	 InsetTable *tmpi = insettable;
	 InsetTable *tmpi2 = tmpi;
	 while (tmpi && tmpi->pos != pos) {
	    tmpi2=tmpi;
	    tmpi=tmpi->next;
	 }
	 if (tmpi) {		       /* this sould always be true */
	   if (tmpi->inset)     /* delete the inset if it exists */
	     delete tmpi->inset;
	   if (tmpi == insettable)
	     insettable = tmpi->next;
	   else 
	     tmpi2->next = tmpi->next;
	   delete tmpi;
	 }
      }
      
      for (i = pos; i < last - 1; i++)
	{
	   text[i]=text[i+1];
	}
      last--;
      
      /* erase entries in the tables */ 
      int found = 0;
      FontTable *tmp = fonttable;
      FontTable *prev = NULL;
      while (tmp && !found) {
	 if (pos >= tmp->pos && pos <= tmp->pos_end)
	   found = 1;
	 else {
	    prev = tmp;
	    tmp = tmp->next;
	 }
      }
      
      if (found && tmp->pos == tmp->pos_end) {  
	 /* if it is a multi-character font entry, we just make it
	  * smaller (see update below), otherwise we should delete it */
	 if (prev)
	   prev->next = tmp->next;
	 else
	   fonttable = tmp->next;
	 
	 delete tmp;
      }
      
      /* update all other entries */
      
      tmp = fonttable;
      while (tmp) {
	 if (tmp->pos > pos)
	   tmp->pos--;
	 if (tmp->pos_end >= pos)
	   tmp->pos_end--;
	 tmp = tmp->next;
      }
      
      /* update the inset table */ 
      InsetTable *tmpi = insettable;
      while (tmpi) {
	 if (tmpi->pos > pos)
	   tmpi->pos--;
	 tmpi=tmpi->next;
      }
      
   }
  /*
* else
  * fprintf(stderr, "ERASE: what do you want from me? \n"); 
* */ 
}


/* pos is needed to specify the paragraph correctly. Remember the
* closed footnotes */ 
void LyXParagraph::Enlarge(int pos, int number)
{
   int i;
   
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	NextAfterFootnote()->Enlarge(pos - last - 1, number);
      else 
      	fprintf(stderr, "LYX_ERROR: position does not exist \n");
      return;
   }

   if (size - last < number) {
      size += number - size + last + STEP_SIZE_PAR;
      char *tmp = new char[size];
      for (i = 0; i < last; i++)
      	tmp[i] = text[i];
      delete[] text;
      text = tmp;
   }
}


/* make the allocated memory fit to the needed size */
/* used to make a paragraph smaller */
void LyXParagraph::FitSize()
{
  int i;
  
  if (size - last > STEP_SIZE_PAR) {
    size = last + STEP_SIZE_PAR;
    char *tmp = new char[size];
    for (i = 0; i < last; i++)
      tmp[i] = text[i];
    delete[] text;
    text = tmp;
  }
}


void LyXParagraph::InsertChar(int pos, char c)
{
   int i;
   
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	NextAfterFootnote()->InsertChar(pos - last - 1, c);
      else 
      	fprintf(stderr, "LYX_ERROR (InsertChar): position does not exist \n");
      return;
   }
   
   if (last == size)
     {
	size += STEP_SIZE_PAR;
	char *tmp = new char[size];
	for (i = 0; i < last; i++)
	  tmp[i] = text[i];
	delete[] text;
	text = tmp;
     }
   
   for (i = last; i>pos; i--)
     {
	text[i]=text[i-1];
     }
   
   text[pos]=c;
   last++;
   
   /* update the font table */ 
   FontTable *tmp = fonttable;
   while (tmp) {
      if (tmp->pos >= pos)
      	tmp->pos++;
      if (tmp->pos_end >= pos)
      	tmp->pos_end++;
      tmp = tmp->next;
   }
   
   /* update the inset table */ 
   InsetTable *tmpi = insettable;
   while (tmpi) {
      if (tmpi->pos >= pos)
      	tmpi->pos++;
      tmpi=tmpi->next;
   }
}


void LyXParagraph::InsertInset(int pos, Inset *inset)
{
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	NextAfterFootnote()->InsertInset(pos - last - 1, inset);
      else 
      	fprintf(stderr, "LYX_ERROR (InsertInset): position does not exist \n");
      return;
   }

   if (text[pos]!=LYX_META_INSET) {
      fprintf(stderr,
	      "LYX_ERROR (InsertInset): there is no LYX_META_INSET \n");
      return;
   }
   
   if (inset) {
      /* add a new entry in the inset table */ 
      InsetTable *tmpi = new InsetTable;
      tmpi->pos = pos;
      tmpi->inset = inset;
      tmpi->next = insettable;
      insettable = tmpi;
   }
}


Inset* LyXParagraph::GetInset(int pos)
{
   if (pos >= last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	return NextAfterFootnote()->GetInset(pos - last - 1);
      else 
      	fprintf(stderr, "LYX_ERROR (GetInset): position does not exist \n");
      return NULL;
   }
   
   /* find the inset */ 
   InsetTable *tmpi = insettable;
   
   while (tmpi && tmpi->pos != pos)
      tmpi = tmpi->next;
   
   if (tmpi)
      return tmpi->inset;
   else {
      fprintf(stderr, "LYX_ERROR (GetInset): Inset does not exist \n");
      text[pos] = ' ';
      return NULL;
   }
}


LyXFont LyXParagraph::GetFontSettings(int pos)
{
   LyXFont patternfont = {LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT};
   int found = 0;
   FontTable *tmp = fonttable;
   
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	return NextAfterFootnote()->GetFontSettings(pos - last - 1);
      else 
      	fprintf(stderr, "LYX_ERROR: position does not exist \n");
      return patternfont;
   }
   
   if (pos < last) {
      while (tmp && !found) {
	 if (pos >= tmp->pos && pos <= tmp->pos_end) 
	   found = 1;
	 else
	   tmp = tmp->next;
      }
      if (found) {
	 return tmp->font;
      }
      else
      	return patternfont;
   }
   else {
      /* 
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	return NextAfterFootnote()->GetFontSettings(pos - last);
      else
      	return patternfont;
       */
      
      if (pos)
      	return GetFontSettings(pos - 1);
      else
      	return patternfont;
      
   }
}


char LyXParagraph::GetChar(int pos)
{
  /* a workaround to 'fix' some bugs in text-class */
  if (pos < 0)
    return '\0';
   
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	return NextAfterFootnote()->GetChar(pos - last - 1);
      else 
      	fprintf(stderr, "LYX_ERROR (GetChar): position does not exist \n");
      return 'F';
   }
   
   if (pos < last) {
      return text[pos];
   }
   else {
      /* we should have a footnote environment */ 
      if (!next || next->footnoteflag == LYX_NO_FOOTNOTE)
      	return 'F';
      
      if (next->footnotekind == LYX_FOOTNOTE)
      	return LYX_META_FOOTNOTE;
      if (next->footnotekind == LYX_MARGIN)
      	return LYX_META_MARGIN;
      if (next->footnotekind == LYX_FIG)
      	return LYX_META_FIG;
      if (next->footnotekind == LYX_TAB)
      	return LYX_META_TAB;
      
      return 'F';		       /* this should not happen!  */
   }
}


int LyXParagraph::Last()
{
   if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE)
     return last + NextAfterFootnote()->Last() + 1;   /* the 1 is the symbol
						       * for the footnote */
   else
     return last;
}


LyXParagraph *LyXParagraph::ParFromPos(int pos)
{
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	return NextAfterFootnote()->ParFromPos(pos - last - 1);
      else 
      	fprintf(stderr, "LYX_ERROR: position does not exist \n");
      return this;
   }
   else
     return this;
}


int LyXParagraph::PositionInParFromPos(int pos)
{
   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	return NextAfterFootnote()->PositionInParFromPos(pos - last - 1);
      else 
      	fprintf(stderr, "LYX_ERROR: position does not exist \n");
      return pos;
   }
   else
     return pos;
}


void LyXParagraph::SetFont(int pos, LyXFont font)
{
   LyXFont patternfont = {LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT,
			  LYX_LAYOUT_DEFAULT};
   FontTable *tmp2;

   /* > because last is the next unused position, and you can 
    * use it if you want  */
   if (pos > last) {
      if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) 
      	NextAfterFootnote()->SetFont(pos - last - 1, font);
      else 
      	fprintf(stderr, "LYX_ERROR: position does not exist \n");
      return;
   }
   
   
   /* if this paragraph is a open footnote, just make the font 
    * bigger, so that it appears correctly */ 
   if (footnoteflag == LYX_OPEN_FOOTNOTE
       && footnotekind == LYX_FOOTNOTE) {
	  if (font.size < LYX_SIZE_HUGE) font.size++;   /* that means even
							 * no default or
							 * unchange value */
       }
   
   int found = 0;
   FontTable *tmp = fonttable;
   while (tmp && !found) {
      if (pos >= tmp->pos && pos <= tmp->pos_end)
      	found = 1;
      else
      	tmp = tmp->next;
   }
   
   if (!found) {
      /* if we did not found a font entry, and it is only
       * default, we just forget it */ 
      if (font.family == patternfont.family
	  && font.series == patternfont.series
	  && font.shape == patternfont.shape
	  && font.size == patternfont.size
	  && font.bar == patternfont.bar
	  && font.latex == patternfont.latex)
      	return;
      
      /* ok, we did not find a font entry. But maybe there is exactly
       * the needed font entry one position left */ 
      found = 0;
      tmp2 = fonttable;
      while (tmp2 && !found) {
	 if (pos - 1 >= tmp2->pos && pos - 1 <= tmp2->pos_end)
	   found = 1;
	 else
	   tmp2 = tmp2->next;
      }
      if (found) {
	 /* ok there is one. maybe it is exactly the needed font */
	 if ((tmp2->font.family == font.family
	      || (font.family == LYX_NO_FONT_CHANGE
		  && tmp2->font.family == LYX_LAYOUT_DEFAULT))
	     &&
	     (tmp2->font.series == font.series
	      || (font.series == LYX_NO_FONT_CHANGE
		  && tmp2->font.series == LYX_LAYOUT_DEFAULT))
	     &&
	     (tmp2->font.shape == font.shape
	      || (font.shape == LYX_NO_FONT_CHANGE
		  && tmp2->font.shape == LYX_LAYOUT_DEFAULT))
	     &&
	     (tmp2->font.size == font.size
	      || (font.size == LYX_NO_FONT_CHANGE
		  && tmp2->font.size == LYX_LAYOUT_DEFAULT))
	     &&
	     (tmp2->font.bar == font.bar
	      || (font.bar == LYX_NO_FONT_CHANGE
		  && tmp2->font.bar == LYX_LAYOUT_DEFAULT))
	     &&
	     (tmp2->font.latex == font.latex
	      || (font.latex == LYX_NO_FONT_CHANGE
		  && tmp2->font.latex == LYX_LAYOUT_DEFAULT))
	     
	     ) {
		/* put the position under the font */ 
		tmp2->pos_end++;
		return;
	     }
      }
      /* Add a new entry in the 
       * fonttable for the position */
      tmp = new FontTable;
      tmp->pos = pos;
      tmp->pos_end = pos;
      tmp->font = patternfont;
      tmp->next = fonttable;
      fonttable = tmp;
   }
   else {
      /* we found a font entry. maybe we have to split it and create
       * a new one */ 
      
      if (tmp->pos != tmp->pos_end) {  /* more than one character  */
	 
	 if (pos == tmp->pos) {
	    /* maybe we could enlarge the left fonttable */ 
	    
	    found = 0;
	    tmp2 = fonttable;
	    while (tmp2 && !found) {
	       if (pos - 1 >= tmp2->pos && pos - 1 <= tmp2->pos_end)
	       	 found = 1;
	       else
	       	 tmp2 = tmp2->next;
	    }
	    if (found) {
	       /* ok there is one. maybe it is exactly the needed font */
	       if ((tmp2->font.family == font.family
		    || (font.family == LYX_NO_FONT_CHANGE
			&& tmp2->font.family == tmp->font.family))
		   &&
		   (tmp2->font.series == font.series
		    || (font.series == LYX_NO_FONT_CHANGE
			&& tmp2->font.series == tmp->font.series))
		   &&
		   (tmp2->font.shape == font.shape
		    || (font.shape == LYX_NO_FONT_CHANGE
			&& tmp2->font.shape == tmp->font.shape))
		   &&
		   (tmp2->font.size == font.size
		    || (font.size == LYX_NO_FONT_CHANGE
			&& tmp2->font.size == tmp->font.size))
		   &&
		   (tmp2->font.bar == font.bar
		    || (font.bar == LYX_NO_FONT_CHANGE
			&& tmp2->font.bar == tmp->font.bar))
		   &&
		   (tmp2->font.latex == font.latex
		    || (font.latex == LYX_NO_FONT_CHANGE
			&& tmp2->font.latex == tmp->font.latex))
		   ) {
		      /* put the position under the font */ 
		      tmp2->pos_end++;
		      tmp->pos++;
		      return;
		   }
	    }
	    
	    /* Add a new entry in the 
	     * fonttable for the position */
	    tmp2 = new FontTable;
	    tmp2->pos = pos + 1;
	    tmp2->pos_end = tmp->pos_end;
	    tmp2->font = tmp->font;
	    tmp->pos_end = pos;
	    tmp2->next = fonttable;
	    fonttable = tmp2;
	 }
	 else if (pos == tmp->pos_end) {
	    /* Add a new entry in the 
	     * fonttable for the position */
	    tmp2 = new FontTable;
	    tmp2->pos = tmp->pos;
	    tmp2->pos_end = tmp->pos_end - 1;
	    tmp2->font = tmp->font;
	    tmp->pos = tmp->pos_end;
	    tmp2->next = fonttable;
	    fonttable = tmp2;
	 }
	 else {
	    /* Add a new entry in the 
	     * fonttable for the position */
	    tmp2 = new FontTable;
	    tmp2->pos = tmp->pos;
	    tmp2->pos_end = pos - 1;
	    tmp2->font = tmp->font;
	    tmp2->next = fonttable;
	    fonttable = tmp2;
	    
	    tmp2 = new FontTable;
	    tmp2->pos = pos + 1;
	    tmp2->pos_end = tmp->pos_end;
	    tmp2->font = tmp->font;
	    tmp2->next = fonttable;
	    fonttable = tmp2;
	    
	    tmp->pos = pos;
	    tmp->pos_end = pos;
	 }
      }
   }
   
   if (font.family != LYX_NO_FONT_CHANGE)
     tmp->font.family = font.family;
   if (font.series != LYX_NO_FONT_CHANGE)
     tmp->font.series = font.series;
   if (font.shape != LYX_NO_FONT_CHANGE)
     tmp->font.shape = font.shape;
   if (font.size != LYX_NO_FONT_CHANGE)
     tmp->font.size = font.size;
   if (font.latex != LYX_NO_FONT_CHANGE)
     tmp->font.latex = font.latex;
   
   if (font.bar != LYX_NO_FONT_CHANGE)
      tmp->font.bar = font.bar;
}

   
/* this function is able to hide closed footnotes */
LyXParagraph *LyXParagraph::Next()
{
   LyXParagraph *tmp;
   if (next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) {
      tmp = next;
      while (tmp && tmp->footnoteflag == LYX_CLOSED_FOOTNOTE)
      	tmp = tmp->next;
      if (tmp && tmp->footnoteflag != LYX_CLOSED_FOOTNOTE) 
      	return tmp->Next();	       /* there can be more than one footnote
					* in a logical paragraph */
      else
      	return next; 		       /* this should never happen! */
   }
   else
     return next;
}


LyXParagraph *LyXParagraph::NextAfterFootnote()
{
   LyXParagraph *tmp;
   if (next && next->footnoteflag != LYX_NO_FOOTNOTE) {
      tmp = next;
      while (tmp && tmp->footnoteflag != LYX_NO_FOOTNOTE)
      	tmp = tmp->next;
      if (tmp && tmp->footnoteflag != LYX_CLOSED_FOOTNOTE) 
      	return tmp;	       /* there can be more than one footnote
				* in a logical paragraph */
      else
      	return next; 		       /* this should never happen! */
   }
   else
     return next;
}


LyXParagraph *LyXParagraph::PreviousBeforeFootnote()
{
   LyXParagraph *tmp;
   if (previous && previous->footnoteflag != LYX_NO_FOOTNOTE) {
      tmp = next;
      while (tmp && tmp->footnoteflag != LYX_NO_FOOTNOTE)
      	tmp = tmp->previous;
      if (tmp && tmp->footnoteflag != LYX_CLOSED_FOOTNOTE) 
      	return tmp;	       /* there can be more than one footnote
				* in a logical paragraph */
      else
      	return previous; 		       /* this should never happen! */
   }
   else
     return previous;
}


LyXParagraph *LyXParagraph::LastPhysicalPar()
{
   LyXParagraph *tmp;
   if (footnoteflag != LYX_NO_FOOTNOTE)
     return this;
   
   tmp = this;
   while (tmp->next && tmp->next->footnoteflag != LYX_NO_FOOTNOTE)
     tmp = tmp->NextAfterFootnote();
   
   return tmp;
   
}


LyXParagraph *LyXParagraph::FirstPhysicalPar()
{
   LyXParagraph *tmppar;
   
   if (!IsDummy())
     return this;
   tmppar = this;
   while (tmppar && (tmppar->IsDummy()
		     || tmppar->footnoteflag != LYX_NO_FOOTNOTE))
     tmppar = tmppar->previous;
   
   if (!tmppar)
     return this;	       /* this should never happen!  */
   else
     return tmppar;
   
}


/* this function is able to hide closed footnotes */
LyXParagraph *LyXParagraph::Previous()
{
   LyXParagraph *tmp;
   tmp = previous;
   if (!tmp)
     return tmp;
   
   if (tmp->previous && tmp->previous->footnoteflag == LYX_CLOSED_FOOTNOTE) {
      tmp = tmp->previous;
      while (tmp && tmp->footnoteflag == LYX_CLOSED_FOOTNOTE)
      	tmp = tmp->previous;
      if (tmp && tmp->footnoteflag != LYX_CLOSED_FOOTNOTE) 
      	return tmp->next->Previous();	

      else
      	return previous; 
   }
   else
     return previous;
}

   
void LyXParagraph::BreakParagraph(int pos, int flag)
{
   int i, pos_end, pos_first;

   /* create a new paragraph */
   LyXParagraph *par = ParFromPos(pos);
   LyXParagraph *firstpar = FirstPhysicalPar();
   
   LyXParagraph *tmp = new LyXParagraph(par);
   
   tmp->footnoteflag = footnoteflag;
   tmp->footnotekind = footnotekind;
   
   /* this is an idea for a more userfriendly layout handling, I will
    * see what the users say */
   
   /* layout stays the same with latex-environments */ 
   if (flag) {
      tmp->SetOnlyLayout(firstpar->layout);
      tmp->SetLabelWidthString(firstpar->labelwidthstring);
   }
   
   if (Last() > pos || !Last() || flag == 2) {
      tmp->SetOnlyLayout(firstpar->layout);
      tmp->align = firstpar->align;
      tmp->SetLabelWidthString(firstpar->labelwidthstring);
      
      tmp->line_bottom = firstpar->line_bottom;
      firstpar->line_bottom = 0;
      tmp->pagebreak_bottom = firstpar->pagebreak_bottom;
      firstpar->pagebreak_bottom = 0;
      tmp->added_space_bottom = firstpar->added_space_bottom;
      firstpar->added_space_bottom = 0;
      
      tmp->fill_bottom = firstpar->fill_bottom;
      firstpar->fill_bottom = 0;
   
      tmp->depth = firstpar->depth;
      tmp->noindent = firstpar->noindent;
   
      /* copy everything behind the break-position to the new paragraph */
      pos_first = 0;
      while (ParFromPos(pos_first) != par)
       pos_first++;
   
      pos_end = pos_first + par->last - 1;
   
      /* make shure there is enough memory for the now larger paragraph.
       * This is not neccessary, because InsertFromMinibuffer will enlarge
       * the memory (it uses InsertChar of course). But doing it by hand
       * is MUCH faster! (only one time, not thousend times!!) */
   
      tmp->Enlarge(0, pos_end - pos);
   
      for (i = pos; i <= pos_end; i++) {
	  par->CutIntoMinibuffer(i - pos_first);
	  tmp->InsertFromMinibuffer(i - pos);
      }

      for (i = pos_end; i >= pos; i--)
       par->Erase(i - pos_first);
      /* free memory of the now shorter paragraph*/
      par->FitSize();
   }
   
   /* just an idea of me */ 
   if (!pos) {
      tmp->line_top = firstpar->line_top;
      tmp->pagebreak_top = firstpar->pagebreak_top;
      tmp->added_space_top = firstpar->added_space_top;
      tmp->fill_top = firstpar->fill_top;
      firstpar->Clear();
      /* layout stays the same with latex-environments */ 
      if (flag) {
	 firstpar->SetOnlyLayout(tmp->layout);
	 firstpar->SetLabelWidthString(tmp->labelwidthstring);
	 firstpar->depth = tmp->depth;
      }
   }
}


void LyXParagraph::MakeSameLayout(LyXParagraph *par)
{
  par = par->FirstPhysicalPar();
  footnoteflag = par->footnoteflag;
  footnotekind = par->footnotekind;
   
  layout = par->layout;
  align = par-> align;
  SetLabelWidthString(par->labelwidthstring);
  
  line_bottom = par->line_bottom;
  pagebreak_bottom = par->pagebreak_bottom;
  added_space_bottom = par->added_space_bottom;
  fill_bottom = par->fill_bottom;
  
  line_top = par->line_top;
  pagebreak_top = par->pagebreak_top;
  added_space_top = par->added_space_top;
  fill_top = par->fill_top;
  
  noindent = par->noindent;
  depth = par->depth;
}


LyXParagraph *LyXParagraph::FirstSelfrowPar()
{
   LyXParagraph *tmppar;
   
   tmppar = this;
   while (tmppar && (
		     (tmppar->IsDummy() && tmppar->previous->footnoteflag == LYX_CLOSED_FOOTNOTE)
		     || tmppar->footnoteflag == LYX_CLOSED_FOOTNOTE))
     tmppar = tmppar->previous;
   
   if (!tmppar)
     return this;	       /* this should never happen!  */
   else
     return tmppar;
}


LyXParagraph *LyXParagraph::Clone()
{
  int i;
   
   /* create a new paragraph */
   LyXParagraph *result = new LyXParagraph();
   
   result->MakeSameLayout(this);

   /* this is because of the dummy layout of the paragraphs that
      follow footnotes */
   result->layout = layout;
   
   /* table stuff -- begin*/ 
   if (table)
     result->table = table->Clone();
   else
     result->table = NULL;
   /* table stuff -- end*/ 
   
   /* copy everything behind the break-position to the new paragraph */
   
   /* make shure there is enough memory for the now larger paragraph.
    * This is not neccessary, because InsertFromMinibuffer will enlarge
    * the memory (it uses InsertChar of course). But doing it by hand
    * is MUCH faster! (only one time, not thousend times!!) */
   
   result->Enlarge(0, last+2);
   
   for (i = 0; i < last; i++) {
     
     CopyIntoMinibuffer(i);
     result->InsertFromMinibuffer(i);
   }
   return result;
}


char LyXParagraph::HasSameLayout(LyXParagraph* par)
{
   par = par->FirstPhysicalPar();
   
  return (
	  par->footnoteflag == footnoteflag &&
	  par->footnotekind == footnotekind&&
	  
	  par->layout == layout &&

	  par->align == align&&
	  
	  par->line_bottom == line_bottom&&
	  par->pagebreak_bottom == pagebreak_bottom&&
	  par->added_space_bottom == added_space_bottom&&
	  par->fill_bottom == fill_bottom&&
	  
	  par->line_top == line_top&&
	  par->pagebreak_top == pagebreak_top&&
	  par->added_space_top == added_space_top&&
	  par->fill_top == fill_top&&

	  par->table == table && // what means: NO TABLE AT ALL 
	  
	  par->noindent == noindent&&
	  par->depth == depth);
}


void LyXParagraph::BreakParagraphConservative(int pos)
{
   int i, pos_end, pos_first;
   
   /* create a new paragraph */
   LyXParagraph *par = ParFromPos(pos);

   LyXParagraph *tmp = new LyXParagraph(par);
   
   tmp->MakeSameLayout(par);
   
   if (Last() > pos) {   
     /* copy everything behind the break-position to the new paragraph */
      pos_first = 0;
      while (ParFromPos(pos_first) != par)
       pos_first++;
   
      pos_end = pos_first + par->last - 1;
   
      /* make shure there is enough memory for the now larger paragraph.
       * This is not neccessary, because InsertFromMinibuffer will enlarge
       * the memory (it uses InsertChar of course). But doing it by hand
       * is MUCH faster! (only one time, not thousend times!!) */
   
      tmp->Enlarge(0, pos_end - pos);
   
      for (i = pos; i <= pos_end; i++) {
      
	  par->CutIntoMinibuffer(i - pos_first);
	  tmp->InsertFromMinibuffer(i - pos);
      }
      for (i = pos_end; i >= pos; i--)
       par->Erase(i - pos_first);

      /* free memory of the now shorter paragraph*/
      par->FitSize();
   }
}
   

/* be carefull, this does not make any check at all */ 
void LyXParagraph::PasteParagraph()
{
   int i, pos_end, pos_insert;
   LyXParagraph *the_next;
   
   /* copy the next paragraph to this one */
   the_next = Next();
   
   LyXParagraph *firstpar = FirstPhysicalPar();
   
   /* first the DTP-stuff */ 
   firstpar->line_bottom = the_next->line_bottom;
   firstpar->added_space_bottom = the_next->added_space_bottom;
   firstpar->fill_bottom = the_next->fill_bottom;
   firstpar->pagebreak_bottom = the_next->pagebreak_bottom;
   
   pos_end = the_next->last - 1;
   pos_insert = Last();
   
   /* enlarge the paragraph. This is faster than enlarge it
    * every 10th insertion. */ 
   if (pos_end >= 0)
     Enlarge(pos_insert, pos_end);
      
   /* ok, now copy the paragraph */ 
   for (i = 0; i <= pos_end; i++) {
      the_next->CutIntoMinibuffer(i);
      InsertFromMinibuffer(pos_insert + i);
   }
   
   /* delete the next paragraph */
   delete the_next;
}


void LyXParagraph::OpenFootnote(int pos)
{
   LyXParagraph *par = ParFromPos(pos);
   par = par->next;
   while (par && par->footnoteflag == LYX_CLOSED_FOOTNOTE) {
      par->footnoteflag = LYX_OPEN_FOOTNOTE;
      par = par->next;
   }
}


void LyXParagraph::CloseFootnote(int pos)
{
   LyXParagraph *par = ParFromPos(pos);
   par = par->next;
   while (par && par->footnoteflag == LYX_OPEN_FOOTNOTE) {
      par->footnoteflag = LYX_CLOSED_FOOTNOTE;
      par = par->next;
   }
}


int LyXParagraph::GetLayout()
{
   return FirstPhysicalPar()->layout;
}


char LyXParagraph::GetDepth()
{
   return FirstPhysicalPar()->depth;
}


char LyXParagraph::GetAlign()
{
   return FirstPhysicalPar()->align;
}


char* LyXParagraph::GetLabelString()
{
   return FirstPhysicalPar()->labelstring;
}


char LyXParagraph::GetCounter(int i)
{
   return FirstPhysicalPar()->counter[i];
}


/* the next two functions are for the manual labels */ 
char* LyXParagraph::GetLabelWidthString()
{
   if (FirstPhysicalPar()->labelwidthstring)
     return FirstPhysicalPar()->labelwidthstring;
   else
     return "Senseless with this layout!";
}


void LyXParagraph::SetLabelWidthString(char* s)
{
   LyXParagraph *par = FirstPhysicalPar();

   if (par->labelwidthstring)
     delete[] par->labelwidthstring;
   
   par->labelwidthstring = StringCopy(s);
}


void LyXParagraph::SetOnlyLayout(char new_layout)
{
   LyXParagraph *par = FirstPhysicalPar();
   par->layout = new_layout;
   /* table stuff -- begin*/ 
   if (table) 
      par->layout = 0;
   /* table stuff -- end*/ 
}


void LyXParagraph::SetLayout(char new_layout)
{
   LyXParagraph *par = FirstPhysicalPar();
   par->layout = new_layout;
   par->labelwidthstring = NULL;
   par->align = LYX_ALIGN_LAYOUT;
   //par->depth = 0;
   par->added_space_top = 0;
   par->added_space_bottom = 0;
   /* table stuff -- begin*/ 
   if (table) 
      par->layout = 0;
   /* table stuff -- end*/ 
}


/* if the layout of a paragraph contains a manual label, the beginning of the 
* main body is the beginning of the second word. This is what the par-
* function returns. If the layout does not contain a label, the main
* body always starts with position 0. This differentiation is necessary,
* because there cannot be a newline or a blank <= the beginning of the 
* main body in TeX. */ 

int LyXParagraph::BeginningOfMainBody()
{
   if (FirstPhysicalPar() != this)
     return -1;
   
   int i = 0;
   
   while (i < last   &&  !(i > 1 && GetChar(i-1)==' ')
	  && GetChar(i)!=LYX_META_NEWLINE)
     i++;
   
   if (i==0 && i == last &&
       !(footnoteflag==LYX_NO_FOOTNOTE
	 && next && next->footnoteflag != LYX_NO_FOOTNOTE)
       )
     i++;			       /* the cursor should not jump  
					* to the main body if there
					* is nothing in! */
   return i;
}


LyXParagraph* LyXParagraph::DepthHook(int deth)
{
   LyXParagraph *newpar = this;
   if (deth < 0)
     return NULL;
   
   do {
      newpar = newpar->FirstPhysicalPar()->Previous();
   } while (newpar && newpar->GetDepth() > deth
	    && newpar->footnoteflag == footnoteflag);
   
   if (!newpar) {
      if (Previous() || GetDepth())
      	 fprintf(stderr, "LYX_ERROR (paragraph, DepthHook()): no hook \n");
      newpar = this;
   }
   return newpar->FirstPhysicalPar();
}


int LyXParagraph::AutoDeleteInsets()
{
   InsetTable *tmpi = insettable;
   InsetTable *tmpi2 = tmpi;
   int i=0;
   while (tmpi) {
      tmpi2 = tmpi;
      tmpi = tmpi->next;
      if (tmpi2->inset)
      if (tmpi2->inset->AutoDelete()) {
	i++;
	Erase(tmpi2->pos);
      } else {}
      else
        fprintf(stderr, "LYX::LyXParagraph CANNOT AUTODELETEINSET\n");
   }
   return i;
}


Inset* LyXParagraph::ReturnNextInsetPointer(int &pos)
{
  InsetTable *tmpi = insettable;
  InsetTable *tmpi2 = NULL;
  while (tmpi){
    if (tmpi->pos >= pos) {
      if (!tmpi2 || tmpi->pos < tmpi2->pos)
	tmpi2 = tmpi;
    }
    tmpi=tmpi->next;
  }
  if (tmpi2){
    pos = tmpi2->pos;
    return tmpi2->inset;
  }
  else
    return NULL;
}


  /* returns -1 if inset not found */
int LyXParagraph::GetPositionOfInset(Inset* inset)
{
  /* find the entry */ 
  InsetTable *tmpi = insettable;
  while (tmpi && tmpi->inset != inset) {
    tmpi=tmpi->next;
  }
  if (tmpi && tmpi->inset)
    return tmpi->pos;
  else{
    /* think about footnotes */
    if (footnoteflag == LYX_NO_FOOTNOTE 
	&& next && next->footnoteflag == LYX_CLOSED_FOOTNOTE) {
      return last + 1 + NextAfterFootnote()->GetPositionOfInset(inset);
    }
    return -1;
  }
}
