/***************************************************************************
 *  File: string.c                                                         *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 *                                                                         *
 *  This code was freely distributed with the The Isles 1.1 source code,   *
 *  and has been used here for OLC - OLC would not be what it is without   *
 *  all the previous coders who released their source code.                *
 *                                                                         *
 ***************************************************************************/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "tables.h"
#include "olc.h"

char *string_linedel ( char *, int );
char *string_lineadd ( char *, char *, int );
char *numlineas ( char * );

/*****************************************************************************
 Name:		string_edit
 Purpose:	Clears string and puts player into editing mode.
 Called by:	none
 ****************************************************************************/
void string_edit ( CHAR_DATA * ch, char **pString )
{
    chsend ( "-========- Entering EDIT Mode -=========-\n\r", ch );
    chsend ( "    Type .h on a new line for help\n\r", ch );
    chsend ( " Terminate with a ~ or @ on a blank line.\n\r", ch );
    chsend ( "-=======================================-\n\r", ch );

    if ( *pString == NULL )
    {
        *pString = str_dup ( "" );
    }
    else
    {
        **pString = '\0';
    }

    ch->desc->pString = pString;

    return;
}

/*****************************************************************************
 Name:		string_append
 Purpose:	Puts player into append mode for given string.
 Called by:	(many)olc_act.c
 ****************************************************************************/
void string_append ( CHAR_DATA * ch, char **pString )
{
    chsend ( "-=======- Entering APPEND Mode -========-\n\r", ch );
    chsend ( "    Type .h on a new line for help\n\r", ch );
    chsend ( " Terminate with a ~ or @ on a blank line.\n\r", ch );
    chsend ( "-=======================================-\n\r", ch );

    if ( *pString == NULL )
    {
        *pString = str_dup ( "" );
    }
    chsend ( numlineas ( *pString ), ch );

/* numlineas entrega el string con \n\r */
/*  if ( *(*pString + strlen( *pString ) - 1) != '\r' )
	chsend( "\n\r", ch ); */

    ch->desc->pString = pString;

    return;
}

/*****************************************************************************
 Name:		string_replace
 Purpose:	Substitutes one string for another.
 Called by:	string_add(string.c) (aedit_builder)olc_act.c.
 ****************************************************************************/
char *string_replace ( char *orig, char *old, char *pnew )
{
    char xbuf[MAX_STRING_LENGTH];
    int i;

    xbuf[0] = '\0';
    strcpy ( xbuf, orig );
    if ( strstr ( orig, old ) != NULL )
    {
        i = strlen ( orig ) - strlen ( strstr ( orig, old ) );
        xbuf[i] = '\0';
        strcat ( xbuf, pnew );
        strcat ( xbuf, &orig[i + strlen ( old )] );
        free_string ( orig );
    }

    return str_dup ( xbuf );
}

/*****************************************************************************
 Name:		string_add
 Purpose:	Interpreter for string editing.
 Called by:	game_loop_xxxx(comm.c).
 ****************************************************************************/
