roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * color.cpp
 *	 Functions to handle color.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "login.h"
#include "commands.h"
#include <sstream>

#define CLEAR		"\033[0m"		// Resets color
#define C_BLACK		"\033[0;30m"	// Normal colors
#define C_RED		"\033[0;31m"
#define C_GREEN		"\033[0;32m"
#define C_YELLOW	"\033[0;33m"
#define C_BLUE		"\033[0;34m"
#define C_MAGENTA	"\033[0;35m"
#define C_CYAN		"\033[0;36m"
#define C_WHITE		"\033[0;37m"
#define C_D_GREY	"\033[1;30m"  	// Light Colors
#define C_B_RED		"\033[1;31m"
#define C_B_GREEN	"\033[1;32m"
#define C_B_YELLOW	"\033[1;33m"
#define C_B_BLUE	"\033[1;34m"
#define C_B_MAGENTA	"\033[1;35m"
#define C_B_CYAN	"\033[1;36m"
#define C_B_WHITE	"\033[1;37m"
#define C_BOLD		"\033[1m"
#define C_BLINK		"\033[5m"

int Ansi[12] = { 30, 31, 32, 33, 34, 35, 36, 37, 1, 0, 5 };
int Mirc[9] = {  1,  4,  9,  8, 12, 13, 11, 15, 15 };

//***********************************************************************
//						nameToColorCode
//***********************************************************************

char nameToColorCode(bstring name) {
	name = name.toLower();
	if(name == "blink")
		return('!');
	if(name == "blue")
		return('b');
	if(name == "bold blue")
		return('B');
	if(name == "red")
		return('r');
	if(name == "bold red")
		return('R');
	if(name == "cyan")
		return('c');
	if(name == "bold cyan")
		return('C');
	if(name == "green")
		return('g');
	if(name == "bold green")
		return('G');
	if(name == "magenta")
		return('m');
	if(name == "bold magenta")
		return('M');
	if(name == "yellow")
		return('y');
	if(name == "bold yellow")
		return('Y');
	if(name == "white")
		return('w');
	if(name == "bold white")
		return('W');
	if(name == "black")
		return('d');
	if(name == "grey")
		return('D');
	if(name == "gold")
		return('l');
	if(name == "cerulean")
		return('e');
	if(name == "pink")
		return('p');
	if(name == "sky blue")
		return('s');
	if(name == "brown")
		return('o');
	return('x');
}

//***********************************************************************
//						getColor
//***********************************************************************

const char* getAnsiColorCode(const unsigned char ch) {
	switch(ch) {
	case 'd':	return(C_BLACK);
	case 'b':	return(C_BLUE);
	case 'E':
	case 'c':	return(C_CYAN);
	case 'g':	return(C_GREEN);
	case 'm':	return(C_MAGENTA);
	case 'r':	return(C_RED);
	case 'w':	return(C_WHITE);
	case 'o':
	case 'y':	return(C_YELLOW);
	case 'B':	return(C_B_BLUE);
	case 'e':
	case 'C':	return(C_B_CYAN);
	case 'G':	return(C_B_GREEN);
	case 'M':	return(C_B_MAGENTA);
	case 'R':	return(C_B_RED);
	case 'W':	return(C_B_WHITE);
	case 'Y':	return(C_B_YELLOW);
	case 'D':	return(C_D_GREY);
	case '@':	return(C_BOLD);
	case '#':	return(C_BLINK);
	case '^':	return("^");
	case 'x':
	default:	return(CLEAR C_WHITE);
	}
}

//*********************************************************************
//						getCustomColor
//**********************************************************************

bstring Player::getCustomColor(CustomColor i, bool caret) const {
	char color;
	if(customColors[(int)i] != CUSTOM_COLOR_DEFAULT) {
		color = customColors[(int)i];
		if(color == '!')
			color = '#';
		return((bstring)(caret?"^":"") + color);
	}
	return(gConfig->getCustomColor(i, caret));
}

bstring Config::getCustomColor(CustomColor i, bool caret) const {
	if(customColors[(int)i] == CUSTOM_COLOR_DEFAULT)
		return((bstring)(caret?"^":"") + "x");
	return((bstring)(caret?"^":"") + customColors[(int)i]);
}

