/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "xp.h"
#include "layout.h"
#include "pa_parse.h"

static LO_Element *lo_duplicate_element(MWContext *context, lo_DocState *state,
					int32 block_watch, LO_Element *eptr);
         
#ifdef XP_WIN16
#define SIZE_LIMIT 32000
#endif


static LO_Element *
lo_line_to_element(lo_DocState *state, int32 line)
{
	LO_Element *eptr;
	LO_Element **line_array;
#ifdef XP_WIN16
        intn a_size;
        intn a_indx;
        intn a_line;
        XP_Block *larray_array;
#endif /* XP_WIN16 */

	if (line >= (state->line_num - 1))
	{
		return(NULL);
	}

#ifdef XP_WIN16
        a_size = SIZE_LIMIT / sizeof(LO_Element *);
        a_indx = (intn)(line / a_size);
        a_line = (intn)(line - (a_indx * a_size));

        XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
        state->line_array = larray_array[a_indx];
        XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
        eptr = line_array[a_line];

        XP_UNLOCK_BLOCK(state->line_array);
        XP_UNLOCK_BLOCK(state->larray_array);
#else
        XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
        eptr = line_array[line];
        XP_UNLOCK_BLOCK(state->line_array);
#endif /* XP_WIN16 */

	return(eptr);
}


static void
lo_set_element_to_line(lo_DocState *state, LO_Element *eptr, int32 line)
{
	LO_Element **line_array;
#ifdef XP_WIN16
        intn a_size;
        intn a_indx;
        intn a_line;
        XP_Block *larray_array;
#endif /* XP_WIN16 */

	if (line >= (state->line_num - 1))
	{
		return;
	}

#ifdef XP_WIN16
        a_size = SIZE_LIMIT / sizeof(LO_Element *);
        a_indx = (intn)(line / a_size);
        a_line = (intn)(line - (a_indx * a_size));

        XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
        state->line_array = larray_array[a_indx];
        XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
	line_array[a_line] = eptr;

        XP_UNLOCK_BLOCK(state->line_array);
        XP_UNLOCK_BLOCK(state->larray_array);
#else
        XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
	line_array[line] = eptr;
        XP_UNLOCK_BLOCK(state->line_array);
#endif /* XP_WIN16 */
}

static void
lo_duplicate_cell(MWContext *context, lo_DocState *state, int32 block_watch,
		LO_CellStruct *old_cell, LO_CellStruct *new_cell)
{
	LO_Element *list_ptr;
	LO_Element *tail_ptr;
	LO_Element *eptr;

	/*
	 * Duplicate the cells normal list of elements.
	 */
	list_ptr = NULL;
	eptr = old_cell->cell_list;
	if (eptr != NULL)
	{
		list_ptr = lo_duplicate_element(context, state,
					block_watch, eptr);
		eptr = eptr->lo_any.next;
	}
	tail_ptr = list_ptr;
	while (eptr != NULL)
	{
		tail_ptr->lo_any.next = lo_duplicate_element(context, state,
							block_watch, eptr);
		tail_ptr = tail_ptr->lo_any.next;
		eptr = eptr->lo_any.next;
	}
	if (tail_ptr != NULL)
	{
		tail_ptr->lo_any.next = NULL;
	}
	new_cell->cell_list = list_ptr;
	new_cell->cell_list_end = tail_ptr;

	/*
	 * Duplicate the cells floating list of elements.
	 */
	list_ptr = NULL;
	eptr = old_cell->cell_float_list;
	if (eptr != NULL)
	{
		list_ptr = lo_duplicate_element(context, state,
						block_watch, eptr);
		eptr = eptr->lo_any.next;
	}
	tail_ptr = list_ptr;
	while (eptr != NULL)
	{
		tail_ptr->lo_any.next = lo_duplicate_element(context, state,
						block_watch, eptr);
		tail_ptr = tail_ptr->lo_any.next;
		eptr = eptr->lo_any.next;
	}
	if (tail_ptr != NULL)
	{
		tail_ptr->lo_any.next = NULL;
	}
	new_cell->cell_float_list = list_ptr;
}


static LO_Element *
lo_duplicate_element(MWContext *context, lo_DocState *state,
			int32 block_watch, LO_Element *eptr)
{
	LO_Element *ret_eptr;

	if (eptr == NULL)
	{
		return(NULL);
	}

	ret_eptr = lo_NewElement(context, state, eptr->type, NULL, 0);
	if (ret_eptr == NULL)
	{
		return(NULL);
	}

	switch(eptr->type)
	{
		case LO_TEXT:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_TextStruct));
			break;
		case LO_LINEFEED:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_LinefeedStruct));
			break;
		case LO_HRULE:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_HorizRuleStruct));
			break;
		case LO_IMAGE:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_ImageStruct));