void string_add ( CHAR_DATA * ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];

    /*
     * Thanks to James Seng
     */
    smash_tilde ( argument );

    if ( *argument == '.' )
    {
        char arg1[MAX_INPUT_LENGTH];
        char arg2[MAX_INPUT_LENGTH];
        char arg3[MAX_INPUT_LENGTH];
        char tmparg3[MAX_INPUT_LENGTH];

        argument = one_argument ( argument, arg1 );
        argument = first_arg ( argument, arg2, FALSE );
        strcpy ( tmparg3, argument );
        argument = first_arg ( argument, arg3, FALSE );

        if ( !str_cmp ( arg1, ".c" ) )
        {
            chsend ( "String cleared.\n\r", ch );
            free_string ( *ch->desc->pString );
            *ch->desc->pString = str_dup ( "" );
            return;
        }

        if ( !str_cmp ( arg1, ".s" ) )
        {
            chsend ( "String so far:\n\r", ch );
            chsend ( numlineas ( *ch->desc->pString ), ch );
            return;
        }

        if ( !str_cmp ( arg1, ".r" ) )
        {
            if ( arg2[0] == '\0' )
            {
                chsend ( "usage:  .r \"old string\" \"new string\"\n\r", ch );
                return;
            }

            *ch->desc->pString =
                string_replace ( *ch->desc->pString, arg2, arg3 );
            sprintf ( buf, "'%s' replaced with '%s'.\n\r", arg2, arg3 );
            chsend ( buf, ch );
            return;
        }

        if ( !str_cmp ( arg1, ".f" ) )
        {
            *ch->desc->pString = format_string ( *ch->desc->pString );
            chsend ( "String formatted.\n\r", ch );
            return;
        }

        if ( !str_cmp ( arg1, ".ld" ) )
        {
            *ch->desc->pString =
                string_linedel ( *ch->desc->pString, atoi ( arg2 ) );
            chsend ( "Linea borrada.\n\r", ch );
            return;
        }

        if ( !str_cmp ( arg1, ".li" ) )
        {
            *ch->desc->pString =
                string_lineadd ( *ch->desc->pString, tmparg3, atoi ( arg2 ) );
            chsend ( "Linea insertada.\n\r", ch );
            return;
        }

        if ( !str_cmp ( arg1, ".lr" ) )
        {
            *ch->desc->pString =
                string_linedel ( *ch->desc->pString, atoi ( arg2 ) );
            *ch->desc->pString =
                string_lineadd ( *ch->desc->pString, tmparg3, atoi ( arg2 ) );
            chsend ( "Linea reemplazada.\n\r", ch );
            return;
        }

        if ( !str_cmp ( arg1, ".h" ) )
        {
            chsend ( "Sedit help (commands on blank line):   \n\r", ch );
            chsend ( ".r 'old' 'new'   - replace a substring \n\r", ch );
            chsend ( "                   (requires '', \"\") \n\r", ch );
            chsend ( ".h               - get help (this info)\n\r", ch );
            chsend ( ".s               - show string so far  \n\r", ch );
            chsend ( ".f               - (word wrap) string  \n\r", ch );
            chsend ( ".c               - clear string so far \n\r", ch );
            chsend ( ".ld <num>        - borra linea numero <num>\n\r", ch );
            chsend ( ".li <num> <str>  - anade <str> en linea <num>\n\r",
                     ch );
            chsend ( ".lr <num> <str>  - reemplaza linea <num> por <str>\n\r",
                     ch );
            chsend ( "@                - end string          \n\r", ch );
            return;
        }

        chsend ( "SEdit:  Invalid dot command.\n\r", ch );
        return;
    }

    if ( *argument == '~' || *argument == '@' )
    {
        if ( ch->desc->editor == ED_MPCODE )    /* para los mobprogs */
        {
            MOB_INDEX_DATA *mob;
            int hash;
            MPROG_LIST *mpl;
            MPROG_CODE *mpc;

            EDIT_MPCODE ( ch, mpc );

            if ( mpc != NULL )
                for ( hash = 0; hash < MAX_KEY_HASH; hash++ )
                    for ( mob = mob_index_hash[hash]; mob; mob = mob->next )
                        for ( mpl = mob->mprogs; mpl; mpl = mpl->next )
                            if ( mpl->vnum == mpc->vnum )
                            {
                                sprintf ( buf, "Arreglando mob %ld.\n\r",
                                          mob->vnum );
                                chsend ( buf, ch );
                                mpl->code = mpc->code;
                            }
        }

        ch->desc->pString = NULL;
        return;
    }

    strcpy ( buf, *ch->desc->pString );

    /*
     * Truncate strings to MAX_STRING_LENGTH.
     * --------------------------------------
     */
    if ( strlen ( buf ) + strlen ( argument ) >= ( MAX_STRING_LENGTH - 4 ) )
    {
        chsend ( "String too long, last line skipped.\n\r", ch );

        /* Force character out of editing mode. */
        ch->desc->pString = NULL;
        return;
    }

    /*
     * Ensure no tilde's inside string.
     * --------------------------------
     */
    smash_tilde ( argument );

    strcat ( buf, argument );
    strcat ( buf, "\n\r" );
    free_string ( *ch->desc->pString );
    *ch->desc->pString = str_dup ( buf );
    return;
}