//*********************************************************************
//						customColorize
//**********************************************************************

const bstring Monster::customColorize(bstring text, bool caret) const {
	if(getMaster() && getMaster()->isPlayer())
		text = getMaster()->getAsConstPlayer()->customColorize(text, caret);
	return(text);
}
const bstring Player::customColorize(bstring text, bool caret) const {
	text.Replace("*CC:BROADCAST*", getCustomColor(CUSTOM_COLOR_BROADCAST, caret).c_str());
	text.Replace("*CC:GOSSIP*", getCustomColor(CUSTOM_COLOR_GOSSIP, caret).c_str());
	text.Replace("*CC:PTEST*", getCustomColor(CUSTOM_COLOR_PTEST, caret).c_str());
	text.Replace("*CC:NEWBIE*", getCustomColor(CUSTOM_COLOR_NEWBIE, caret).c_str());
	text.Replace("*CC:DM*", getCustomColor(CUSTOM_COLOR_DM, caret).c_str());
	text.Replace("*CC:ADMIN*", getCustomColor(CUSTOM_COLOR_ADMIN, caret).c_str());
	text.Replace("*CC:SEND*", getCustomColor(CUSTOM_COLOR_SEND, caret).c_str());
	text.Replace("*CC:MESSAGE*", getCustomColor(CUSTOM_COLOR_MESSAGE, caret).c_str());
	text.Replace("*CC:WATCHER*", getCustomColor(CUSTOM_COLOR_WATCHER, caret).c_str());
	text.Replace("*CC:CLASS*", getCustomColor(CUSTOM_COLOR_CLASS, caret).c_str());
	text.Replace("*CC:RACE*", getCustomColor(CUSTOM_COLOR_RACE, caret).c_str());
	text.Replace("*CC:CLAN*", getCustomColor(CUSTOM_COLOR_CLAN, caret).c_str());
	text.Replace("*CC:TELL*", getCustomColor(CUSTOM_COLOR_TELL, caret).c_str());
	text.Replace("*CC:GROUP*", getCustomColor(CUSTOM_COLOR_GROUP, caret).c_str());
	text.Replace("*CC:DAMAGE*", getCustomColor(CUSTOM_COLOR_DAMAGE, caret).c_str());
	text.Replace("*CC:SELF*", getCustomColor(CUSTOM_COLOR_SELF, caret).c_str());
	text.Replace("*CC:GUILD*", getCustomColor(CUSTOM_COLOR_GUILD, caret).c_str());
	return(text);
}

//*********************************************************************
//						resetCustomColors
//**********************************************************************

void Player::resetCustomColors() {
	memset(customColors, CUSTOM_COLOR_DEFAULT, sizeof(customColors));
	customColors[CUSTOM_COLOR_SIZE-1] = 0;
}

//***********************************************************************
//						cmdColors
//***********************************************************************
// this function demonstrates all the color options available
// Letters Remaining:
// :: a    f hijk  no q  tuv   z
// :: A    F HIJKL NOPQ STUV X Z

const char* colorSection(bool staff, const char* color, char colorChar = 0) {
	static char str[80];
	char code[10];
	code[0] = 0;
	if(colorChar == 0)
		colorChar = nameToColorCode(color);

	// staff get to see the color code
	if(staff)
		sprintf(code, "^x^^%c: ", colorChar);

	sprintf(str, "%s^%c%-10s", code, colorChar, color);
	return(str);
}