#ifndef M12N            /* XXXM12N  */
			(void)IL_ReplaceImage(context,
				(LO_ImageStruct *)ret_eptr,
				(LO_ImageStruct *)eptr);
#else
            ((LO_ImageStruct *)ret_eptr)->image_req =
                ((LO_ImageStruct *)eptr)->image_req;
#endif /* M12N */
			FE_ShiftImage(context, (LO_ImageStruct *)ret_eptr);
			break;
		case LO_BULLET:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_BulletStruct));
			break;
		case LO_FORM_ELE:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_FormElementStruct));
			break;
		case LO_SUBDOC:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_SubDocStruct));
			break;
		case LO_TABLE:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_TableStruct));
			break;
		case LO_CELL:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_CellStruct));
			lo_duplicate_cell(context, state, block_watch,
				(LO_CellStruct *)eptr,
				(LO_CellStruct *)ret_eptr);
			break;
		case LO_EMBED:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_EmbedStruct));
			break;
#ifdef SHACK
		case LO_BUILTIN:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_BuiltinStruct));
			break;
#endif /* SHACK */
#ifdef JAVA
		case LO_JAVA:
			XP_BCOPY((char *)eptr, (char *)(ret_eptr),
				sizeof(LO_JavaAppStruct));
			break;
#endif
		case LO_NONE:
		case LO_UNKNOWN:
			ret_eptr = NULL;
			break;
	}

	/*
	 * If we have duplicated the blocking element, set its
	 * new pointer into state.
	 */
	if ((ret_eptr != NULL)&&(block_watch != -1))
	{
		if (ret_eptr->lo_any.ele_id == block_watch)
		{
			state->top_state->layout_blocking_element = 
				ret_eptr;
			block_watch = -1;
		}
	}

	return(ret_eptr);
}


