/**    
    @file     tstrlist.c
    @date     2009/1/14
    @author   오재경 freefrug@falinux.com  FALinux.Co.,Ltd.
    @brief    Ver 0.9.1
              Delphi 형식의 스트링리스트 객체이다.
              tlist.c 에서 구현한 tlist 를 상속(?) 받아 사용한다.
    
    @modify   
    @todo     
    @bug     
    
    @remark   함수 테스트 필요 (테스트 완료표시 ">>")
    
              >>tstrlist_create     
              >>tstrlist_free       
              >>tstrlist_clear      
              >>tstrlist_add
              >>tstrlist_add_object        
              >>tstrlist_delete     
              >>tstrlist_get_string 
              >>tstrlist_get_object 
              >>tstrlist_get_tag    
                tstrlist_exchange   
              >>tstrlist_indexof    
                tstrlist_insert     
              >>tstrlist_first      
              >>tstrlist_last       
                tstrlist_move       
              >>tstrlist_put_string 
              >>tstrlist_put_tag
              >>tstrlist_remove     
                tstrlist_pack       
              >>tstrlist_getcount   
              >>tstrlist_sort    
              >>tstrlist_commatext   
              >>tstrlist_load_from_file
              >>tstrlist_save_to_file
    
    @warning 
*/
//  
//  저작권    에프에이리눅스(주)
//            외부공개 금지
//
//----------------------------------------------------------------------------

#define EMBEDDED_LINUX                                          // 이렇게 처리하지 않으면 EClipse에서 C 영역이 회색 바탕이 됨

#ifdef MS_WIN32
    #undef EMBEDDED_LINUX
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include <unistd.h>
#include <string.h>

#include <tlist.h>
#include <tstrlist.h>
                                                    
#ifdef EMBEDDED_LINUX

	#include <sys/resource.h>

#endif

char desc_tstrlist[] = "falinux tstrlist ver 0.9.0";

/// @{
/// @brief  local 함수리스트
static int  tstrlist_calcu_strcap( const char *str );                                   /// 할당할 문자열의 공간을 계산한다.
static tstritem *tstrlist_newitem( const char *str );                                   /// 새로운 문자열 아이템을 생성한다.
static tstritem *tstrlist_replace_itemstr( tstritem *sitem, const char *str );          /// 문자열 아이템의 문자열을 변경한다.
static int tstrlist_sortfunc( const void *ppa, const void *ppb );                       /// 문자열 정렬을 위해 크기를 비교한다.
/// @}



static void trim( char *str)
// 설명: 인수로 받은 문자열에서 앞과 뒤의 공백 문자를 제거한다.
{
    int     sz_str;
    int     ndx;

    sz_str = strlen( str);
    for( ndx = sz_str; 0 <= ndx; ndx--)                                         // 문자열 뒤의 화이트 문자를 제거
    {
        if ( ' ' != str[ndx])    break;                                         // 한글이 있을 경우를 생각해서 ' ' 문자를 직접 비교한다.
        str[ndx]   = '\0';
    }

    sz_str  = strlen( str);                                                     // 문자열 앞의 화이트 문자를 제거
    for( ndx = 0; ndx < sz_str; ndx++)
    {
        if ( ' ' != str[ndx])                                                   // 한글이 있을 경우를 생각해서 ' ' 문자를 직접 비교한다.
            break;
    }         
    
    memcpy( str, str+ndx, strlen( str));                                        // 회이트 문자가 아닌 부분을 포인터의 시작 위치로 이동
}

