/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@falcon.mercer.peachnet.edu 
MUD++ development mailing list    mudpp-list@spice.com
------------------------------------------------------------------------------
env.cc
*/

// Warning: VERY incomplete!

#include <stdio.h>
#include "llist.h"
#include "pc.h"
#include "screen.h"
#include "env.h"

#include "global.h"

Random Weather::ran;
Random Environment::ran;

const char *Environment::seasons[] =
{
	"fall",
	"winter",
	"spring",
	"summer"
};

// New -> half -> full -> half  
const char *Environment::phases[] =
{
	"is not visible... it must be a new moon",
	"is crescent shaped on its eastern side... it must be waxing",
	"is completly visible... it must be a full moon",
	"is crescent shaped on its western side... it must be waning"
};

const char *Environment::time_of_day_normal[] =
{
	"The stars shine brightly as you look out into the utter blackness of space.",
	"The stars are beginning to fade and the sun is preparing for another day.",
	"The sun begins to lighten the sky as it rises in the east. It is morning.",
	"The sun's rays light up the day. It is almost noon.",
	"The sun shines vibrently through the sky. It is afternoon.",
	"The sun it almost set. It is late afternoon.",
	"The sun is set, and the stars are becoming visible. It is nighttime.",
	"The stars shine brightly as it is nighttime."
};

const char *Environment::time_of_day_cloudy[] =
{
	"It is pitch black outside, and there are no stars showing.",
	"It is dark, and the stars are not out.",
	"The sky is beginning to lighten.",
	"The sky is very light.",
	"The sky is light.",
	"The sky has faded slightly.",
	"The sky has faded quite a bit.  It is almost dark."
	"It is very dark, and the stars are not out.\n"
};

const char *Weather::temps[] =
{
	"cool",
	"cold",
	"warm",
	"hot"
};

const char *Weather::precips[] =
{
	"It is a calm day outside.",
	"There is a slight breeze blowing.",
	"The wind is blowing steadily.",
	"Its cloudy out. Looks like a storm's brewin'...",
	"Its raining out.",
	"Its raining and lightning is blazing through the sky!"
};

const char *Weather::precips_cold[] =
{
	"It is a calm day outside.",
	"There is a slight breeze blowing.",
	"An icy wind is blowing steadily.",
	"Its cloudy out. Looks like a storm's brewin'...",
	"Its snowing out.",
	"Its snowing and you can see lightning."
};

// Update the moons
void Environment::updateMoons()
{
	// Lunos has a period of 28 days with 7 days per quarter
	if( ( moon1_path++ ) > LUNOS_PERIOD )
		moon1_path = 1;

	// Cronos has a period of 16 days with 4 days per quarter
	if( ( moon2_path++ ) > CRONOS_PERIOD )
		moon2_path = 1;
}

// This method updates the environment.
void Environment::update()
{
	bool new_month = false;
	bool new_day = false;

	hour++;

	if( hour > HOURS_PER_DAY )
	{
		hour = 0;
		day++;
		new_day = true;

		if( day > DAYS_PER_MONTH )
		{
			day = 1;
			month++;
			new_month = true;

			if( month > MONTHS_PER_YEAR )
			{
				month = 1;
				year++;
			}
		}
	}

	if( new_day )
		updateMoons();

	if( new_month )
		updateSeason();

	// Temp
	weather.update( season );

	sendInfo();
}

// Change the season
void Environment::updateSeason()
{
	if( month >= 1 && month <= 3 )
		season = SEASON_FALL;
	else if( month >= 5 && month <= 7 )
		season = SEASON_WINTER;
	else if( month >= 8 && month <= 10 )
		season = SEASON_SPRING;
	else if( month >= 11 && month <= 13 )
		season = SEASON_SUMMER;
	else
		season = SEASON_FALL;
}

// Return a string that is the MUD time and date. (do_time command)
const String & Environment::getTime()
{
	static String str;

	str.clr();

	str << "The month is " << month << " and the year is " << year << ".\n";

	// pseudocode
	//if indoors
	//{
	//"You are indoors and cannot tell what time it is.\n"
	//return
	//}

	if( weather.isClear() )
		str << time_of_day_normal[ hour / 3 ] << "\n\r";
	else
		str << time_of_day_cloudy[ hour / 3 ] << "\n\r";

	if( ( hour >= 0 && hour <= 5 ) || ( hour >= 18 && hour <= 23 ) )
		if( weather.isClear() )
			str << "Lunos " << phases[ moon1_path / 7 ] << "\n\r"
				<< "Cronos " << phases[ moon2_path / 4 ] << "\n\r";
		else
			str << "The moons are not visible this night.\n\r";

	str << "It is " << seasons[ season ] << ".\n\r";

	return str;
}

// Send some useful info to the players 
void Environment::sendInfo()
{
	String str;
	String str1;
	String str2;

	// Warn players that it is nighttime or morning (if outside),
	// but only send the chime message if they are in a city and
	// they can hear it.

	if( hour == 0 )
	{
		str1 = "The clock tower chimes midnight.\n\r";
	}
	else if( hour == 6 )
	{
		str1 = "The morning begins as the sun rises in the east.\n\r";
		str2 = "The clock tower chimes 6 o'clock, and the beginning of a new day.\n\r";
	}
	else if( hour == 12 )
	{
		str1 = "The clock tower chimes 12 o'clock.\n\r"; 
	}
	else if (hour == 18)
	{
		str1 = "The night begins as the sun sets in the west.\n\r";
		str2 = "The clock tower chimes 6 o'clock.\n\r";
	}
	else
		return;

	str << "\n\r" << BBLUE << str1 << NORMAL;
	outAllChar( str1 );
	outAllChar( str2 );
}