int cmdColors(Player* player, cmd* cmnd) {
	bool staff = player->isStaff();
//	player->defineMXP();

	if(!strcmp(cmnd->str[1], "reset")) {
		player->print("Custom colors have been reset to defaults.\n");
		player->resetCustomColors();
		return(0);
	} else if(cmnd->num > 2) {
		CustomColor i;
		bstring type = cmnd->str[1];
		type = type.toLower();

		if(type == "broadcast")
			i = CUSTOM_COLOR_BROADCAST;
		else if((type == "ptest" || type == "p-test") && isPtester(player))
			i = CUSTOM_COLOR_PTEST;
		else if(type == "gossip")
			i = CUSTOM_COLOR_GOSSIP;
		else if(type == "newbie")
			i = CUSTOM_COLOR_NEWBIE;
		else if(type == "dm" && player->isDm())
			i = CUSTOM_COLOR_DM;
		else if(type == "admin" && player->isAdm())
			i = CUSTOM_COLOR_ADMIN;
		else if(type == "send" && player->isCt())
			i = CUSTOM_COLOR_SEND;
		else if(type == "message" && player->isStaff())
			i = CUSTOM_COLOR_MESSAGE;
		else if(type == "watcher" && player->isWatcher())
			i = CUSTOM_COLOR_WATCHER;
		else if(type == "class")
			i = CUSTOM_COLOR_CLASS;
		else if(type == "race")
			i = CUSTOM_COLOR_RACE;
		else if(type == "clan")
			i = CUSTOM_COLOR_CLAN;
		else if(type == "tell")
			i = CUSTOM_COLOR_TELL;
		else if(type == "group")
			i = CUSTOM_COLOR_GROUP;
		else if(type == "damage")
			i = CUSTOM_COLOR_DAMAGE;
		else if(type == "self")
			i = CUSTOM_COLOR_SELF;
		else if(type == "guild")
			i = CUSTOM_COLOR_GUILD;
		else {
			player->print("Custom color type choice not understood.\n");
			return(0);
		}

		char color = nameToColorCode(getFullstrText(cmnd->fullstr, 2));
		player->setCustomColor(i, color);
		if(color == '!')
			color = '#';
		player->printColor("The ^%ccustom color^x has been set.\n", color);
		return(0);
	}

	// must be on separate lines because colorSection uses a static char
	player->print("Colors:\n");
	player->printColor("   %s", colorSection(staff, "Blue"));
	player->printColor("   %s\n", colorSection(staff, "Bold Blue"));
	player->printColor("   %s", colorSection(staff, "Red"));
	player->printColor("   %s\n", colorSection(staff, "Bold Red"));
	player->printColor("   %s", colorSection(staff, "Cyan"));
	player->printColor("   %s\n", colorSection(staff, "Bold Cyan"));
	player->printColor("   %s", colorSection(staff, "Green"));
	player->printColor("   %s\n", colorSection(staff, "Bold Green"));
	player->printColor("   %s", colorSection(staff, "Magenta"));
	player->printColor("   %s\n", colorSection(staff, "Bold Magenta"));
	player->printColor("   %s", colorSection(staff, "Yellow"));
	player->printColor("   %s\n", colorSection(staff, "Bold Yellow"));
	player->printColor("   %s", colorSection(staff, "White"));
	player->printColor("   %s\n", colorSection(staff, "Bold White"));
	player->printColor("   %s", colorSection(staff, "Black"));
	player->printColor("   %s\n", colorSection(staff, "Grey"));
	player->printColor("   %s\n", colorSection(staff, "Blink", '#'));
	player->print("\n");

	player->print("New MXP Colors:\n");
	if(!player->flagIsSet(P_MXP_ENABLED))
		player->printColor("   ^yYou will need to type \"set mxpcolors\" to enable the following colors.\n");
	player->printColor("   %s", colorSection(staff, "Gold"));
	player->printColor("   %s\n", colorSection(staff, "Cerulean"));
	player->printColor("   %s", colorSection(staff, "Pink"));
	player->printColor("   %s\n", colorSection(staff, "Sky Blue"));
	player->printColor("   %s\n", colorSection(staff, "Brown"));
	player->print("\n");
	player->printColor("Custom Colors:  type [color ^W<type> <color>^x]\n");
	player->printColor("                Choose ^Wtype^x from below, ^Wcolor^x from above.\n");
	player->printColor("                Type ^Wcolor reset^x to return to default colors.\n\n");

	std::map<bstring,bstring> options;
	std::map<bstring,bstring>::iterator it;
	options["Self: @"] = player->customColorize("*CC:SELF*");
	options["Broadcast"] = player->customColorize("*CC:BROADCAST*");
	options["Tell"] = player->customColorize("*CC:TELL*");
	options["Gossip"] = player->customColorize("*CC:GOSSIP*");
	options["Newbie"] = player->customColorize("*CC:NEWBIE*");
	options["Class"] = player->customColorize("*CC:CLASS*");
	options["Race"] = player->customColorize("*CC:RACE*");
	options["Clan"] = player->customColorize("*CC:CLAN*");
	options["Group"] = player->customColorize("*CC:GROUP*");
	options["Damage"] = player->customColorize("*CC:DAMAGE*");
	if(player->isDm())
		options["DM"] = player->customColorize("*CC:DM*");
	if(player->isAdm())
		options["Admin"] = player->customColorize("*CC:ADMIN*");
	if(player->isCt())
		options["Send"] = player->customColorize("*CC:SEND*");
	if(player->isStaff())
		options["Message"] = player->customColorize("*CC:MESSAGE*");
	if(player->isWatcher())
		options["Watcher"] = player->customColorize("*CC:WATCHER*");
	if(isPtester(player))
		options["P-Test"] = player->customColorize("*CC:PTEST*");
	if(player->getGuild())
		options["Guild"] = player->customColorize("*CC:GUILD*");

	for(it = options.begin() ; it != options.end() ; ) {
		for(int n=0; n<3; n++) {
			if(it != options.end()) {
				player->printColor("   ^x* %s%-10s", (*it).second.c_str(), (*it).first.c_str());
				it++;
			}
		}
		player->print("\n");
	}
	player->print("\n");

	return(0);
}

