/***************************************************************************
 *  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.                *
 *                                                                         *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"

/*****************************************************************************
 Name:		string_edit
 Purpose:	Clears string and puts player into editing mode.
 Called by:	none
 ****************************************************************************/
void string_edit ( CHAR_DATA * ch, char **pString )
{
    send_to_char
        ( "{r-{R========{r- {WEntering EDIT Mode {r-{R========={r-{x\n\r",
          ch );
    send_to_char ( "    {WType {C.h{W on a new line for help{x\n\r", ch );
    send_to_char
        ( " {WTerminate with a {C~{W or {C@{W on a blank line.{x\n\r", ch );
    send_to_char ( "{r-{R======================================={r-{x\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 )
{
    send_to_char
        ( "{r-{R======={r- {WEntering APPEND Mode {r-{R========{r-{x\n\r",
          ch );
    send_to_char ( "    {WType {C.h{W on a new line for help.{x\n\r", ch );
    send_to_char
        ( " {WTerminate with a {C~ {Wor {C@ {Won a blank line.{x\n\r", ch );
    send_to_char ( "{r-{R======================================={r-{x\n\r",
                   ch );

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

    if ( *( *pString + strlen ( *pString ) - 1 ) != '\r' )
        send_to_char ( "\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 *new )
{
    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, new );
        strcat ( xbuf, &orig[i + strlen ( old )] );
        free_string ( orig );
    }

    return str_dup ( xbuf );
}

/*****************************
 Name:      del_last_line
 Purpose:   Removes last line from string
 Called by: many.
 ******************/
char *del_last_line ( char *string )
{
    int len;
    bool found = FALSE;

    char xbuf[MAX_STRING_LENGTH];

    xbuf[0] = '\0';
    if ( string == NULL || string[0] == '\0' )
        return ( str_dup ( xbuf ) );

    strcpy ( xbuf, string );

    for ( len = strlen ( xbuf ); len > 0; len-- )
    {
        if ( xbuf[len] == '\r' )
        {
            if ( !found )       /* back it up */
            {
                if ( len > 0 )
                    len--;
                found = TRUE;
            }
            else                /* found second one */
            {
                xbuf[len + 1] = '\0';
                free_string ( string );
                return ( str_dup ( xbuf ) );
            }
        }
    }
    xbuf[0] = '\0';
    free_string ( string );
    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];

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

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

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

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

            smash_tilde ( arg3 );   /* Just to be sure -- Hugin */
            *ch->desc->pString =
                string_replace ( *ch->desc->pString, arg2, arg3 );
            sprintf ( buf, "'%s' replaced with '%s'.\n\r", arg2, arg3 );
            send_to_char ( buf, ch );
            return;
        }

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

        if ( !str_cmp ( arg1, ".h" ) )
        {
            send_to_char ( "Sedit help (commands on blank line):   \n\r",
                           ch );
            send_to_char ( ".r 'old' 'new'   - replace a substring \n\r",
                           ch );
            send_to_char ( "                   (requires '', \"\") \n\r",
                           ch );
            send_to_char ( ".h               - get help (this info)\n\r",
                           ch );
            send_to_char ( ".s               - show string so far  \n\r",
                           ch );
            send_to_char ( ".f               - (word wrap) string  \n\r",
                           ch );
            send_to_char ( ".c               - clear string so far \n\r",
                           ch );
            send_to_char ( ".d               - delete last line    \n\r",
                           ch );
            send_to_char ( " @               - end string          \n\r",
                           ch );
            return;
        }

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

    if ( *argument == '~' || *argument == '@' )
    {
        ch->desc->pString = NULL;
        return;
    }

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

    /*
     * Truncate strings to MAX_STRING_LENGTH.
     * --------------------------------------
     */
    if ( strlen ( *ch->desc->pString ) + strlen ( argument ) >=
         ( MAX_STRING_LENGTH - 4 ) )
    {
        send_to_char ( "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;
}