/*
 *	$Id$
 */

#include  "ml_line_bidi.h"

#include  <string.h>		/* memset */
#include  <kiklib/kik_debug.h>
#include  <kiklib/kik_mem.h>	/* alloca */


/* --- static functions --- */

static void
copy_char_with_mirror_check(
	ml_char_t *  dst ,
	ml_char_t *  src ,
	u_int16_t *  visual_order ,
	u_int16_t  visual_order_size ,
	int  pos
	)
{
	ml_char_copy( dst , src) ;

	if( (( pos > 0 && visual_order[pos - 1] == visual_order[pos] + 1) ||
	     ( pos + 1 < visual_order_size &&
	       visual_order[pos] == visual_order[pos + 1] + 1)) )
	{
		/*
		 * Pos is RTL character.
		 * => XXX It is assumed that pos is always US_ASCII or ISO10646_UCS4_1.
		 */
	#if  0
		mkf_charset_t  cs ;

		if( (cs = ml_char_cs( dst)) == US_ASCII || cs == ISO10646_UCS4_1)
	#endif
		{
			u_int  mirror ;

			if( ( mirror = ml_bidi_get_mirror_char( ml_char_code( dst))))
			{
				ml_char_set_code( dst , mirror) ;
			}
		}
	}
}

static void
set_visual_modified(
	ml_line_t *  line ,
	int  logical_mod_beg ,
	int  logical_mod_end
	)
{
	int  visual_mod_beg ;
	int  visual_mod_end ;

	/* same as 0 <= char_index < size */
	if( ((u_int)logical_mod_beg) >= line->ctl_info.bidi->size ||
	    ((u_int)logical_mod_end) >= line->ctl_info.bidi->size)
	{
		ml_line_set_modified_all( line) ;

		return ;
	}

	visual_mod_beg = line->ctl_info.bidi->visual_order[logical_mod_beg] ;
	visual_mod_end = line->ctl_info.bidi->visual_order[logical_mod_end] ;

	if( visual_mod_beg > visual_mod_end)
	{
		int  tmp ;

		tmp = visual_mod_end ;
		visual_mod_end = visual_mod_beg ;
		visual_mod_beg = tmp ;
	}

#if  0
	kik_debug_printf( "%p %d %d -> %d %d\n" , line ,
		logical_mod_beg , logical_mod_end ,
		visual_mod_beg , visual_mod_end) ;
#endif

	ml_line_set_modified( line , visual_mod_beg , visual_mod_end) ;
}


/* --- global functions --- */

int
ml_line_set_use_bidi(
	ml_line_t *  line ,
	int  flag
	)
{
	if( flag)
	{
		if( ml_line_is_using_bidi( line))
		{
			return  1 ;
		}
		else if( line->ctl_info_type != 0)
		{
			return  0 ;
		}

		if( ( line->ctl_info.bidi = ml_bidi_new()) == NULL)
		{
			return  0 ;
		}

		line->ctl_info_type = VINFO_BIDI ;
	}
	else
	{
		if( ml_line_is_using_bidi( line))
		{
			ml_bidi_delete( line->ctl_info.bidi) ;
			line->ctl_info_type = 0 ;
		}
	}

	return  1 ;
}

/* The caller should check ml_line_is_using_bidi() in advance. */
int
ml_line_bidi_render(
	ml_line_t *  line ,	/* is always modified */
	ml_bidi_mode_t  bidi_mode ,
	const char *  separators
	)
{
	int  ret ;

	if( ml_line_is_real_modified( line))
	{
		int  base_was_rtl ;

		base_was_rtl = BASE_IS_RTL( line->ctl_info.bidi) ;

		if( ( ret = ml_bidi( line->ctl_info.bidi , line->chars ,
				line->num_of_filled_chars , bidi_mode , separators)) <= 0)
		{
			return  ret ;
		}

		/* Conforming line->change_{beg|end}_col to visual mode. */
		if( base_was_rtl != BASE_IS_RTL( line->ctl_info.bidi))
		{
			/*
			 * shifting RTL-base to LTR-base or LTR-base to RTL-base.
			 * (which requires redrawing line all)
			 */
			ml_line_set_modified_all( line) ;

			return  1 ;
		}
	}
	else
	{
		ret = 1 ;	/* order is not changed */
	}

	if( ret == 2) /* order is changed => force to redraw all */
	{
		if( ml_line_get_end_of_modified( line) > ml_line_end_char_index( line))
		{
			ml_line_set_modified_all( line) ;
		}
		else
		{
			ml_line_set_modified( line , 0 , ml_line_end_char_index( line)) ;
		}
	}
	else if( HAS_RTL(line->ctl_info.bidi))
	{
		set_visual_modified( line , ml_line_get_beg_of_modified( line) ,
			ml_line_get_end_of_modified( line)) ;
	}

	return  1 ;
}