//------------------------------------------------------------------------------
/** @brief   문자열에서 앞뒤의 특수문자와 공백문자를 제거한다.
    @param   line 문자열 포인터
*///----------------------------------------------------------------------------
static void  __trimstr( char *line )
{
	int   idx, len;
	
	// 뒷쪽 공백문자와 특수문자 제거
	len = strlen(line);
	for( idx=len-1; idx>=0; idx-- )
	{
		if ( line[idx] > ' ' ) break;
	}
	line[idx+1] = '\0';
	
	// 앞쪽 공백문자와 특수문자 제거
	len = strlen(line);
	for( idx=0; idx<len; idx++ )
	{
		if ( line[idx] > ' ' ) break;
	}
	if ( (0 < idx) && (idx < len) )
	{
		memmove( line, line + idx, len-idx+1 );
	}
}
//------------------------------------------------------------------------------
/** @brief   할당할 문자열의 공간을 계산한다.
    @param   str 추가되는 문자열 포인터
    @return  할당할 문자열의 개수
    @remark  최소 MIN_STR_LEN 바이트이상이며 2의 배수로 증가한다.
*///----------------------------------------------------------------------------
static int  tstrlist_calcu_strcap( const char *str )
{
	int  slen, square; 

	slen   = strlen( str );
	square = 0;

	while( slen )
	{
		slen = slen >> 1;
		square ++;
	}
	
	slen = 1 << square;
	if ( slen < MIN_STR_LEN ) slen = MIN_STR_LEN;
	
	return slen;
}
//------------------------------------------------------------------------------
/** @brief   새로운 문자열 아이템을 생성한다.
    @param   str 추가되는 문자열 포인터
    @return  생성된 문자열 아이템 구조체 포인터
*///----------------------------------------------------------------------------
static tstritem *tstrlist_newitem( const char *str )
{
	tstritem *sitem;
	int       cap;
	
	cap = tstrlist_calcu_strcap( str );
	
	sitem = (tstritem *)malloc( sizeof(tstritem) + cap );
	
	sitem->fstrcap = cap;
	sitem->fstr    = (char *)sitem + sizeof(tstritem);
	sitem->ftag    = 0;
	
	strncpy( sitem->fstr, str, cap );
	
	return sitem;
}
//------------------------------------------------------------------------------
/** @brief   문자열 아이템의 문자열을 변경한다.
    @param   sitem 문자열 아이템 구조체의 포인터
    @param   str 변경되는 문자열 포인터
    @return  변경된 아이템구조체 포인터
*///----------------------------------------------------------------------------
static tstritem *tstrlist_replace_itemstr( tstritem *sitem, const char *str )
{
	int  cap;
	
	cap = sitem->fstrcap;
	
	if ( (int)strlen(str) > sitem->fstrcap )
	{
		tstritem  *newitem;
		
		newitem = tstrlist_newitem( str );
		newitem->ftag  = sitem->ftag;
		newitem->fdata = sitem->fdata;
		
		free( sitem );
		return newitem;
	}
	else
	{
		strncpy( sitem->fstr, str, cap );
		return sitem;
	}
}