void
lo_ClipLines(MWContext *context, lo_DocState *state, int32 line)
{
	int32 i;
	int32 dy;
	int32 block_watch;
	LO_Element *start_clipping;
	LO_Element *end_clipping;
	LO_Element *eptr;
	LO_Element *end_ptr;
	LO_Element *end_last_line;
	LO_Element *float_list;
	LO_Element *float_ptr;

#ifdef MEMORY_ARENAS	
	lo_arena *old_first_arena;
	lo_arena *old_current_arena;

	/*
	 * Save off the current memory arena, and make a new one.
	 */
	old_first_arena = state->top_state->first_arena;
	old_current_arena = state->top_state->current_arena;
	state->top_state->first_arena = NULL;
	state->top_state->current_arena = NULL;
	lo_InitializeMemoryArena(state->top_state);
	if (state->top_state->first_arena == NULL)
	{
		state->top_state->out_of_memory = TRUE;
		state->top_state->first_arena = old_first_arena;
		state->top_state->current_arena = old_current_arena;
		return;
	}


#endif /* MEMORY_ARENAS */
	
	/*
	 * Clear the selection (if any).
	 */
	LO_HighlightSelection(context, FALSE);
	state->selection_start = NULL;
	state->selection_start_pos = 0;
	state->selection_end = NULL;
	state->selection_end_pos = 0;

	/*
	 * If there is a blocked element that changes, watch it and reset
	 * it properly.
	 */
	block_watch = -1;
	if (state->top_state->layout_blocking_element != NULL)
	{
		block_watch =
		    state->top_state->layout_blocking_element->lo_any.ele_id;
	}

	dy = 0;

	/*
	 * Delete all elements in the lines being clipped off.
	 */
	start_clipping = lo_line_to_element(state, 0);
	end_clipping = lo_line_to_element(state, line);
	if ((end_clipping != NULL)&&(start_clipping != NULL))
	{
		dy = end_clipping->lo_any.y - start_clipping->lo_any.y;
	}
	while ((start_clipping != NULL)&&(start_clipping != end_clipping))
	{
		lo_ScrapeElement(context, start_clipping);
		start_clipping = start_clipping->lo_any.next;
	}

	/*
	 * Loop through all the lines after the clipped line.
	 */
	end_last_line = NULL;
	for (i=0; i < (state->line_num - line - 1); i++)
	{
		LO_Element *line_start;
		LO_Element *lptr;

		/*
		 * Find the start of this line, and the start of
		 * the next line.
		 */
		eptr = lo_line_to_element(state, (line + i));
		end_ptr = lo_line_to_element(state, (line + 1 + i));
		if (eptr != NULL)
		{
			line_start = lo_duplicate_element(context, state,
						block_watch, eptr);
			lptr = line_start;
			eptr = eptr->lo_any.next;
		}
		else
		{
			line_start = NULL;
			lptr = NULL;
		}

		/*
		 * Set the start of this new line into the line array.
		 */
		lo_set_element_to_line(state, line_start, i);

		/*
		 * Copy all the elements in the line.
		 */
		while (eptr != end_ptr)
		{
			lptr->lo_any.y -= dy;
			if (lptr->type == LO_CELL)
			{
				lo_ShiftCell((LO_CellStruct *)lptr, 0, -dy);
			}
			else if (lptr->type == LO_IMAGE)
			{
				FE_ShiftImage(context, (LO_ImageStruct *)lptr);
			}

			lptr->lo_any.next = lo_duplicate_element(context, state,
							block_watch, eptr);
			if (lptr->lo_any.next == NULL)
			{
				break;
			}
			lptr->lo_any.next->lo_any.prev = lptr;
			lptr = lptr->lo_any.next;
			eptr = eptr->lo_any.next;
		}
		if (lptr != NULL)
		{
			lptr->lo_any.y -= dy;
			if (lptr->type == LO_CELL)
			{
				lo_ShiftCell((LO_CellStruct *)lptr, 0, -dy);
			}
			else if (lptr->type == LO_IMAGE)
			{
				FE_ShiftImage(context, (LO_ImageStruct *)lptr);
			}
			lptr->lo_any.next = NULL;
		}

		/*
		 * Link the beginning of the line we just copied to
		 * the end of the last line.
		 */
		line_start->lo_any.prev = end_last_line;
		if (end_last_line != NULL)
		{
			end_last_line->lo_any.next = line_start;
		}
		end_last_line = lptr;
		lptr->lo_any.next = NULL;
	}

	/*
	 * Clear the extra slots at the end.
	 */
	for (i = (state->line_num - line - 1); i < (state->line_num - 1); i++)
	{
		lo_set_element_to_line(state, NULL, i);
	}

	float_list = NULL;
	float_ptr = NULL;
	eptr = state->float_list;
	while (eptr != NULL)
	{
		/*
		 * Floating elements that move up off the top are deleted.
		 */
		if ((eptr->lo_any.y - dy) < 0)
		{
			lo_ScrapeElement(context, eptr);
			eptr = eptr->lo_any.next;
			continue;
		}

		if (float_list == NULL)
		{
			float_list = lo_duplicate_element(context, state,
					block_watch, eptr);
			float_ptr = float_list;
		}
		else
		{
			float_ptr->lo_any.next = lo_duplicate_element(context,
						state, block_watch, eptr);
			float_ptr = float_ptr->lo_any.next;
		}
		float_ptr->lo_any.y -= dy;
		if (float_ptr->type == LO_CELL)
		{
			lo_ShiftCell((LO_CellStruct *)float_ptr, 0, -dy);
		}
		else if (float_ptr->type == LO_IMAGE)
		{
			FE_ShiftImage(context, (LO_ImageStruct *)float_ptr);
		}

		eptr = eptr->lo_any.next;
	}
	if (float_ptr != NULL)
	{
		float_ptr->lo_any.next = NULL;
	}

	/*
	 * If there are blocked tags, one of them may be holded
	 * a partially fetched blocked image element, duplicate it.
	 */
	if (state->top_state->tags != NULL)
	{
		PA_Tag *tag_ptr;

		tag_ptr = state->top_state->tags;
		while (tag_ptr != NULL)
		{
			if ((tag_ptr->type == P_IMAGE)&&
				(tag_ptr->lo_data != NULL))
			{
				LO_Element *new_eptr;
				LO_Element *eptr;

				eptr = (LO_Element *)tag_ptr->lo_data;
				new_eptr = lo_duplicate_element(context, state,
                                                block_watch, eptr);
				tag_ptr->lo_data = (void *)new_eptr;
			}
			tag_ptr = tag_ptr->next;
		}
	}

	state->line_num -= line;
	state->y -= dy;
	state->end_last_line = end_last_line;
	state->float_list = float_list;
	state->current_ele = NULL;

	lo_ShiftMarginsUp(context, state, dy);

	/*
	 * If we had a blocking element that didn't get duplicated, we don't
	 * know what happened to it.
	 */
	if (block_watch != -1)
	{
		state->top_state->layout_blocking_element = NULL;
	}

#ifdef MEMORY_ARENAS	
	(void)lo_FreeMemoryArena(old_first_arena);
#endif /* MEMORY_ARENAS	*/

}