/* The caller should check ml_line_is_using_bidi() in advance. */
int
ml_line_bidi_visual(
	ml_line_t *  line
	)
{
	int  count ;
	ml_char_t *  src ;
	
	if( line->ctl_info.bidi->size == 0 ||
	    ! HAS_RTL( line->ctl_info.bidi))
	{
	#ifdef  __DEBUG
		kik_debug_printf( KIK_DEBUG_TAG " Not need to visualize.\n") ;
	#endif

		return  1 ;
	}

	if( ( src = ml_str_alloca( line->ctl_info.bidi->size)) == NULL)
	{
		return  0 ;
	}

	ml_str_copy( src , line->chars , line->ctl_info.bidi->size) ;

	for( count = 0 ; count < line->ctl_info.bidi->size ; count ++)
	{
		copy_char_with_mirror_check(
				line->chars + line->ctl_info.bidi->visual_order[count] ,
				src + count , line->ctl_info.bidi->visual_order ,
				line->ctl_info.bidi->size , count) ;
	}

	ml_str_final( src , line->ctl_info.bidi->size) ;

	return  1 ;
}

/* The caller should check ml_line_is_using_bidi() in advance. */
int
ml_line_bidi_logical(
	ml_line_t *  line
	)
{
	int  count ;
	ml_char_t *  src ;
	
	if( line->ctl_info.bidi->size == 0 ||
	    ! HAS_RTL( line->ctl_info.bidi))
	{
	#ifdef  __DEBUG
		kik_debug_printf( KIK_DEBUG_TAG " Not need to logicalize.\n") ;
	#endif

		return  0 ;
	}

	if( ( src = ml_str_alloca( line->ctl_info.bidi->size)) == NULL)
	{
		return  0 ;
	}
	
	ml_str_copy( src , line->chars , line->ctl_info.bidi->size) ;

	for( count = 0 ; count < line->ctl_info.bidi->size ; count ++)
	{
		copy_char_with_mirror_check( line->chars + count ,
			src + line->ctl_info.bidi->visual_order[count] ,
			line->ctl_info.bidi->visual_order ,
			line->ctl_info.bidi->size , count) ;
	}

	ml_str_final( src , line->ctl_info.bidi->size) ;

	/*
	 * !! Notice !!
	 * is_modified is as it is , which should not be touched here.
	 */

	return  1 ;
}

/* The caller should check ml_line_is_using_bidi() in advance. */
int
ml_line_bidi_convert_logical_char_index_to_visual(
	ml_line_t *  line ,
	int  char_index ,
	int *  ltr_rtl_meet_pos
	)
{
	if( ((u_int)char_index) < line->ctl_info.bidi->size && /* same as 0 <= char_index < size */
	    HAS_RTL( line->ctl_info.bidi))
	{
		int  count ;

		if( ! BASE_IS_RTL( line->ctl_info.bidi) && char_index >= 1)
		{
			for( count = char_index - 2 ; count >= -1 ; count --)
			{
				/*
				 * visual order -> 1 2 4 3 5
				 *                   ^ ^   ^- char index
				 *                   | |
				 * cursor position --+ +-- meet position
				 *
				 * visual order -> 1 2*5*4 3 6
				 *                 ^ ^ ^     ^- char index
				 *                   | |
				 * cursor position --+ +-- meet position
				 *
				 * visual order -> 1 2 3 6 5 4 7
				 *                   ^ ^ ^     ^- char index
				 *                     | |
				 *   cursor position --+ +-- meet position
				 */
			#if  0
				kik_debug_printf(
				" Normal pos %d - Current pos %d %d %d - Meet position %d\n" ,
					line->ctl_info.bidi->visual_order[char_index] ,
					count >= 0 ? line->ctl_info.bidi->visual_order[count] : 0 ,
				        line->ctl_info.bidi->visual_order[count + 1] ,
				        line->ctl_info.bidi->visual_order[count + 2] ,
					*ltr_rtl_meet_pos) ;
			#endif
				if( (count < 0 ||
				        line->ctl_info.bidi->visual_order[count] <
				        line->ctl_info.bidi->visual_order[count + 1]) &&
				    line->ctl_info.bidi->visual_order[count + 1] + 1 <
				        line->ctl_info.bidi->visual_order[count + 2])
				{
					/*
					 * If meet position is not changed, text isn't changed
					 * but cursor is moved. In this case cursor position should
					 * not be fixed to visual_order[count + 1].
					 */
					if( *ltr_rtl_meet_pos !=
						line->ctl_info.bidi->visual_order[count + 1] +
					        line->ctl_info.bidi->visual_order[count + 2])
					{
						*ltr_rtl_meet_pos =
						    line->ctl_info.bidi->visual_order[count + 1] +
						    line->ctl_info.bidi->visual_order[count + 2] ;
						
						if( line->ctl_info.bidi->visual_order[char_index] ==
							line->ctl_info.bidi->visual_order[count + 2] + 1)
						{
							return  line->ctl_info.bidi->visual_order[
									count + 1] ;
						}
					}

					break ;
				}
			}

			if( count == 0)
			{
				*ltr_rtl_meet_pos = 0 ;
			}
		}
		else if( BASE_IS_RTL( line->ctl_info.bidi) && char_index >= 1)
		{
			for( count = char_index - 2 ; count >= -1 ; count --)
			{
				/*
				 * visual order -> 6 5 4 2 3 1
				 *                   ^ ^ ^   ^- char index
				 *                     |
				 *                     +-- meet position & cursor position
				 * visual order -> 7 6 5 2 3*4*1
				 *                   ^ ^ ^     ^- char index
				 *                     |
				 *                     +-- meet position & cursor position
				 *
				 * visual order -> 7 6 4 5 3 2 1
				 *                 ^ ^ ^   ^- char index
				 *                   | |
				 * cursor position --+ +-- meet position
				 * visual order -> 7 6 3 4*5*2 1
				 *                 ^ ^ ^     ^- char index
				 *                   | |
				 * cursor position --+ +-- meet position
				 */
			#if  0
				kik_debug_printf(
				" Normal pos %d - Current pos %d %d %d - Meet position %d\n" ,
					line->ctl_info.bidi->visual_order[char_index] ,
					count >= 0 ? line->ctl_info.bidi->visual_order[count] : 0 ,
				        line->ctl_info.bidi->visual_order[count + 1] ,
				        line->ctl_info.bidi->visual_order[count + 2] ,
					*ltr_rtl_meet_pos) ;
			#endif

				if( (count < 0 ||
				        line->ctl_info.bidi->visual_order[count] >
				        line->ctl_info.bidi->visual_order[count + 1]) &&
				    line->ctl_info.bidi->visual_order[count + 1] >
				        line->ctl_info.bidi->visual_order[count + 2] + 1)
				{
					if( *ltr_rtl_meet_pos !=
						line->ctl_info.bidi->visual_order[count + 1] +
						line->ctl_info.bidi->visual_order[count + 2])
					{
						*ltr_rtl_meet_pos =
						    line->ctl_info.bidi->visual_order[count + 1] +
						    line->ctl_info.bidi->visual_order[count + 2] ;

						if( line->ctl_info.bidi->visual_order[char_index] + 1 ==
					            line->ctl_info.bidi->visual_order[count + 2])
						{
							return  line->ctl_info.bidi->visual_order[
									count + 1] ;
						}
					}

					break ;
				}
			}

			if( count == 0)
			{
				*ltr_rtl_meet_pos = 0 ;
			}
		}
		else
		{
			*ltr_rtl_meet_pos = 0 ;
		}
		
		return  line->ctl_info.bidi->visual_order[char_index] ;
	}
	else
	{
		*ltr_rtl_meet_pos = 0 ;
		
		return  char_index ;
	}
}


