/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: html_box.cxx,v 1.30 2003/02/19 11:06:19 schmitzj Exp $)
 * 
 * Copyright (C) 1998 Jürgen Schmitz
*
 * HTML Parser system
 * (c) 1998 by Jürgen Schmitz - all rights reserved
 * THIS IS NOT GPL OR SOMETHING OTHER FREE LICENSE!!
 * it may only be used together with Xclasses!
 *-----------------------------------------------------------------------
 */

#include <string.h>
#include <stdio.h>
#include "include/html_box.h"
#include "include/html_drawspec.h"
#include "include/html_parser.h"
#include "include/html_item.h"
#include "../include/strbuf.h"

#define MIN(v1,v2)	((v1)>(v2)?(v2):(v1))
#define MAX(v1,v2)	((v1)<(v2)?(v2):(v1))

html_box::html_box(FONTSTYLE _fontstyle,FONTSIZE _fontsize,enum ADJUST _adjust) : cursor_color(), marktext(), stringdata()
{
	x=0;
	y=0;
	width=0;
	height=0;
	xoffset=0;
	fontstyle=_fontstyle;
	fontsize=_fontsize;
	adjust=_adjust;
	cursor_x=0;
	cursor_y=0;
	drawable=CreateDrawspec(this);
	items=NULL;
	last=(html_item*)&items;
	workbox=NULL;
	parentbox=NULL;
	Font(fontstyle,fontsize);
	nowrap=0;
	firsti=NULL;
	lasti=NULL;
}
html_box::~html_box()
{
	html_item *i1=items,*i2;
	while (i1)
	{
		i2=i1->next;
		delete i1;
		i1=i2;
	}
	items=NULL;
	last=NULL;
	if (drawable) delete drawable;
	drawable=NULL;
}

void html_box::AddItem(html_item *i)
{
	if (workbox)
		workbox->AddItem(i);
	else if (i)
	{
		if (items)
			last->next=i;
		else
			items=i;
		last=i;
		i->next=NULL;
 	}
}
void html_box::SetData(char *d)
{
	if (workbox)
		workbox->SetData(d);
	else if (d)
	{
		stringdata.clear();
		stringdata+=d;
	}
}
char *html_box::GetData(void)
{
	return stringdata();
}