/*
 * Thanks to Kalgen for the new procedure (no more bug!)
 * Original wordwrap() written by Surreality.
 */
/*****************************************************************************
 Name:		format_string
 Purpose:	Special string formating and word-wrapping.
 Called by:	string_add(string.c) (many)olc_act.c
 ****************************************************************************/
char *format_string ( char *oldstring /*, bool fSpace */  )
{
    char xbuf[MAX_STRING_LENGTH];
    char xbuf2[MAX_STRING_LENGTH];
    char *rdesc;
    int i = 0;
    bool cap = TRUE;

    xbuf[0] = xbuf2[0] = 0;

    i = 0;

    for ( rdesc = oldstring; *rdesc; rdesc++ )
    {
        if ( *rdesc == '\n' )
        {
            if ( xbuf[i - 1] != ' ' )
            {
                xbuf[i] = ' ';
                i++;
            }
        }
        else if ( *rdesc == '\r' );
        else if ( *rdesc == ' ' )
        {
            if ( xbuf[i - 1] != ' ' )
            {
                xbuf[i] = ' ';
                i++;
            }
        }
        else if ( *rdesc == ')' )
        {
            if ( xbuf[i - 1] == ' ' && xbuf[i - 2] == ' ' &&
                 ( xbuf[i - 3] == '.' || xbuf[i - 3] == '?' ||
                   xbuf[i - 3] == '!' ) )
            {
                xbuf[i - 2] = *rdesc;
                xbuf[i - 1] = ' ';
                xbuf[i] = ' ';
                i++;
            }
            else
            {
                xbuf[i] = *rdesc;
                i++;
            }
        }
        else if ( *rdesc == '.' || *rdesc == '?' || *rdesc == '!' )
        {
            if ( xbuf[i - 1] == ' ' && xbuf[i - 2] == ' ' &&
                 ( xbuf[i - 3] == '.' || xbuf[i - 3] == '?' ||
                   xbuf[i - 3] == '!' ) )
            {
                xbuf[i - 2] = *rdesc;
                if ( *( rdesc + 1 ) != '\"' )
                {
                    xbuf[i - 1] = ' ';
                    xbuf[i] = ' ';
                    i++;
                }
                else
                {
                    xbuf[i - 1] = '\"';
                    xbuf[i] = ' ';
                    xbuf[i + 1] = ' ';
                    i += 2;
                    rdesc++;
                }
            }
            else
            {
                xbuf[i] = *rdesc;
                if ( *( rdesc + 1 ) != '\"' )
                {
                    xbuf[i + 1] = ' ';
                    xbuf[i + 2] = ' ';
                    i += 3;
                }
                else
                {
                    xbuf[i + 1] = '\"';
                    xbuf[i + 2] = ' ';
                    xbuf[i + 3] = ' ';
                    i += 4;
                    rdesc++;
                }
            }
            cap = TRUE;
        }
        else
        {
            xbuf[i] = *rdesc;
            if ( cap )
            {
                cap = FALSE;
                xbuf[i] = UPPER ( xbuf[i] );
            }
            i++;
        }
    }
    xbuf[i] = 0;
    strcpy ( xbuf2, xbuf );

    rdesc = xbuf2;

    xbuf[0] = 0;

    for ( ;; )
    {
        for ( i = 0; i < 77; i++ )
        {
            if ( !*( rdesc + i ) )
                break;
        }
        if ( i < 77 )
        {
            break;
        }
        for ( i = ( xbuf[0] ? 76 : 73 ); i; i-- )
        {
            if ( *( rdesc + i ) == ' ' )
                break;
        }
        if ( i )
        {
            *( rdesc + i ) = 0;
            strcat ( xbuf, rdesc );
            strcat ( xbuf, "\n\r" );
            rdesc += i + 1;
            while ( *rdesc == ' ' )
                rdesc++;
        }
        else
        {
            bug ( "No spaces", 0 );
            *( rdesc + 75 ) = 0;
            strcat ( xbuf, rdesc );
            strcat ( xbuf, "-\n\r" );
            rdesc += 76;
        }
    }
    while ( *( rdesc + i ) &&
            ( *( rdesc + i ) == ' ' || *( rdesc + i ) == '\n' ||
              *( rdesc + i ) == '\r' ) )
        i--;
    *( rdesc + i + 1 ) = 0;
    strcat ( xbuf, rdesc );
    if ( xbuf[strlen ( xbuf ) - 2] != '\n' )
        strcat ( xbuf, "\n\r" );

    free_string ( oldstring );
    return ( str_dup ( xbuf ) );
}