/*
 * This function is used only by a loader of this module (not used inside this module),
 * so it is assumed that ml_line_is_using_bidi() was already checked (otherwise this
 * module can be loaded unnecessarily).
 */
int
ml_line_bidi_convert_visual_char_index_to_logical(
	ml_line_t *  line ,
	int  char_index
	)
{
	u_int  count ;

	for( count = 0 ; count < line->ctl_info.bidi->size ; count++)
	{
		if( line->ctl_info.bidi->visual_order[count] == char_index)
		{
			return  count ;
		}
	}

	return  char_index ;
}

/*
 * This function is used only by a loader of this module (not used inside this module),
 * so it is assumed that ml_line_is_using_bidi() was already checked (otherwise this
 * module can be loaded unnecessarily).
 */
int
ml_line_bidi_is_rtl(
	ml_line_t *  line
	)
{
	return  BASE_IS_RTL( line->ctl_info.bidi) ;
}

int
ml_line_bidi_need_shape(
	ml_line_t *  line
	)
{
	return  HAS_RTL( line->ctl_info.bidi) ;
}

/*
 * This function is used only by a loader of this module (not used inside this module),
 * so it is assumed that ml_line_is_using_bidi() was already checked.
 */
int
ml_line_bidi_copy_logical_str(
	ml_line_t *  line ,
	ml_char_t *  dst ,
	int  beg ,		/* visual position */
	u_int  len
	)
{
	/*
	 * XXX
	 * adhoc implementation.
	 */

	int *  flags ;
	int  bidi_pos ;
	int  norm_pos ;
	int  dst_pos ;

	if( line->ctl_info.bidi->size == 0)
	{
		return  0 ;
	}

	if( ( flags = alloca( sizeof( int) * line->ctl_info.bidi->size)) == NULL)
	{
		return  0 ;
	}

	memset( flags , 0 , sizeof( int) * line->ctl_info.bidi->size) ;

	for( bidi_pos = beg ; bidi_pos < beg + len ; bidi_pos ++)
	{
		for( norm_pos = 0 ; norm_pos < line->ctl_info.bidi->size ; norm_pos ++)
		{
			if( line->ctl_info.bidi->visual_order[norm_pos] == bidi_pos)
			{
				flags[norm_pos] = 1 ;
			}
		}
	}

	for( dst_pos = norm_pos = 0 ; norm_pos < line->ctl_info.bidi->size ;
		norm_pos ++)
	{
		if( flags[norm_pos])
		{
			copy_char_with_mirror_check( &dst[dst_pos ++] ,
				line->chars + line->ctl_info.bidi->visual_order[norm_pos] ,
				line->ctl_info.bidi->visual_order ,
				line->ctl_info.bidi->size , norm_pos) ;
		}
	}

	return  1 ;
}