void html_box::CalcSize(void)
{
	html_item *i=items;
	FONTSTYLE ofy;
	FONTSIZE ofs;
	Font(fontstyle,fontsize);
	cursor_adjust=adjust;
	cursor_nowrap=nowrap;
	cursor_color.replace("#000000");
	ofy=cursor_fontstyle;
	ofs=cursor_fontsize;
	drawable->Font(cursor_fontstyle,cursor_fontsize);

	while (i)
	{
		i->box=this;
		i->CalcSize();

		i->adjust=cursor_adjust;
		i->fontstyle=cursor_fontstyle;
		i->fontsize=cursor_fontsize;
		i->color.replace(cursor_color());
		i->nowrap=cursor_nowrap;
		if (ofy!=cursor_fontstyle || ofs!=cursor_fontsize)
		{
			drawable->Font(cursor_fontstyle,cursor_fontsize);
			ofy=cursor_fontstyle;
			ofs=cursor_fontsize;
		}
		i=i->next;
	}
}
void html_box::CalcBox(int _x,int _y,int _width,int &real_width,int &real_height)
{
	html_item *i=items,*linestart=i;
	int w=0,h=0,old_xoffset=0,old_free_width=0;

	width=0;
	height=0;
	x=_x;
	y=_y;
	cursor_x=0;
	cursor_y=0;
	xoffset=0;

	while (i)
	{
		cursor_adjust=i->adjust;
		cursor_nowrap=i->nowrap;
//		drawable->Font(i->fontstyle,i->fontsize);

		i->hidden=0;		
		i->CalcItem();
		if (xoffset!=old_xoffset)
		{
			cursor_x-=old_xoffset;
			cursor_x+=xoffset;
			old_xoffset=xoffset;
			w+=xoffset;
		}
		i->y=cursor_y;
		i->x=cursor_x;
		if (i->height>h) h=i->height;
		if (cursor_x==xoffset && i->free_width)
		{
		}
		else
		{
			cursor_x+=i->width;
			w+=i->width;
		}
		if ((w>_width && !cursor_nowrap) || i->finishline || i->finishfullline)
		{
			cursor_x=xoffset;
			cursor_y+=h;
			w-=i->width;
			if (i->free_width)
			{
				i->hidden=1;
				i->x=cursor_x;
				i->y=cursor_y;
			}
			else
			{
				i->x=cursor_x;
				i->y=cursor_y;
			}
			if (cursor_adjust!=LEFT)
			{
				if (cursor_adjust==CENTER)
					cursor_x=xoffset+(_width-w)/2;
				else if (cursor_adjust==RIGHT)
					cursor_x=xoffset+_width-w;
				while (linestart!=i)
				{
					linestart->x=cursor_x;
					cursor_x+=linestart->width;
					linestart=linestart->next;
				}
				cursor_x=xoffset;
			}
			if (w>width) width=w;
			height+=h;
			if (i->finishfullline && (w==0 || old_free_width))
			{
				if (old_free_width)
				{
					html_item *i2=linestart;
					while (i2!=i)
					{
						if (!i2->free_width && i2->width>0) break;
						i2=i2->next;
					}
					if (i2==i)
					{
						cursor_y-=h;
						height-=h;
					}
				}
				else
				{
					cursor_y-=h;
					height-=h;
				}
			}
			w=xoffset;	// 0;
			h=0;
			if (i->width>0 && i->height>h) h=i->height;
			if (!i->free_width) w+=i->width;
			cursor_x+=w-xoffset;
			linestart=i;
		}
		old_free_width=i->free_width;

		i=i->next;
	}
	if (w>width) width=w;
	height+=h;
	if (width>_width)
		real_width=width;
	else
	{
		real_width=_width;
		width=_width;
	}
	real_height=height;
}
void html_box::CreateBox(html_drawspec *parent)
{
	html_item *i=items;

	drawable->Create(parent,x,y,width,height);

	Font(fontstyle,fontsize);
	cursor_adjust=adjust;
	cursor_nowrap=nowrap;
	cursor_color.replace("#000000");

	while (i)
	{
		Font(i->fontstyle,i->fontsize);
		i->Create(drawable);
		i=i->next;
	}
}
void html_box::MoveBox(void)
{
	html_item *i=items;

	drawable->Move(x,y,width,height);
	while (i)
	{
		i->Move();
		i=i->next;
	}
}
void html_box::MoveBox(int _x,int _y)
{
	x=_x;
	y=_y;
	MoveBox();
}
void html_box::SetItem(html_item *i)
{
	cursor_x=i->x;
	cursor_y=i->y;
	cursor_adjust=i->adjust;
	cursor_nowrap=i->nowrap;
	if (i->fontstyle!=cursor_fontstyle || i->fontsize!=cursor_fontsize)
	{
		Font(i->fontstyle,i->fontsize);
		drawable->Font(cursor_fontstyle,cursor_fontsize);
	}
	if (strcmp(i->color(),cursor_color()))
	{
		cursor_color.replace(i->color());
		drawable->Color(cursor_color());
	}
}
int html_box::LineOfY(int y)
{
	int l=0,lne=0,yy=0;
	html_item *i=items;
	while (i)
	{
		if (!i->hidden)
		{
			if (i->y!=yy)
			{
				yy=i->y;
				lne++;
			}
			if (i->y+i->height>=y)
			{
				l=lne-1;
				break;
			}
		}
		i=i->next;
	}
	return l;
}
void html_box::DrawBox(int nosub)
{
	html_item *i=items;

	xoffset=0;
	Font(fontstyle,fontsize);
	cursor_adjust=adjust;
	cursor_nowrap=nowrap;
	cursor_color.replace("#000000");

	drawable->Font(cursor_fontstyle,cursor_fontsize);
	drawable->Color(cursor_color());

	while (i)
	{
		if (!i->hidden)
		{
			SetItem(i);
			i->Draw(nosub);
		}
		i=i->next;
	}
}
void html_box::DrawBox(int y1,int y2,int nosub)
{
	html_item *i=items;

	xoffset=0;
	Font(fontstyle,fontsize);
	cursor_adjust=adjust;
	cursor_nowrap=nowrap;
	cursor_color.replace("#000000");

	drawable->Font(cursor_fontstyle,cursor_fontsize);
	drawable->Color(cursor_color());

	while (i)
	{
		if (i->y<=y2 && i->y+height>=y2 && !i->hidden)
		{
			SetItem(i);
			i->Draw(nosub);
		}
		i=i->next;
	}
}
void html_box::FreeBox(void)
{
	html_item *i=items;

	while (i)
	{
		i->Free();
		i=i->next;
	}
	drawable->Free();
}
char *html_box::Mark(int x1,int y1,int x2,int y2,int nosub)
{
	if (LineOfY(y1)<LineOfY(y2) || (LineOfY(y1)==LineOfY(y2) && x1<=x2))
	{
		mx1=x1;
		my1=y1;
		mx2=x2;
		my2=y2;
	}
	else
	{
		mx1=x2;
		my1=y2;
		mx2=x1;
		my2=y1;
	}

	if (firsti) Unmark(nosub);

	marktext.clear();
	html_item *i=items,*lastline=NULL;
	firsti=NULL;
	lasti=NULL;
	lastline=i;

	while (i)
	{
		if (!i->hidden)
		{
			if (!firsti && i->y>=my1)
			{ // back to lastline
				i=lastline;
				firsti=i;
				while (i)
				{
					if (!i->hidden)
					{
						firsti=i;
						if (i->x+i->width>=mx1) break;
						if (i->y>lastline->y) break;
					}
					i=i->next;
				} // we have the first
				lastline=firsti;
				lasti=firsti;
			}
			if (firsti && i->y>=my2 && !i->hidden)
			{ // back to lastline
				i=lastline;
				lasti=i;
				while (i)
				{
					if (!i->hidden)
					{
						if (i->x+i->width>=mx2)
						{
							lasti=i;
							break;
						}
						if (i->y>lastline->y) break;
						lasti=i;
					}
					i=i->next;
				} // we have it
				break;
			}
			if (i->y>lastline->y) lastline=i;
		}
		i=i->next;
	}
	if (firsti)
	{
		if (!lasti) lasti=firsti;
		if (firsti==lasti)
		{
			SetItem(firsti);
			marktext+=firsti->Mark(mx1-firsti->x,my1-firsti->y,mx2-firsti->x,my2-firsti->y,nosub);
		}
		else
		{
			i=firsti;
			while (i!=lasti)
			{
				if (!i->hidden)
				{
					SetItem(i);
					if (i==firsti)
					{
						int mxx2=mx2;
						if (LineOfY(y1)!=LineOfY(y2)) mxx2=mx2+firsti->width+1;
						marktext+=i->Mark(mx1-i->x,my1-i->y,mxx2-i->x,my2-i->y,nosub);
					}
					else
						marktext+=i->Mark(0,0,i->width+1,i->height+1,nosub);
				}
				i=i->next;
			}
			SetItem(lasti);
			marktext+=lasti->Mark(0,0,mx2-lasti->x,my2-lasti->y,nosub);
		}
	}
	return marktext();
}
void html_box::Unmark(int nosub)
{
	marktext.clear();
	html_item *i=items;

	if (firsti)
	{
		if (!lasti) lasti=firsti;
		if (firsti==lasti)
		{
			SetItem(firsti);
			firsti->Unmark(nosub);
		}
		else
		{
			i=firsti;
			while (i!=lasti)
			{
				SetItem(i);
				i->Unmark(nosub);
				i=i->next;
			}
			SetItem(lasti);
			lasti->Unmark(nosub);
		}
	}
	firsti=NULL;
	lasti=NULL;
}