/*
 * Used above in string_add.  Because this function does not
 * modify case if fCase is FALSE and because it understands
 * parenthesis, it would probably make a nice replacement
 * for one_argument.
 */
/*****************************************************************************
 Name:		first_arg
 Purpose:	Pick off one argument from a string and return the rest.
 		Understands quates, parenthesis (barring ) ('s) and
 		percentages.
 Called by:	string_add(string.c)
 ****************************************************************************/
char *first_arg ( char *argument, char *arg_first, bool fCase )
{
    char cEnd;

    while ( *argument == ' ' )
        argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"' || *argument == '%' ||
         *argument == '(' )
    {
        if ( *argument == '(' )
        {
            cEnd = ')';
            argument++;
        }
        else
            cEnd = *argument++;
    }

    while ( *argument != '\0' )
    {
        if ( *argument == cEnd )
        {
            argument++;
            break;
        }
        if ( fCase )
            *arg_first = LOWER ( *argument );
        else
            *arg_first = *argument;
        arg_first++;
        argument++;
    }
    *arg_first = '\0';

    while ( *argument == ' ' )
        argument++;

    return argument;
}

/*
 * Used in olc_act.c for aedit_builders.
 */
char *string_unpad ( char *argument )
{
    char buf[MAX_STRING_LENGTH];
    char *s;

    s = argument;

    while ( *s == ' ' )
        s++;

    strcpy ( buf, s );
    s = buf;

    if ( *s != '\0' )
    {
        while ( *s != '\0' )
            s++;
        s--;

        while ( *s == ' ' )
            s--;
        s++;
        *s = '\0';
    }

    free_string ( argument );
    return str_dup ( buf );
}

/*
 * Same as capitalize but changes the pointer's data.
 * Used in olc_act.c in aedit_builder.
 */
char *string_proper ( char *argument )
{
    char *s;

    s = argument;

    while ( *s != '\0' )
    {
        if ( *s != ' ' )
        {
            *s = UPPER ( *s );
            while ( *s != ' ' && *s != '\0' )
                s++;
        }
        else
        {
            s++;
        }
    }

    return argument;
}

char *string_linedel ( char *string, int line )
{
    char *strtmp = string;
    char buf[MAX_STRING_LENGTH];
    int cnt = 1, tmp = 0;

    buf[0] = '\0';

    for ( ; *strtmp != '\0'; strtmp++ )
    {
        if ( cnt != line )
            buf[tmp++] = *strtmp;

        if ( *strtmp == '\n' )
        {
            if ( *( strtmp + 1 ) == '\r' )
            {
                if ( cnt != line )
                    buf[tmp++] = *( ++strtmp );
                else
                    ++strtmp;
            }

            cnt++;
        }
    }

    buf[tmp] = '\0';

    free_string ( string );
    return str_dup ( buf );
}

