/*
 * This file is part of the Simutrans project under the Artistic License.
 * (see LICENSE.txt)
 */

#include <string.h>

#include "../gui_theme.h"
#include "gui_fixedwidth_textarea.h"
#include "../../dataobj/translator.h"
#include "../../utils/cbuffer.h"
#include "../../utils/unicode.h"



gui_fixedwidth_textarea_t::gui_fixedwidth_textarea_t(cbuffer_t* buf_, const sint16 width) :
	reserved_area(0, 0)
{
	buf = buf_;
	set_width(width);
}



void gui_fixedwidth_textarea_t::recalc_size()
{
	scr_size newsize = calc_display_text(scr_coord::invalid, false);
	if (newsize.h != size.h) {
		gui_component_t::set_size( newsize );
	}
}


void gui_fixedwidth_textarea_t::set_width(const scr_coord_val width)
{
	if(  width>0  ) {
		// height is simply reset to 0 as it requires recalculation anyway
		size = scr_size(width,0);
		set_visible(true);

		scr_size newsize = calc_display_text(scr_coord::invalid, false);
		gui_component_t::set_size( newsize );
	}
	else {
		set_visible(false);
		size = scr_size(0, 0);
	}
}



void gui_fixedwidth_textarea_t::set_reserved_area(const scr_size area)
{
	if(  area.w>=0  &&  area.h>=0  ) {
		reserved_area = area;
	}
}



scr_size gui_fixedwidth_textarea_t::get_min_size() const
{
	scr_size size = calc_display_text(scr_coord(0,0), false);
	size.clip_lefttop(reserved_area);
	return size;
}


scr_size gui_fixedwidth_textarea_t::get_max_size() const
{
	return scr_size::inf;
}



/* calculates the height of the text that flows around the world_view
 * if draw is true, it will also draw the text
 * borrowed from ding_infowin_t::calc_draw_info() with adaptation
 */
scr_size gui_fixedwidth_textarea_t::calc_display_text(const scr_coord offset, const bool draw) const
{
	scr_coord_val x=0, word_x=0, y = 0, new_width=get_size().w;

	const char* text(*buf);
	const utf8 *p = (const utf8 *)text;
	const utf8 *line_start = p;
	const utf8 *word_start = p;
	const utf8 *line_end  = p;

	if (!text || !*text) {
		if (!draw) {
			// never had text => too early to guess minimum size
			return scr_size(max(new_width, reserved_area.w), reserved_area.h);
		}
		else {
			// still nothing => then empty ...
			return scr_size(max(1, reserved_area.w), reserved_area.h);
		}
	}

	// pass 1 (and not drawing): find out if we can shrink width
	if(!draw) {
		scr_coord_val new_height = 0;
		scr_coord_val x_size = 0;

		// find the width needed for word next to the reserved text area
		const char* next = text-1;
		do {
			const char* buf = next+1;
			next = strchr(buf, '\n');

			const size_t len = next ? next - buf : 999;
			// we are in the image area
			scr_coord_val px_len = gfx->calc_text_width_n(buf, len);
			if (new_height <= reserved_area.h) {
				px_len += reserved_area.w;
			}
			if (px_len > x_size) {
				x_size = px_len;
			}

			new_height += LINESPACE;

		} while (next  &&  next[1]); // ignore trailing \n

		if (x_size < new_width) {
			// shrink if smaller and return new width
			new_width = x_size;
			return scr_size(new_width, max(new_height, reserved_area.h));
		}
		// else we need pass 2
	}

	// pass 2: height calculation and drawing (if requested)

	// also in unicode *c==0 is end
	while(  *p!= UNICODE_NUL  ||  p!=line_end  ) {

		// force at end of text or newline
		const scr_coord_val max_width = ( y < reserved_area.h ) ? new_width-reserved_area.w : new_width;

		// smaller than the allowed width?
		do {
			// end of line?
			utf32 next_char = utf8_decoder_t::decode(p);

			if(next_char==0  ||  next_char=='\n') {
				line_end = p-1;
				if(  next_char == 0  ) {
					p--;
				}
				word_start = p;
				word_x = 0;
				break;
			}
			// Space: Maybe break here
			else if(  next_char==' '  ||  (next_char >= 0x3000  &&   next_char<0xFE70)  ) {
				// ignore space at start of line
				if(next_char!=' '  ||  x>0) {
					x += gfx->get_char_width( next_char );
				}
				word_start = p;
				word_x = 0;
			}
			else {
				// normal char: retrieve and calculate width
				const scr_coord_val ch_width = gfx->get_char_width( next_char );
				x += ch_width;
				word_x += ch_width;
			}
		} while(  x<=max_width  );

		// spaces at the end can be omitted
		line_end = word_start;
		if(line_end==line_start) {
			// too long word for a single line => break the word
			word_start = line_end = p;
			word_x = 0;
		}
		else if(word_start[-1]==' '  ||  word_start[-1]=='\n') {
			line_end --;
		}

		// start of new line or end of text
		if(draw  &&  (line_end-line_start)!=0) {
			gfx->draw_text_clipped_n( offset.x, offset.y+y, (const char *)line_start, ALIGN_LEFT | DT_CLIP, SYSCOL_TEXT, true, (size_t)(line_end - line_start) CLIP_NUM_DEFAULT);
		}
		y += LINESPACE;
		// back to start of new line
		line_start = word_start;
		x = word_x;
		word_x = 0;
	}

	// reset component height where necessary
	return scr_size(new_width, y);
}


void gui_fixedwidth_textarea_t::draw(scr_coord offset)
{
	size = calc_display_text(offset + get_pos(), true);
}