//------------------------------------------------------------------------------
/** @brief   tstrlist 객체를 생성한다.
    @return  객체의 포인터
*///----------------------------------------------------------------------------
tstrlist* tstrlist_create     ( void )
{
   tstrlist *that;
   that = ( tstrlist *) malloc( sizeof( tstrlist ) );
   
   that->ftlst = tlist_create();
   
   return that;
}
//------------------------------------------------------------------------------
/** @brief   tstrlist 객체를 소멸시킨다.
    @param   *that 관리 객체의 포인터
*///----------------------------------------------------------------------------
void   tstrlist_free       ( tstrlist *that )
{
    tstrlist_clear( that );
    tlist_free( that->ftlst );
    free( that );
}
//------------------------------------------------------------------------------
/** @brief   모든 아이템을 제거한다.
    @param   that 관리 객체의 포인터
*///----------------------------------------------------------------------------
void   tstrlist_clear      ( tstrlist *that )
{
	int    loop;
	void  *ptr;
	tlist *tlst = that->ftlst;
	
	// 문자열 객체를 해제한다.
	for (loop=0; loop<tlst->fcount; loop++)
	{
		ptr = tlist_get( tlst, loop );
		if (ptr) free( ptr );
	}
	
	tlist_clear( tlst );
}
//------------------------------------------------------------------------------
/** @brief   문자열 아이템을 추가한다.
    @param   that 관리 객체의 포인터
    @param   str 새로이 추가되는 문자열 포인터
    @return  추가된 아이템의 인덱스
*///----------------------------------------------------------------------------
int    tstrlist_add        ( tstrlist *that, const char *str )
{
	tlist    *tlst = that->ftlst;
	tstritem *sitem;
	int       result;

	sitem  = tstrlist_newitem( str );	// 문자열 객체 생성
	result = tlist_add( tlst, sitem );

	return result;
}
//------------------------------------------------------------------------------
/** @brief   문자열과 사용자 포인터를 추가한다.
    @param   that 관리 객체의 포인터
    @param   str 새로이 추가되는 문자열 포인터
    @param   pdata 사용자 포인터
    @return  추가된 아이템의 인덱스
*///----------------------------------------------------------------------------
int    tstrlist_add_object ( tstrlist *that, const char *str, void *obj )
{
	tlist    *tlst = that->ftlst;
	tstritem *sitem;
	int       result;

	result = tstrlist_add( that, str );
	
	sitem  = (tstritem *)tlist_get( tlst, result );
	sitem->fdata = obj;

	return result;
}
//------------------------------------------------------------------------------
/** @brief   하나의 아이템을 제거한다.
    @param   that 관리 객체의 포인터
    @param   index 제거될 아이템의 인덱스
*///----------------------------------------------------------------------------
void   tstrlist_delete     ( tstrlist *that, int index )
{
	tlist    *tlst = that->ftlst;
	tstritem *sitem;
	
	sitem  = (tstritem *)tlist_get( tlst, index );
	if (sitem)
	{
		free( sitem );	
	}
	
	tlist_delete( tlst, index );	
}
//------------------------------------------------------------------------------
/** @brief   인덱스에 해당하는 문자열 포인터를 반환한다.
    @param   that 관리 객체의 포인터
    @param   index 아이템 인덱스
    @return  인덱스에 해당하는 문자열 포인터
*///----------------------------------------------------------------------------
char *tstrlist_get_string  ( tstrlist *that, int index )
{
    tlist    *tlst = that->ftlst;
    tstritem *sitem;
    
    sitem = (tstritem *)tlist_get( tlst, index );
    if (sitem)
    {
    	return sitem->fstr;
    }
    return NULL;
}
//------------------------------------------------------------------------------
/** @brief   인덱스에 해당하는 사용자 포인터를 반환한다.
    @param   that 관리 객체의 포인터
    @param   index 아이템 인덱스
    @return  인덱스에 해당하는 사용자 포인터
*///----------------------------------------------------------------------------
void *tstrlist_get_object  ( tstrlist *that, int index )
{
    tlist    *tlst = that->ftlst;
    tstritem *sitem;
    
    sitem = (tstritem *)tlist_get( tlst, index );
    if (sitem)
    {
    	return sitem->fdata;
    }
    return NULL;
}
//------------------------------------------------------------------------------
/** @brief   인덱스에 해당하는 정수형 태그변수를 반환한다.
    @param   that 관리 객체의 포인터
    @param   index 아이템 인덱스
    @return  인덱스에 해당하는 정수형 태그변수
*///----------------------------------------------------------------------------
int  tstrlist_get_tag  ( tstrlist *that, int index )
{
    tlist    *tlst = that->ftlst;
    tstritem *sitem;
    
    sitem = (tstritem *)tlist_get( tlst, index );
    if (sitem)
    {
    	return sitem->ftag;
    }

    return -1;
}
//------------------------------------------------------------------------------
/** @brief   2개의 아이템 위치를 교환한다.
    @param   that 관리 객체의 포인터
    @param   index1 교환할 인덱스1
    @param   index2 교환할 인덱스2
*///----------------------------------------------------------------------------
void   tstrlist_exchange   ( tstrlist *that, int index1, int index2 )
{
    tlist    *tlst = that->ftlst;
    
    tlist_exchange( tlst, index1, index2 );
}
//------------------------------------------------------------------------------
/** @brief   문자열이 동일한 인덱스를 구한다.
    @param   that 관리 객체의 포인터
    @param   str  비교 문자열 포인터
    @return  문자열이 해당되는 인덱스, 존재하지 않으면 -1
*///----------------------------------------------------------------------------
int    tstrlist_indexof    ( tstrlist *that, const char *str )
{
    tlist    *tlst = that->ftlst;
    tstritem *sitem;
	int       loop;
	
	for (loop=0; loop<tlst->fcount; loop++)
	{
		sitem = (tstritem *)tlist_get( tlst, loop );
		
		if ( 0 == strcmp( sitem->fstr, str ) )
		{
			return loop;	
		}
	}

	return -1;
}
//------------------------------------------------------------------------------
/** @brief   아이템을 특정 위치에 추가한다.
    @param   that 관리 객체의 포인터
    @param   index 추가할 아이템가 들어갈 인덱스
    @param   str  추가할 문자열 포인터
*///----------------------------------------------------------------------------
void   tstrlist_insert     ( tstrlist *that, int index, char *str )
{
    tlist    *tlst = that->ftlst;
    tstritem *sitem;
	
	sitem = tstrlist_newitem( str );	// 문자열 객체 생성
	tlist_insert( tlst, index, sitem );
}
//------------------------------------------------------------------------------
/** @brief   첫번째 문자열 포인터를 돌려준다.
    @param   that 관리 객체의 포인터
    @return  첫번째 문자열 포인터
*///----------------------------------------------------------------------------
char  *tstrlist_first      ( tstrlist *that )
{
    return tstrlist_get_string( that, 0 );
}
//------------------------------------------------------------------------------
/** @brief   마지막 문자열 포인터를 구한다.
    @param   that 관리 객체의 포인터
    @return  마지막 문자열 포인터
*///----------------------------------------------------------------------------
char  *tstrlist_last       ( tstrlist *that )
{
    tlist    *tlst = that->ftlst;

    return tstrlist_get_string( that, tlst->fcount-1 );
}
//------------------------------------------------------------------------------
/** @brief   특정 아이템의 위치(인덱스)를 변경한다.
    @param   that 관리 객체의 포인터
    @param   curindex 변경할 아이템의 인덱스
    @param   newindex 변경될 아이템의 인덱스
*///----------------------------------------------------------------------------
void   tstrlist_move       ( tstrlist *that, int curindex, int newindex )
{
	tlist    *tlst = that->ftlst;
	
	tlist_move( tlst, curindex, newindex );
}
//------------------------------------------------------------------------------
/** @brief   특정아이템의 문자열을 변경한다.
    @param   that  관리 객체의 포인터
    @param   index 문자열이 변경될 인덱스
    @param   str  새로 변경될 문자열
*///----------------------------------------------------------------------------
void   tstrlist_put_string  ( tstrlist *that, int index, char *str )
{
	tlist    *tlst = that->ftlst;
	tstritem *sitem;

	sitem = tlist_get( tlst, index );
	if ( sitem )
	{
		sitem = tstrlist_replace_itemstr( sitem, str );
		
		tlist_put( tlst, index, sitem );
	}
}
//------------------------------------------------------------------------------
/** @brief   특정아이템의 사용자 메모리 변수를 변경한다.
    @param   that  관리 객체의 포인터
    @param   index 변경될 아이템 인덱스
    @param   obj   새로 변경될 사용자 포인터
*///----------------------------------------------------------------------------
void   tstrlist_put_object ( tstrlist *that, int index, void *obj )
{
	tlist    *tlst = that->ftlst;
	tstritem *sitem;

	sitem = tlist_get( tlst, index );
	if ( sitem )
	{
		sitem->fdata = obj;
	}
}
//------------------------------------------------------------------------------
/** @brief   특정아이템의 태그변수를 변경한다.
    @param   that  관리 객체의 포인터
    @param   index 태그가 변경될 인덱스
    @param   tag  새로 변경될 정수형 태그변수
*///----------------------------------------------------------------------------
void   tstrlist_put_tag  ( tstrlist *that, int index, int tag )
{
	tlist    *tlst = that->ftlst;
	tstritem *sitem;

	sitem = tlist_get( tlst, index );
	if ( sitem )
	{
		sitem->ftag = tag;
	}
}
//------------------------------------------------------------------------------
/** @brief   인자로 전해준 동일한 문자열을 찾아 삭제한다.
    @param   that 관리 객체의 포인터
    @param   item 삭제할 아이템의 포인터
    @return  삭제된 아이템의 과거 인덱스
*///----------------------------------------------------------------------------
int    tstrlist_remove     ( tstrlist *that, char *str )
{
    int result;
    
    result = tstrlist_indexof( that, str );
    if( result >= 0 ) tstrlist_delete( that, result );
    return result;
}
//------------------------------------------------------------------------------
/** @brief   메모리 관리를 위해 사용하지 않는 메모리를 반환한다.
    @param   that 관리 객체의 포인터
    @remark  실제 메모리를 반환하지 않는다.
    @todo    메모리를 반환하도록 재작성되어야 한다.
*///----------------------------------------------------------------------------
void   tstrlist_pack       ( tstrlist *that )
{
	tlist    *tlst = that->ftlst;

	tlist_pack( tlst );
}
//------------------------------------------------------------------------------
/** @brief   아이템의 개수를 얻는다.
    @param   that 관리 객체의 포인터
    @return  아이템의 개수
*///----------------------------------------------------------------------------
int    tstrlist_getcount   ( tstrlist *that )
{
    tlist    *tlst = that->ftlst;
    
    return tlst->fcount;
}