//***********************************************************************
//						defineColors
//***********************************************************************
// Set color flags according to negotiated telnet options on character
// creation.  Character flags override these settings after creation

void Player::defineColors() {
	if(mySock->getColorOpt() == ANSI_COLOR) {
		setFlag(P_ANSI_COLOR);
		clearFlag(P_MXP_ENABLED);
		clearFlag(P_MIRC);
		clearFlag(P_NEWLINE_AFTER_PROMPT);
	}
	else if(mySock->getColorOpt() == MXP_COLOR) {
		setFlag(P_ANSI_COLOR);
		setFlag(P_MXP_ENABLED);
        clearFlag(P_MIRC);
        clearFlag(P_NEWLINE_AFTER_PROMPT);
		mySock->defineMxp();
	} else if(mySock->getColorOpt() == NO_COLOR) {
		clearFlag(P_ANSI_COLOR);
		clearFlag(P_MXP_ENABLED);
		clearFlag(P_MIRC);
		clearFlag(P_NEWLINE_AFTER_PROMPT);
	}
}
//***********************************************************************
//                      setSocketColors
//***********************************************************************
// Set color options on the socket according to player flags (which can
// overide what we've negotiated)
void Player::setSockColors() {
    if(flagIsSet(P_ANSI_COLOR)) {
        if(flagIsSet(P_MXP_ENABLED) && mySock->getMxp()) {
            mySock->setColorOpt(MXP_COLOR);
        } else {
            mySock->setColorOpt(ANSI_COLOR);
        }
    } else {
        mySock->setColorOpt(NO_COLOR);
    }
}


//***********************************************************************
//						ANSI
//***********************************************************************