char *string_lineadd ( char *string, char *newstr, int line )
{
    char *strtmp = string;
    int cnt = 1, tmp = 0;
    bool done = FALSE;
    char buf[MAX_STRING_LENGTH];

    buf[0] = '\0';

    for ( ; *strtmp != '\0' || ( !done && cnt == line ); strtmp++ )
    {
        if ( cnt == line && !done )
        {
            strcat ( buf, newstr );
            strcat ( buf, "\n\r" );
            tmp += strlen ( newstr ) + 2;
            cnt++;
            done = TRUE;
        }

        buf[tmp++] = *strtmp;

        if ( done && *strtmp == '\0' )
            break;

        if ( *strtmp == '\n' )
        {
            if ( *( strtmp + 1 ) == '\r' )
                buf[tmp++] = *( ++strtmp );

            cnt++;
        }

        buf[tmp] = '\0';
    }

    free_string ( string );
    return str_dup ( buf );
}

/* buf queda con la linea sin \n\r */
char *getline ( char *str, char *buf );

char *getline ( char *str, char *buf )
{
    int tmp = 0;
    bool found = FALSE;

    while ( *str )
    {
        if ( *str == '\n' )
        {
            found = TRUE;
            break;
        }

        buf[tmp++] = *( str++ );
    }

    if ( found )
    {
        if ( *( str + 1 ) == '\r' )
            str += 2;
        else
            str += 1;
    }                           /* para que quedemos en el inicio de la prox linea */

    buf[tmp] = '\0';

    return str;
}

char *numlineas ( char *string )
{
    int cnt = 1;
    static char buf[MAX_STRING_LENGTH * 2];
    char buf2[MAX_STRING_LENGTH], tmpb[MAX_STRING_LENGTH];

    buf[0] = '\0';

    while ( *string )
    {
        string = getline ( string, tmpb );
        sprintf ( buf2, "%2d. %s\n\r", cnt++, tmpb );
        strcat ( buf, buf2 );
    }

    return buf;
}

/* Converts a ~ into a colour code for ~ */
void tilde_to_color ( char *buffer, char *str )
{
    int i = 0;
    char buf[MSL];

    memset ( buf, 0, MSL );

    if ( !str || *str == '\0' )
        return;

    while ( *str != '\0' )
    {
        if ( *str == '~' )
        {
            buf[i] = ANSI_KEY;
            i++;
            buf[i] = '-';
            i++;
            str++;
        }
        else
        {
            buf[i] = *str;
            i++;
            str++;
        }
    }
    strcpy ( buffer, buf );
    return;
}

/* Removes all colour coding from a string */
void smash_colour ( char *buffer, char *str )
{
    int i = 0;
    char buf[MSL];

    memset ( buf, 0, MSL );

    if ( !str || *str == '\0' )
        return;

    while ( *str != '\0' )
    {
        if ( *str == ANSI_KEY )
        {
            str++;
            str++;
        }
        else if ( *str == ANSI_CUSTOM )
        {
            str++;
            while ( *str != ANSI_END )
                str++;
            str++;
        }
        else
        {
            buf[i] = *str;
            i++;
            str++;
        }
    }
    strcpy ( buffer, buf );
    return;
}

/* Converts a colour code for a ~ into a ~ */
void color_to_tilde ( char *buffer, char *str )
{
    int i = 0;
    char buf[MSL];

    memset ( buf, 0, MSL );

    if ( !str || *str == '\0' )
        return;

    while ( *str != '\0' )
    {
        if ( *str == ANSI_KEY )
        {
            str++;
            if ( *str == '-' )
            {
                buf[i] = '~';
                i++;
                str++;
            }
            else
                str++;
        }
        else
        {
            buf[i] = *str;
            i++;
            str++;
        }
    }
    strcpy ( buffer, buf );
    return;
}