html_box *html_box::CreateBox(void)
{
	html_box *r;
	if (workbox)
		r=workbox->CreateBox();
	else
	{
		r=new html_box(fontstyle,fontsize,adjust);
		workbox=r;
		r->parentbox=this;
	}
	return r;
}
void html_box::ResetBox(void)
{
	if (workbox)
	{
		if (workbox->workbox)
			workbox->ResetBox();
		else
			workbox=NULL;
	}
}
void html_box::SetNoWrap(int OnOff)
{
	nowrap=OnOff?1:0;
	cursor_nowrap=nowrap;
}
void html_box::Position(int &_x,int &_y)
{
	_x+=x;
	_y+=y;
	if (parentbox)
		parentbox->Position(_x,_y);
}
html_box *html_box::GetMasterBox(void)
{
	if (parentbox)
		return parentbox->GetMasterBox();
	else
		return this;
}

void html_box::Callback(html_parser *cb)
{
	callback=cb;
	drawable->Callback();
	nowrap=1;
	cursor_nowrap=1;
}
void html_box::Callback(void)
{
	if (callback) callback->Event(this);
}
void html_box::BackgroundColor(char *c)
{
	drawable->BackgroundColor(c);
}

static int lines=0;
void html_box::Text(char *t,int xmove)
{
	lines=0;
	drawable->Cursor(cursor_x-xmove,cursor_y);
	drawable->Text(t);
}
void html_box::MarkedText(char *t,int x1,int x2,int xmove)
{
	lines=0;
	drawable->Cursor(cursor_x-xmove,cursor_y);
	drawable->MarkedText(t,x1,x2);
}
char *html_box::CutText(char *t,int x1,int x2,int &size)
{
	return drawable->CutText(t,x1,x2,size);
}
void html_box::Line(int x1,int y1,int x2,int y2)
{
	lines=0;
	drawable->Line(x1,y1,x2,y2);
}
int html_box::TextLength(char *t)
{
	lines=0;
	return drawable->TextLength(t);
}
int html_box::TextHeight(char *t)
{
	lines=0;
	return drawable->TextHeight(t);
}

void html_box::Color(char *c)
{
	if (c)
	{
		cursor_color.replace(c);
	}
	else
	{
		cursor_color.replace("#000000");
	}
}

int html_box::Newline(int size)
{
	if (!size) lines=0;
	lines+=size;
	if (lines>size)
		return 0;
	else
		return size;
}
void html_box::XOffset(int offset)
{
	xoffset+=offset;
}
void html_box::Font(FONTSTYLE _style,FONTSIZE _size)
{
	cursor_fontstyle=_style;
	cursor_fontsize=_size;
}
void html_box::Adjust(enum ADJUST _adjust)
{
	cursor_adjust=_adjust;
}
void html_box::NoWrap(int OnOff)
{
	cursor_nowrap=OnOff?1:0;
}
