/*
 * $Id: string_edit.c,v 1.32 1999/03/10 11:06:21 fjoe Exp $
 */

/***************************************************************************
 *                                                                         *
 *  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 "olc/olc.h"

DECLARE_DO_FUN(do_replay);

const char *string_linedel(const char *, int);
const char *string_lineadd(const char *, char *, int);
char *numlines(const char *);

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

	if (*pString == NULL)
		*pString = str_dup(str_empty);
	char_puts(numlines(*pString), ch);

	ch->desc->pString = pString;
	ch->desc->backup = str_dup(*pString);
}

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

	strnzcpy(xbuf, sizeof(xbuf), orig);

	if ((p = strstr(xbuf, old))) {
		*p = '\0';
		strnzcat(xbuf, sizeof(xbuf), new);
		strnzcat(xbuf, sizeof(xbuf), orig + (p - xbuf) + strlen(old));
	}

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

void string_add_exit(CHAR_DATA *ch, bool save)
{
	if (!save) {
		char_puts("No changes saved.\n", ch);
		free_string(*ch->desc->pString);
		*ch->desc->pString = ch->desc->backup;
	}
	else {
		olced_t *olced;

		free_string(ch->desc->backup);
		if ((olced = OLCED(ch)))
			olced->cmd_table[FUN_TOUCH].olc_fun(ch, str_empty,
					olced->cmd_table+FUN_TOUCH);
	}

	ch->desc->pString = NULL;
	if (IS_SET(ch->comm, COMM_QUIET_EDITOR))
		do_replay(ch, str_empty);
}

/*****************************************************************************
 Name:		string_add
 Purpose:	Interpreter for string editing.
 Called by:	game_loop_xxxx(comm.c).
 ****************************************************************************/
void string_add(CHAR_DATA *ch, const char *argument)
{
	const char *p;
	size_t len;
        char arg1[MAX_INPUT_LENGTH];

    /*
     * Thanks to James Seng
     */

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

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

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

        if (!str_cmp(arg1+1, "s"))
        {
            char_printf(ch, "String so far:\n%s",
            		numlines(*ch->desc->pString));
            return;
        }

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

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

        if (!str_cmp(arg1+1, "f"))
        {
            *ch->desc->pString = format_string(*ch->desc->pString);
            char_puts("String formatted.\n", ch);
            return;
        }
        
	if (!str_cmp(arg1+1, "ld"))
	{
		*ch->desc->pString = string_linedel(*ch->desc->pString, atoi(arg2));
		char_puts("Line deleted.\n", ch);
		return;
	}

	if (!str_cmp(arg1+1, "li"))
	{
		*ch->desc->pString = string_lineadd(*ch->desc->pString, tmparg3, atoi(arg2));
		char_puts("Line inserted.\n", ch);
		return;
	}

	if (!str_cmp(arg1+1, "lr"))
	{
		*ch->desc->pString = string_linedel(*ch->desc->pString, atoi(arg2));
		*ch->desc->pString = string_lineadd(*ch->desc->pString, tmparg3, atoi(arg2));
		char_puts("Line replaced.\n", ch);
		return;
	}

	if (!str_cmp(arg1+1, "q!")) {
		string_add_exit(ch, FALSE);
		return;
	}

	if (!str_cmp(arg1+1, "x")
	||  !str_cmp(arg1+1, "wq")) {
		string_add_exit(ch, TRUE);
        	return;
	}

        if (!str_cmp(arg1+1, "h"))
        {
            char_puts("Sedit help (commands on blank line):\n", ch);
            char_puts(":r 'old' 'new'   - replace a substring\n", ch);
            char_puts("                   (requires '', \"\")\n", ch);
            char_puts(":h               - get help (this info)\n", ch);
            char_puts(":s               - show string so far\n", ch);
            char_puts(":f               - (word wrap) string\n", ch);
            char_puts(":c               - clear string so far\n", ch);
            char_puts(":ld <num>        - delete line #num\n", ch);
            char_puts(":li <num> <str>  - insert <str> before line #num\n", ch);
	    char_puts(":lr <num> <str>  - replace line #num with <str>\n", ch);
            char_puts("@, ~, :x, :wq    - finish editing (save changes)\n", ch);
            char_puts(":q!              - abort editing (do not save changes)\n", ch);
            return;
        }

        char_puts("SEdit: Invalid command.\n", ch);
        return;
    }

	if (*argument == '~' || *argument == '@') {
		string_add_exit(ch, TRUE);
		return;
	}

	if (*argument == '.')
		argument++;

	/*
	 * Truncate strings to MAX_STRING_LENGTH.
	 * --------------------------------------
	*/
	len = strlen(argument);
	if (strlen(*ch->desc->pString) + len >= (MAX_STRING_LENGTH - 4)) {
		char_puts("String too long, last line skipped.\n", ch);
		return;
	}

	p = *ch->desc->pString;
	if (argument[len-1] == '\\') {
		strnzncpy(arg1, sizeof(arg1), argument, len-1);
		*ch->desc->pString = str_printf("%s%s",
						*ch->desc->pString, arg1);
	}
	else
		*ch->desc->pString = str_printf("%s%s\n",
						*ch->desc->pString, argument);
	free_string(p);
}

/*
 * 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
 ****************************************************************************/
const char *format_string(const char *oldstring /*, bool fSpace */)
{
  char xbuf[MAX_STRING_LENGTH];
  char xbuf2[MAX_STRING_LENGTH];
  const char *rdesc;
  int i=0;
  bool cap=TRUE;
  
	if (IS_NULLSTR(oldstring))
		return oldstring;

  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;
  strnzcpy(xbuf2, sizeof(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)
    {
      strnzncat(xbuf, sizeof(xbuf), rdesc, i);
      strnzcat(xbuf, sizeof(xbuf), "\n");
      rdesc += i+1;
      while (*rdesc == ' ') rdesc++;
    }
    else
    {
      bug ("No spaces", 0);
      strnzncat(xbuf, sizeof(xbuf), rdesc, 75);
      strnzcat(xbuf, sizeof(xbuf), "-\n");
      rdesc += 76;
    }
  }
  while (*(rdesc+i) && (*(rdesc+i)==' '||
                        *(rdesc+i)=='\n'||
                        *(rdesc+i)=='\r'))
    i--;
  strnzncat(xbuf, sizeof(xbuf), rdesc, i+1);
  if (xbuf[strlen(xbuf)-2] != '\n')
    strnzcat(xbuf, sizeof(xbuf), "\n");

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

const char *string_linedel(const char *string, int line)
{
	const 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')
			cnt++;
	}

	buf[tmp] = '\0';

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

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

	buf[0] = '\0';

	if (newstr[0] == '.')
		newstr++;

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

		buf[tmp++] = *strtmp;

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

		if (*strtmp == '\n')
			cnt++;

		buf[tmp] = '\0';
	}

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

/*
 * getline_ -- copy str to buf up to '\n', len is buf size
 */
const char *getline_(const char *str, char *buf, size_t len) // renamed by prool for avoid conflict with standard headers
{
	const char *p;

	p = strchr(str, '\n');
	if (!p) 
		p = strchr(str, '\0');

	strnzncpy(buf, len, str, p - str);

	if (*p == '\n') {
		p++;
		if (*p == '\r')
			p++;
	}

	return p;
}

char *numlines(const 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, sizeof(tmpb));
		snprintf(buf2, sizeof(buf2),
			 "%2d. %s\n", cnt++, tmpb);
		strnzcat(buf, sizeof(buf), buf2);
	}

	return buf;
}