/* formats a string into a length and alignment
    *fill can be used as NULL, length '0' returns 77 */
void stringf ( char *buffer, int length, int align, char *fill, char *string )
{
    char buf[MSL];
    char buf2[MSL];
    char *new_string;
    char *count_string;
    char temp;
    int count = 0, nCount = 0;
    int pos = 0;

    new_string = buf;
    count_string = buf2;
    strcpy ( buf2, string );

    if ( !fill )
        fill = " ";

    if ( length == 0 )
        length = 77;

    while ( *count_string && nCount != length )
    {
        temp = *count_string++;

        if ( temp == ANSI_KEY )
        {
            temp = *count_string++;
            if ( temp == ANSI_KEY )
                nCount++;
            continue;
        }
        else if ( temp == ANSI_CUSTOM )
        {
            temp = *count_string++;
            while ( temp != ANSI_END )
                temp = *count_string++;
            continue;
        }
        nCount++;
    }

    if ( align == ALIGN_RIGHT )
    {
        count = ( length - ++nCount );
        while ( nCount++ <= length )
        {
            buf[pos++] = *fill;
        }
    }

    if ( align == ALIGN_CENTER )
    {
        nCount = ( length - nCount ) / 2;
        count = nCount;
        while ( nCount-- > 0 )
        {
            buf[pos++] = *fill;
        }
    }

    while ( *string && count != length )
    {
        temp = *string++;
        buf[pos++] = temp;

        if ( temp == ANSI_KEY )
        {
            temp = buf[pos] = *string++;

            if ( temp == ANSI_KEY )
                count++;

            pos++;
            continue;
        }
        else if ( temp == ANSI_CUSTOM )
        {
            temp = buf[pos] = *string++;

            while ( temp != ANSI_END )
            {
                pos++;
                temp = buf[pos] = *string++;
            }
            pos++;
            continue;
        }
        count++;
    }

    while ( count++ < length )
        buf[pos++] = *fill;

    buf[pos] = '\0';
    strcpy ( buffer, new_string );
    return;
}

/* Counts the length of a string excluding colour codes */
int strlen_color ( const char *string )
{
    int i, count = 0;
    char arg[MSL];

    if ( !string )
        return count;

    strcpy ( arg, string );

    for ( i = 0; i <= MIL; i++ )
    {

        if ( arg[i] == '\0' )
        {
            break;
        }

        else if ( string[i] != ANSI_KEY && string[i] != ANSI_CUSTOM )
        {
            count++;
            continue;
        }

        else if ( arg[i] == ANSI_KEY && arg[i + 1] == ANSI_KEY )
        {
            i++;
            count++;
            continue;
        }

        else if ( arg[i] == ANSI_KEY && arg[i + 1] != ANSI_KEY &&
                  arg[i + 1] != '\0' )
        {
            i += 1;
            continue;
        }
        else if ( arg[i] == ANSI_CUSTOM )
        {
            i += 1;
            while ( arg[i] != ANSI_END )
                i += 1;
            i += 1;
            continue;
        }
        else if ( arg[i] == ANSI_KEY && arg[i + 1] == '\0' )
        {
            break;
        }

        else
        {
            count++;
            continue;
        }
    }

    return count;
}

/* returns true if the last char in a string i a colour code */
bool check_cstring ( char *string )
{
    uint i;

    if ( string[0] == '\0' )
    {
        return FALSE;
    }

    for ( i = 0; i < strlen ( string ); i++ )
    {

        if ( string[i] == '\0' )
        {
            return FALSE;
        }

        else if ( string[i] != ANSI_KEY )
        {
            continue;
        }

        else if ( string[i] == ANSI_KEY && string[i + 1] != '\0' )
        {
            i++;
            continue;
        }

        else if ( string[i] == ANSI_KEY && string[i + 1] == '\0' )
        {
            return TRUE;
        }

        else
        {
            continue;
        }
    }

    return FALSE;
}