//------------------------------------------------------------------------------
/** @brief   문자열 정렬을 위해 크기를 비교한다.
    @param   astr 비교 포인터
    @param   bstr 비교 포인터
    @return  문자열 비교값
*///----------------------------------------------------------------------------
static int tstrlist_sortfunc( const void *ppa, const void *ppb )
{
	tstritem **aitem, **bitem;
	
	aitem = (tstritem **)ppa;
	bitem = (tstritem **)ppb;

	return strcmp( (*aitem)->fstr, (*bitem)->fstr );
}
//------------------------------------------------------------------------------
/** @brief   아이템을 사용자 비교함수를 통해 정렬한다.
    @param   that 관리 객체의 포인터
    @param   tstrlistsortcomparefunc 사용자비교 콜백함수이며, int (*func)(const void *, const void *) 형태이다.
*///----------------------------------------------------------------------------
void   tstrlist_sort       ( tstrlist *that )
{
    tlist    *tlst = that->ftlst;
    
    if ( 0 >= tlst->fcount ) return;
    
	tlist_sort( tlst, tstrlist_sortfunc );
}
//------------------------------------------------------------------------------
/** @brief   인자로 전해준 문자열을 분리하여 아이템으로 추가한다.
    @param   that 관리 객체의 포인터
    @param   str 분리할 문자열
    @return  객체가 관리하는 아이템 개수
    @todo    더블쿼테이션(") 으로 싸여진 문자열 분리가 필요하다.
*///----------------------------------------------------------------------------
int    tstrlist_commatext     ( tstrlist *that, char *str )
{    
    #define COMMA_BUFF_MAX      4096   
	tlist  *tlst = that->ftlst;
	int     ndx_sour;
	int     ndx_buff;
	char    ch_division = '\0';
    char    buff[COMMA_BUFF_MAX+1];

    void add_buff_to_list( void)
    {
        trim( buff);	
        tstrlist_add( that, buff);
    }

    ndx_buff = 0;                                                               // 버퍼에 문자를 넣을 위치를 0으로 초기화
    for ( ndx_sour = 0; ndx_sour < strlen( str); ndx_sour++)
    {
        if ( '\0' == ch_division)
        {
            switch( str[ndx_sour])
            {
            case '\''   :
            case '\"'   :
                    if ( 0 < ndx_buff)                                          // 이전에 버퍼로 등록된 문자열이 있다면 리스트에 추가
                    {
                        buff[ndx_buff]  = '\0';
                        add_buff_to_list();
                    }
                    ch_division = str[ndx_sour];
                    ndx_buff    = 0;                                            // 버퍼에 새로운 문자열을 담을 수 있도록 버퍼의 위치를 0 으로 초기화
                    break;
            case ' '    :
            case ','    :
            case '\n'   :
            case '\r'   :
                    if ( 0 < ndx_buff)                                          // 이전에 버퍼로 등록된 문자열이 있다면 리스트에 추가
                    {
                        buff[ndx_buff]  = '\0';
                        add_buff_to_list();
                        ndx_buff        = 0;
                    }
                    break;
            default     :
                    if ( ndx_buff < COMMA_BUFF_MAX)
                    {
                        buff[ndx_buff++] = str[ndx_sour];
                    }
                    else
                    {
                        printf( "tstrlist error: buffer is small in comma text function.");
                    }
                    break;
            }
        }
        else
        {
            if ( ch_division == str[ndx_sour])                                  // 이전 인용부호의 끝이라면
            {
                ch_division     = '\0';
                buff[ndx_buff]  = '\0';
                add_buff_to_list();
                ndx_buff        = 0;
            }
            else
            {
                if ( ndx_buff < COMMA_BUFF_MAX)
                {
                    buff[ndx_buff++] = str[ndx_sour];
                }
                else
                {
                    printf( "tstrlist error: buffer is small in comma text function.");
                }
            }
        }
    }                

    if ( 0 < ndx_buff)                                                          // 이전에 버퍼로 등록된 문자열이 있다면 리스트에 추가
    {
        buff[ndx_buff]  = '\0';
        add_buff_to_list();
    }
	return tlst->fcount; 
}
//------------------------------------------------------------------------------
/** @brief   텍스트파일에서 라인별로 아이템에 등록한다.
    @param   that 관리 객체의 포인터
    @param   fname 파일이름
    @return  객체가 관리하는 아이템 개수
*///----------------------------------------------------------------------------
int    tstrlist_load_from_file ( tstrlist *that, char *fname )
{
	tlist    *tlst = that->ftlst;
	char      line[2048];
	FILE     *pfile;

	pfile = fopen( fname, "r" );
	if ( pfile )
	{
		while( fgets( line, sizeof(line), pfile ) )
		{
			__trimstr( line );
			if ( 0 < strlen(line) )
			{
				tstrlist_add( that, line );
			}
		}
		
		fclose( pfile );
	}
	
	return tlst->fcount; 
}
//------------------------------------------------------------------------------
/** @brief   아이템들을 라인별로 파일로 저장한다.
    @param   that 관리 객체의 포인터
    @param   fname 파일이름
    @return  파일에 저장한 라인수
*///----------------------------------------------------------------------------
int    tstrlist_save_to_file ( tstrlist *that, char *fname )
{
	tlist    *tlst = that->ftlst;
	FILE     *pfile;
	int       loop, wrline;

	pfile  = fopen( fname, "w+" );
	wrline = 0;
	if ( pfile )
	{
		for (loop=0; loop<tlst->fcount; loop++)
		{
			if ( 0 < fprintf( pfile, "%s\n", tstrlist_get_string( that, loop ) ) )
				wrline	++;
		}
		
		fclose( pfile );
	}
	
	return wrline;
}