// Stuff for weather class
void Weather::setHumidity( int h )
{
	if( h < HUMIDITY_LOW || h > HUMIDITY_HIGH )
		return;

	humidity_level = h; 
}

void Weather::setClimate( int ct )
{
	if( ct < SEASONAL || ct > HOT_ALL_YEAR )
		return;

	climate_type = ct;
}

// Update fog
void Weather::updateFog()
{
	String str;

	if( !fog )
	{
		if( ran.get( 100 ) < 10 )
		{
			fog = 1;
			str = "Fog has begun to spread over the area.\n\r";
		}
	}
	else
	{
		if( ran.get( 100 ) < fog * 15 )
		{
			fog = 0;
			str = "The fog has cleared up.\n\r";
		}
		else
		{
			fog++;
			return;
		}
	}

	PC *ch;
	LList<PC> tlist = pcs;
	tlist.reset();

	while ( ( ch = tlist.peek() ) )
	{
		tlist.next();

		ch->out("\n");
		ch->out( GREEN );
		ch->out( str );
		ch->out( NORMAL );
	}
}


// Update's current weather. (called every tick)
void Weather::update( int season )
{
	int send_message = false;
	int old_precip = 0;

	if( climate_type == SEASONAL )
		current_temp = season;
	else
		current_temp = climate_type;

	// Are we already cloudy?
	if( precipitation < CLOUDY )
	{
		// If not do a check to upgrade to cloudy
		if( ran.get( 100 ) < ( (humidity_level * 5 ) + 5 ) )
		{
			send_message = true;
			old_precip = precipitation;
			precipitation = CLOUDY;
		}
		else
		{
			// else just switch between calm and windy
			if( ran.get( 100 ) < 25 )
				precipitation = ran.get(0, 2);
		}
	}
	else
	{
		// We're cloudy, so do a check to update status
		if( ran.get( 100 ) < ( ( humidity_level * 5 ) + 5 + precipitation ) )
		{
			old_precip = precipitation;
			send_message = true;

			// Now see wether we up or downgrade it
			if( ran.get( 100 ) < 50 )
				precipitation--;
			else
			{
				if( ( precipitation++ ) > LIGHTNING )
				{
					precipitation = LIGHTNING;
					send_message = false;
				}
			}
		}
		else
		{
			// No update so now check for thunder
			if( ran.get( 100 ) < 20 )
				sendThunder();
		}
	}

	if( send_message )
		sendInfo( old_precip );

	updateFog();
}


void Weather::sendThunder()
{
	String str;
	int wake = false;

	switch( ran.get( 0, 2 ) )
	{
		case 0:
			str = "You hear the sky grumble.\n\r";
			break;
		case 1:
			str = "A loud crash rumbles through the heavens.\n\r";
			break;
		case 2:
			str = "You are startled suddenly as you hear a loud BOOM in the sky!\n";
			wake = true;
			break;
	}

	// if (!indoors && !soundproof && !fighting)
	//	if (wake && player is asleep)
	//	{
	//		wake player up
	//		str = "You are suddenly jarred awake with a tremendous boom!\n\r";
	//	}
	//	else

	PC *ch;
	LList<PC> tlist = pcs;

	tlist.reset();

	while( ( ch = tlist.peek() ) )
	{
		tlist.next();

		ch->out("\n");
		ch->out( CYAN );
		ch->out( str );
		ch->out( NORMAL );
	}
}


// Send weather change info to players
void Weather::sendInfo( int old_precip )
{
	String str;
	String str2;

	if( old_precip == CLOUDY && precipitation < CLOUDY )
		str2 = "The clouds disappear.\n\r";
	else if( old_precip < CLOUDY && precipitation == CLOUDY )
		str2 = "A number of omninous looking dark clouds have formed in the sky.\n\r";
	else if (old_precip < RAINING && precipitation == RAINING )
	{
		if ( current_temp != TEMP_COLD )
			str2 = "It starts to rain.\n\r";
		else
			str2 = "It starts to snow.\n\r";
	}
	else if( old_precip == RAINING && precipitation < RAINING )
	{
		if( current_temp != TEMP_COLD )
			str2 = "The rain has stopped.\n\r";
		else
			str2 = "The snow has stopped falling.\n\r";
	}
	else if( old_precip < LIGHTNING && precipitation == LIGHTNING )
			str2 = "You see a blinding flash of light in the sky.\n\r";
	else if( old_precip == LIGHTNING && precipitation < LIGHTNING )
			str2 = "The lightning has stopped.\n\r";

        // if (!indoors && !fighting)

	str << "\n\r" << BYELLOW << str2 << NORMAL;
	outAllChar( str );
}

// Returns a string based on current weather
const String & Weather::getWeather()
{
	static String str;

	str.clr();
	//if (indoors)
	//{
	//	sprintf(buf, "You can't tell the weather indoors!\n");
	//	return;
	//}

	str << ( current_temp == TEMP_COLD ? precips_cold[precipitation] :
			precips[precipitation] )
		<< "\n\rIt is " << temps[current_temp] << " outside.\n\r"
		<< ( isFoggy() ? "It is foggy outside.\n\r" : "" );

	return str;
}