void ANSI(Socket* sock, int color) {
	if(sock && sock->getPlayer()) {
		if(sock->getPlayer()->flagIsSet(P_ANSI_COLOR)) {
			if(color & BOLD)			sock->print("%c[%dm", 27, Ansi[COLOR_BOLD]);
			if(color & BLINK)			sock->print("%c[%dm", 27, Ansi[COLOR_BLINK]);
			if(color & NORMAL)			sock->print("%c[%dm", 27, Ansi[COLOR_NORMAL]);

				 if(color & RED)		sock->print("%c[%dm", 27, Ansi[COLOR_RED]);
			else if(color & GREEN)		sock->print("%c[%dm", 27, Ansi[COLOR_GREEN]);
			else if(color & YELLOW)		sock->print("%c[%dm", 27, Ansi[COLOR_YELLOW]);
			else if(color & BLUE)		sock->print("%c[%dm", 27, Ansi[COLOR_BLUE]);
			else if(color & MAGENTA)	sock->print("%c[%dm", 27, Ansi[COLOR_MAGENTA]);
			else if(color & CYAN)		sock->print("%c[%dm", 27, Ansi[COLOR_CYAN]);
			else if(color & BLACK)		sock->print("%c[%dm", 27, Ansi[COLOR_BLACK]);
			else if(color & WHITE)		sock->print("%c[%dm", 27, Ansi[COLOR_WHITE]);

		} else if(sock->getPlayer()->flagIsSet(P_MIRC) && color != BLINK) {
			if(color & BOLD)			sock->print("%c", 2);
			if(color & UNDERLINE)		sock->print("%c", 31);
			if(color & NORMAL)			sock->print("%c%c", 3, 3);

				 if(color & RED)		sock->print("%c%d", 3, Mirc[COLOR_RED]);
			else if(color & GREEN)		sock->print("%c%d", 3, Mirc[COLOR_GREEN]);
			else if(color & YELLOW)		sock->print("%c%d", 3, Mirc[COLOR_YELLOW]);
			else if(color & BLUE)		sock->print("%c%d", 3, Mirc[COLOR_BLUE]);
			else if(color & MAGENTA)	sock->print("%c%d", 3, Mirc[COLOR_MAGENTA]);
			else if(color & CYAN)		sock->print("%c%d", 3, Mirc[COLOR_CYAN]);
			else if(color & BLACK)		sock->print("%c%d", 3, Mirc[COLOR_BLACK]);
			else if(color & WHITE)		sock->print("%c%d", 3, Mirc[COLOR_WHITE]);
		}
	}
}

//***********************************************************************
//						getMXPColor
//***********************************************************************


// This function is being called during parseForOutput so we must use the actual mxp tags
// We're also assuming we get a valid mxp color tag here
bstring getMxpColorTag(bstring str, bool open) {
    std::ostringstream oStr;
    oStr << MXP_SECURE_OPEN << "<";
    if(!open)
        oStr << "/";

    oStr << gConfig->getMxpColorTag(str);

    oStr << ">" << MXP_LOCK_CLOSE;
    return(oStr.str());
}

// TODO: Put this in a lookup table
bool isMxpColor(const unsigned char ch) {
    switch(ch) {
        case 'l':
        case 'e':
        case 'p':
        case 's':
        case 'E':
        case 'o':
            return(true);
    }
    return(false);
}
//***********************************************************************
//                      getMXPColor
//***********************************************************************
// Get the color code we need to print
// Currently handles MXP & ANSI Color
// TODO: Handle xterm256 color

bstring Socket::getColorCode(const unsigned char ch) {
    std::ostringstream oStr;
    if(!opts.color) {
        // Color is not active, only replace a caret
        if(ch == '^')
            oStr << "^";
        return(oStr.str());
    } else {
        // Color is active, do replacement

        // Only return a color if the last color is not equal to the current color
        if(opts.lastColor != ch || opts.lastColor == '^') {
            if(opts.color == MXP_COLOR) {
                // Check if we need to close a mxp color
                if(isMxpColor(opts.lastColor)) {
                    oStr << getMxpColorTag(bstring(1,opts.lastColor), false);
                }
                // Now check if the new color is mxp, if it is we're done here
                // and can return now, otherwise continue on and check for an ANSI
                // color
                if(isMxpColor(ch)) {
                    opts.lastColor = ch;
                    oStr << getMxpColorTag(bstring(1,ch), true);
                    return(oStr.str());
                }
            }
            // Now Handle ANSI
            opts.lastColor = ch;
            oStr << getAnsiColorCode(ch);
        }
        return(oStr.str());
    }
}

//***********************************************************************
//						stripColor
//***********************************************************************

bstring stripColor(bstring color) {
	std::ostringstream str;
	unsigned int i=0, max = color.getLength();
	for(; i < max ; i++) {
		if(color.getAt(i) == '^')
			i++;
		else
			str << color.getAt(i);
	}
	return(str.str());
}

//***********************************************************************
//						escapeColor
//***********************************************************************

bstring escapeColor(bstring color) {
	color.Replace("^","^^");
	return(color);
}