/*
                    Program Primary Routines

	Functions:

	void SWTHandleSignal(int s)
	void SWTResetTimmers(void)
	void SWTReclaimGlobalMemory(bool_t print_response)

	void SWTDoChangeBackgroundMusic(void)

	int SWTDoConnect(char *arg)
	void SWTDoDisconnect(void)
	void SWTDoRefresh(void)
	void SWTCManage(void)

	int SWTInit(int argc, char *argv[])
	void SWTManage(void)
	void SWTShutdown(void)

	int main(int argc, char *argv[])

	---

 */

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>

#include "../include/string.h"
#include "../include/disk.h"

#include "../include/unvmain.h"

#include "../include/swsoundcodes.h"


#include "net.h"
#include "swterm.h"



pid_t root_pid;
int runlevel;

long cur_millitime;
time_t cur_systime;
long lapsed_millitime;
double time_compensation;

xsw_debug_struct debug;
xsw_options_struct option;
CDisplay **curses_display;
int total_curses_displays;
xsw_sound_struct sound;
xsw_dname_struct dname;
xsw_fname_struct fname;
xsw_next_struct next;

xsw_local_control_struct local_control;
xsw_gctl_struct gctl[1];

sw_units_struct sw_units;
xsw_sector_legend_struct sector_legend;

scanner_contacts_struct **scanner_contact;
int total_scanner_contacts;

ocsn_struct **ocsn;
int total_ocsns;

xsw_loadstat_struct loadstat;
xsw_auto_interval_tune_struct auto_interval_tune;

xsw_font_struct xsw_font;
xsw_color_struct xsw_color;

xsw_bridge_win_struct bridge_win;
eco_win_struct eco_win;

dialog_win_struct err_dw;
dialog_win_struct info_dw;


xsw_object_struct **xsw_object;
int total_objects;

xsw_object_struct **inrange_xsw_object;
int total_inrange_objects;

xsw_net_parms_struct net_parms;

ss_item_struct **ss_item;
int total_ss_items;


/*
 *      Segfault counter:
 */
static int segfault_count;



/*
 *	Signal handler.
 */
void SWTHandleSignal(int s)
{
	switch(s)
	{
	  case SIGSEGV:
	    segfault_count++;
	    if(segfault_count >= 2)
		exit(0);

	    break;

	  case SIGINT:
	  case SIGTERM:
	    runlevel = 1;
	    break;

	  case SIGPIPE:
	    signal(SIGPIPE, SWTHandleSignal);
	    break;

	  default:
	    break;
	}

	return;
}

/*
 *	Resets global timmers.
 */
void SWTResetTimmers(void)
{
	int i, n;
	xsw_object_struct **obj_ptr;


        /* Global event and task timmers. */
        next.update_check = 0;
        next.viewscreen = 0;  
        next.bridge_std_redraw = 0;  
        next.memory_clean = 0;
        next.lplayer_pos_send = 0;

        /* Auto interval tunning. */
        auto_interval_tune.next = 0;

        /* Timmers on XSW Objects. */
        for(i = 0, obj_ptr = xsw_object;
            i < total_objects;
            i++, obj_ptr++
        )
        {
            if(*obj_ptr == NULL)
                continue;   

            if((*obj_ptr)->type <= XSW_OBJ_TYPE_GARBAGE)
                continue;

            /* Last network update. */
            (*obj_ptr)->last_updated = 0;

            /* Birth time, since this is in milliseconds midnight
             * we need this to be reset too.  But it may cause certain
             * objects to live a bit longer.
             */
            (*obj_ptr)->birth_time = 0;

            /* Last animation frame. */
            (*obj_ptr)->animation.last_interval = 0;

            /* Weapon last use. */
            for(n = 0; n < (*obj_ptr)->total_weapons; n++)
            {
                if((*obj_ptr)->weapons[n] == NULL)
                    continue;

                (*obj_ptr)->weapons[n]->last_used = 0;
            }
        }

	return;
}


/*
 *	Reclaims global memory.
 */
void SWTReclaimGlobalMemory(bool_t print_response)
{
	if(print_response)
	    MesgAdd("Reclaiming memory...", 0);

        DBReclaim();
/* No isrefs reclaim. */
        OCSReclaimMemory();
/* No vslabels reclaim. */
        DBInRangeUpdate(net_parms.player_obj_num);

        if(print_response)
            MesgAdd("Memory reclaimed.", 0);

	return;
}


/*
 *      Procedure to update background music, this should be called
 *      whenever the background music *may* need changing.
 */
void SWTDoChangeBackgroundMusic(void)
{
        int code = SOUND_CODE_BKG_STANDARD;
        xsw_object_struct *obj_ptr, *tar_obj_ptr;


        /* Check if main menu on bridge window is mapped. */
        if(bridge_win.screen_type == BRIDGE_SCREEN_TYPE_MAIN_MENU)
        {
            /* Main menu on bridge window is mapped. */

            code = SOUND_CODE_BKG_MAINMENU;
        }
        else
        {
            /* View screen is mapped. */

            code = SOUND_CODE_BKG_STANDARD;

            if(net_parms.player_obj_ptr == NULL)
            {
                /* Player object not valid, could be still logging in. */

            }
            else
            {
                /* Player object is valid. */

                /* Get pointer to player object. */
                obj_ptr = net_parms.player_obj_ptr;

                /* Check if in nebula. */
                if(obj_ptr->loc_type == XSW_LOC_TYPE_NEBULA)
                    code = SOUND_CODE_BKG_MYSTY;

                /* Check if locked onto something. */
                if(DBIsObjectGarbage(obj_ptr->locked_on))
                {
                    /* Player object is not locked on anything. */

                }
                else
                {
                    /* Player object is locked on something. */
                    tar_obj_ptr = xsw_object[obj_ptr->locked_on];

                    /* Check if locked on target is controlled,
                     * weapon, or player.
                     */
                    if((tar_obj_ptr->type == XSW_OBJ_TYPE_CONTROLLED) ||
                       (tar_obj_ptr->type == XSW_OBJ_TYPE_PLAYER) ||
                       (tar_obj_ptr->type == XSW_OBJ_TYPE_WEAPON) ||
                       (tar_obj_ptr->type == XSW_OBJ_TYPE_STREAMWEAPON) ||
                       (tar_obj_ptr->type == XSW_OBJ_TYPE_SPHEREWEAPON)
                    )
                        code = SOUND_CODE_BKG_FIGHTING;


                }
            }
        }

        /* Change background music (as needed). */
        if(code != sound.bkg_mood_code)
            SoundChangeBackgroundMusic(code, 0, 0);

        return;
}


/*
 *	Connect procedure. Connects to the universe specified
 *	in the given arg, which should be a URL containing
 *	the protocol, address, port, login name and login password.
 */
int SWTDoConnect(char *arg)
{
	int status;
	char *strptr;
	char url[MAX_URL_LEN];
	char text[MAX_URL_LEN + 256];


	if(arg == NULL)
	    return(-1);

	/* Copy argument to url string for parsing. */
	strncpy(url, arg, MAX_URL_LEN);
	url[MAX_URL_LEN - 1] = '\0';
	StringStripSpaces(url);

	/* Get address (mandatory). */
	strptr = StringParseAddress(url);
        if(strptr == NULL)
        {
	    sprintf(
		text,
		"Cannot parse address"
            );
            MesgAdd(text, 0);
	    return(-1);
	}
	else
	{
	    strncpy(net_parms.address, strptr, MAX_URL_LEN);
	    net_parms.address[MAX_URL_LEN - 1] = '\0';
	}

	/* Get port. */
	net_parms.port = StringParsePort(url);
	if(net_parms.port < 0)
	    net_parms.port = DEF_SWSERV_PORT;

	/* Get login name. */
        strptr = StringParseName(url);
        if(strptr != NULL)
        {
            strncpy(
                net_parms.login_name,
                strptr,   
                XSW_OBJ_NAME_MAX
            );  
            net_parms.login_name[XSW_OBJ_NAME_MAX - 1] = '\0';
        }

        /* Get login password. */
        strptr = StringParsePassword(url);
        if(strptr != NULL)
        {
            strncpy(
                net_parms.login_password,
                strptr,
                XSW_OBJ_PASSWORD_MAX
            );
            net_parms.login_password[XSW_OBJ_PASSWORD_MAX - 1] = '\0';
        }       


        /* Disconnect if already connected. */
        if(net_parms.socket > -1)
        {
            NetSendDisconnect();

            DBDeleteAllObjects();
            NetResetParms();

            /* Clear economy window entry. */
/*
	    EcoWinDoDeleteInventory();
 */
        }      

        /*   NetOpenConnection will print all errors and set up
         *   net_parm variables.
         */
        status = NetOpenConnection(net_parms.address, net_parms.port);
        if(status < 0)
        {
	    return(-1);
        }
	else
	{
            sprintf(text,
                "Connecting to %s...",
                net_parms.address
            );
            MesgAdd(text, 0);
	}



        /* Completely redraw bridge window. */
	BridgeDraw(0);


	return(0);
}

/*
 *	Disconnect procedure.
 */
void SWTDoDisconnect(void)
{
        int deallocate_resources = 0;
        char text[256];


        /* Check if currently connected. */
        if(net_parms.socket > -1)
        {
            /* Currently connected, send disconnect. */
            NetSendDisconnect();

            /* Increment disconnect send count. */
            net_parms.disconnect_send_count++;

            /* Force disconnect if disconnect send count was sent more
             * than 3 times.
             */
            if(net_parms.disconnect_send_count > 3)
                deallocate_resources = 1;
        }
        else
        {
            /* Not connected, reset all network resources and deallocate
             * other resources.
             */
            deallocate_resources = 1;
        }

        /* Deallocate all network related resources? */
        if(deallocate_resources)
        {
            /* Delete all objects. This will also reset the player
             * object referance, delete all scanner contacts, and
             * reset inrange objects list.
             */
            DBDeleteAllObjects();
                
            /* Reset network parameters. */
            NetResetParms();

            /* Change bridge window title. */
/*
            sprintf(text, "%s: Untitled", PROG_NAME);  
            OSWSetWindowTitle(bridge_win.toplevel, text);
 */

            /* Clear economy window entries. */
/*
            EcoWinDoDeleteInventory();
 */

            /* Reset game controller positions. */
            gctl[0].turn = 0;
            gctl[0].throttle = 0;
            gctl[0].thrust_dir = 0;
        }

        /* Completely redraw bridge window. */
	BridgeDraw(0);

	return;
}

/*
 *      Does a network refresh sequence.
 */
void SWTDoRefresh(void)
{
        /* Delete object labels. */
/*
        VSLabelDeleteAll();
 */
        /* Delete scanner contacts. */
        ScDeleteAll();

        /* Update inrange objects list. */
        DBInRangeUpdate(net_parms.player_obj_num);
            
            
        /* Check if currently connected. */
        if(net_parms.socket > -1)
        {
            /* Send refresh to server. */
            NetSendRefresh();
        }

        /* Completely redraw bridge window. */
	BridgeDraw(0);

        return;
}


/*
 *      Manages curses stuff.
 */
void SWTCManage(void)
{
        int i, events_handled;
        CDisplay *display;
        CEvent event;
        char text[256];


        for(i = 0; i < total_curses_displays; i++)
        {
            display = curses_display[i];
            if(display == NULL)
                continue;

	    /* Reset events handled counter. */
	    events_handled = 0;

	    /* Get next event if any. */
            if(CGetNextEvent(
                display,
                &event
            ) <= 0)
                continue;


	    /* Begin calling event managers. */

	    /* Bridge window. */
	    events_handled += BridgeManage(&event);
        }

        return;
}

/* 
 *	ShipWars Terminal initialziation.
 *
 *	Call this function only once at startup.
 */
int SWTInit(int argc, char *argv[])
{
	int i, status;
	char *strptr;
	char cwd[PATH_MAX];
	char home[PATH_MAX];
	char *startup_url = NULL;


        /* First thing to do on initialization is to reset the
         * segfault counter.   
         */
        segfault_count = 0;

        /* Initialize time zone. */
        tzset();

        /* Reset/fetch global variables to their default values. */
        root_pid = getpid();

        debug.level = DEBUG_LEVEL_NONE;
        debug.val = 0;

        cur_millitime = MilliTime();
        cur_systime = time(NULL);
        lapsed_millitime = 0;

        time_compensation = 1.0;
        
        next.update_check = cur_millitime;
        next.viewscreen = cur_millitime;
        next.bridge_std_redraw = cur_millitime;
        next.memory_clean = cur_millitime + MEMORY_CLEAN_INTERVAL;
        next.lplayer_pos_send = cur_millitime;

        strncpy(dname.startup, cwd, PATH_MAX);
        dname.startup[PATH_MAX - 1] = '\0';
        strncpy(dname.toplevel, DEF_XSW_TOPLEVEL_DIR, PATH_MAX);
        dname.toplevel[PATH_MAX - 1] = '\0';
        strncpy(dname.etc, DEF_XSW_ETC_DIR, PATH_MAX);
        dname.etc[PATH_MAX - 1] = '\0'; 
        strncpy(dname.images, DEF_XSW_IMAGES_DIR, PATH_MAX);
        dname.images[PATH_MAX - 1] = '\0';
        strncpy(dname.sounds, DEF_XSW_SOUNDS_DIR, PATH_MAX);
        dname.sounds[PATH_MAX - 1] = '\0';
        strptr = getenv("HOME");
        strncpy(dname.downloads, ((strptr == NULL) ? "/" : strptr), PATH_MAX);
        dname.downloads[PATH_MAX - 1] = '\0';

        xsw_font.std = NULL;
        xsw_font.std_bold = NULL;
        xsw_font.console_heading = NULL;   
        xsw_font.console_standard = NULL;
        xsw_font.console_message = NULL;

        option.rc_version_major = PROG_VERSION_MAJOR;
        option.rc_version_minor = PROG_VERSION_MINOR;
        
        option.units = XSW_UNITS_XSW;    

        option.def_scanner_orient = SCANNER_ORIENT_LOCAL;
        
        option.log_client = 0;
        option.log_net = 0;
        option.log_errors = 0;

	option.energy_saver_mode = 0;
        option.show_formal_label = 1;  
        option.show_net_errors = 0;
        option.show_server_errors = 1;   
        option.local_updates = 1;
        option.sounds = XSW_SOUNDS_NONE;
        option.music = 0;

        option.scanner_limiting = 1;
        option.notify_scanner_contacts = 0;	/* Default to no for swterm. */

	curses_display = NULL;
	total_curses_displays = 0;

        sound.server_type = SNDSERV_TYPE_NONE;
        strncpy(
            sound.start_cmd,
            DEF_SOUND_SERVER_START_CMD,
            PATH_MAX + NAME_MAX
        );
        sound.start_cmd[PATH_MAX + NAME_MAX - 1] = '\0';
        strncpy(
            sound.con_arg,
            DEF_SOUND_SERVER_CONNECT_ARG,
            MAX_URL_LEN
        );
        sound.con_arg[MAX_URL_LEN - 1] = '\0';
        sound.con_data = NULL;

        sw_units.ru_to_au = DEF_UNITCONV_RU_TO_AU;

        sector_legend.x_max = SECTOR_SIZE_X_MAX;
        sector_legend.x_min = SECTOR_SIZE_X_MIN;
        sector_legend.y_max = SECTOR_SIZE_Y_MAX;
        sector_legend.y_min = SECTOR_SIZE_Y_MIN;
        sector_legend.z_max = SECTOR_SIZE_Z_MAX;
        sector_legend.z_min = SECTOR_SIZE_Z_MIN;
        sector_legend.x_len = sector_legend.x_max - sector_legend.x_min;
        sector_legend.y_len = sector_legend.y_max - sector_legend.y_min;
        sector_legend.z_len = sector_legend.z_max - sector_legend.z_min;

	local_control.weapon_freq = 180.25;
	local_control.weapons_online = 1;

        scanner_contact = NULL;
        total_scanner_contacts = 0;

        ocsn = NULL;
        total_ocsns = 0;

        xsw_object = NULL;
        total_objects = 0;
          
        inrange_xsw_object = NULL;
        total_inrange_objects = 0;

        ss_item = NULL;
        total_ss_items = 0;

        /* Reset player object to none (-1). */
        DBSetPlayerObject(-1);


        /* Set default fname. */
        strncpy(
            fname.rc,
            PrefixPaths(cwd, DEF_XSW_RCFILE),
            PATH_MAX + NAME_MAX
        );  
        fname.rc[PATH_MAX + NAME_MAX - 1] = '\0';

        strncpy(
            fname.sound_scheme,
            PrefixPaths(cwd, DEF_XSW_SOUND_SCHEME_FILE),
            PATH_MAX + NAME_MAX
        );
        fname.sound_scheme[PATH_MAX + NAME_MAX - 1] = '\0';

        strncpy(
            fname.ocsn,
            PrefixPaths(cwd, DEF_XSW_OCSN_FILE),
            PATH_MAX + NAME_MAX
        );
        fname.ocsn[PATH_MAX + NAME_MAX - 1] = '\0';

        strncpy(
            fname.log,
            PrefixPaths(cwd, DEF_XSW_LOG_FILE),
            PATH_MAX + NAME_MAX
        );
        fname.log[PATH_MAX + NAME_MAX - 1] = '\0';


        net_parms.connection_state = CON_STATE_NOT_CONNECTED;
        net_parms.socket = -1;

        strptr = getenv("LOGNAME");
        if(strptr != NULL)
            strncpy(net_parms.login_name, strptr, XSW_OBJ_NAME_MAX);
        else
            memset(net_parms.login_name, '\0', XSW_OBJ_NAME_MAX);
        net_parms.login_name[XSW_OBJ_NAME_MAX - 1] = '\0';

        memset(net_parms.login_password, '\0', XSW_OBJ_PASSWORD_MAX);
        
        net_parms.is_address_set = 0;
        net_parms.con_start = 0;
        net_parms.warn15 = 0;  
        net_parms.warn30 = 0;
        net_parms.warn45 = 0;
        net_parms.login_got_lplayer = 0;
        net_parms.login_got_position = 0;
        net_parms.login_got_sector = 0;
        
        strncpy(net_parms.address, DEF_SWSERV_ADDRESS, MAX_URL_LEN);
        net_parms.address[MAX_URL_LEN - 1] = '\0';
       
        net_parms.port = DEF_SWSERV_PORT;

        net_parms.net_int = SERVER_DEF_INT;
        net_parms.disconnect_send_count = 0;   
        net_parms.bad_send_count = 0;
          
        memset(net_parms.co_data, 0x00, CS_DATA_MAX_LEN);
        net_parms.co_data_len = 0;

        loadstat.net_load_max = DEF_NET_LOAD_MAX;
        loadstat.rx_interval = 0;
        loadstat.sx_interval = 0;
        loadstat.rx_ilast = 0;
        loadstat.sx_ilast = 0;
        
        auto_interval_tune.state = 1;
        auto_interval_tune.interval = AINT_DEF_TUNE_INT;
        auto_interval_tune.next = cur_millitime +
            auto_interval_tune.interval;


        /* Initialize universe object management. */
        UNVInit(argc, argv);

        /* Handle command line parameters. */
        for(i = 1; i < argc; i++)
        {
	    if(argv[i] == NULL)
		continue;

	    /* Help. */
	    if(strcasepfx(argv[i], "--h") ||
               strcasepfx(argv[i], "-h") ||
               strcasepfx(argv[i], "-?")
	    )
	    {
		printf(PROG_USAGE_MESG);
		return(-4);
	    }
	    /* Version. */
            else if(strcasepfx(argv[i], "--v") ||
                    strcasepfx(argv[i], "-v")
            )
            {
                printf(
		    "%s Version %s\n",
		    PROG_NAME, PROG_VERSION
		);
                return(-4);
            }
	    /* Data toplevel directory. */
	    else if(strcasepfx(argv[i], "-d"))
	    {
		i++;
		if(i < argc)
		{
		    strncpy(
			dname.toplevel,
			argv[i],
			PATH_MAX
		    );
		    dname.toplevel[PATH_MAX - 1] = '\0';

		    strptr = PrefixPaths(dname.toplevel, "etc");
                    strncpy(
			dname.etc,
			((strptr == NULL) ? "/" : strptr),
			PATH_MAX
		    );
		    dname.etc[PATH_MAX - 1] = '\0';

                    strptr = PrefixPaths(dname.toplevel, "images");
                    strncpy(
                        dname.images,
                        ((strptr == NULL) ? "/" : strptr),
                        PATH_MAX
                    );
                    dname.images[PATH_MAX - 1] = '\0';

                    strptr = PrefixPaths(dname.toplevel, "sounds");
                    strncpy(
                        dname.sounds,
                        ((strptr == NULL) ? "/" : strptr),
                        PATH_MAX
                    );
                    dname.sounds[PATH_MAX - 1] = '\0';
		}
		else
		{
		    fprintf(stderr,
			"%s: Requires argument.\n",
			argv[i - 1]
		    );
		}
	    }
	    else if((argv[i][0] != '-') &&
                    (argv[i][0] != '+')
	    )
	    {
		startup_url = argv[i];
	    }
	}


	/* Connect to curses library. */
	total_curses_displays = 1;
	curses_display = (CDisplay **)malloc(
	    total_curses_displays * sizeof(CDisplay *)
	);
	if(curses_display == NULL)
	{
	    total_curses_displays = 0;
	    return(-1);
	}

	curses_display[0] = CWInit(argc, argv);
	if(curses_display[0] == NULL)
	    return(-1);


	/* Initialize bridge window. */
	if(BridgeInit(argc, argv))
	    return(-1);
	BridgeMap();
	BridgeSetFocus(1);

        /* Initialize reality engine. */
        REngInit();


        /* ********************************************************* */
        /* Select signals to watch for. */

        signal(SIGHUP, SWTHandleSignal);
        signal(SIGINT, SWTHandleSignal);
        signal(SIGQUIT, SWTHandleSignal);
        signal(SIGABRT, SWTHandleSignal);
        signal(SIGKILL, SWTHandleSignal);
        signal(SIGSEGV, SWTHandleSignal);
        signal(SIGPIPE, SWTHandleSignal);
        signal(SIGTERM, SWTHandleSignal);
        signal(SIGCONT, SWTHandleSignal);
        signal(SIGSTOP, SWTHandleSignal);



	/* Connect at startup? */
	if(startup_url != NULL)
	{
	    SWTDoConnect(startup_url);
	}
	else
	{
	    MesgAdd("ShipWars Terminal is ready, press F9 to connect.", 0);
	}

	return(0);
}

/*
 *	ShipWars Terminal management procedure.
 *
 *	This function needs to be called once per loop.
 */
void SWTManage(void)
{
        int i, player_obj_num, tar_obj_num;
        long new_millitime;


        /* Update timmings. */
        cur_systime = time(NULL);
        new_millitime = MilliTime();

        /*   Reset timmers if new millitime is less than
         *   cur_millitime.
         */
        if(new_millitime < cur_millitime)
        {
            /* Reset global timmers. */
            SWTResetTimmers();

            lapsed_millitime = 0;
            time_compensation = 1.0;
        }
        else
        {
            lapsed_millitime = new_millitime - cur_millitime;
            time_compensation = (double)(
                (double)lapsed_millitime / (double)CYCLE_LAPSE_MS
            );
            if(time_compensation < 1)
                time_compensation = 1;
        }

        /* Now set global variable cur_millitime. */
        cur_millitime = new_millitime;


        /* *********************************************************** */

	/* Manage curses input and events. */
	SWTCManage();

	/* Manage sound. */


	/* Manage network. */
	switch(net_parms.connection_state)
        {
          case CON_STATE_CONNECTED:	/* Connected. */
	    /* Handle any incoming data. */
	    NetHandleRecv();

	    /* Adjust automatic interval tunning. */
            if(auto_interval_tune.next <= cur_millitime)
            {
                AIntvTuneHandleAdjust();
        
                /* Schedual next auto interval tune. */
                auto_interval_tune.next = cur_millitime +
                    auto_interval_tune.interval;
            }

            /* Send out local player object position to server. */
            if(next.lplayer_pos_send <= cur_millitime)
            {
                /* Get referance to player object number. */
                player_obj_num = net_parms.player_obj_num;

                /* Send sector position. */
                NetSendObjectSect(player_obj_num);  
            
                /* Pose object (coordinates). */
                NetSendPoseObj(player_obj_num);
        
                /* Throttle position. */
                NetSendObjectThrottle(player_obj_num);   
                    
                /* Schedual next player position send. */
                next.lplayer_pos_send = cur_millitime +
                    net_parms.net_int;
            }
	    break;

	  case CON_STATE_NEGOTIATING:	/* Logging in. */
            /* Warn if logging in is taking longer than it should. */
            if(((net_parms.con_start + 15) <= cur_systime) &&
               !net_parms.warn15
            )   
            {
                MesgAdd(
"Waited 15 seconds for server response, still waiting...",
                    xsw_color.standard_text
                );
                net_parms.warn15 = 1;
            }
            else if(((net_parms.con_start + 30) <= cur_systime) &&
                    !net_parms.warn30
            )
            {
                MesgAdd(
"Waited 30 seconds for server response, still waiting...",
                    xsw_color.standard_text
                );
                net_parms.warn30 = 1;
            }
            else if(((net_parms.con_start + 45) <= cur_systime) &&
                    !net_parms.warn45 
            )
            {
                MesgAdd(
"Waited 45 seconds (press ??? a few times to give up)...",
		    xsw_color.standard_text
		);
                net_parms.warn45 = 1;
            }

            /* Handle incoming data (just checking for login or
	     * or disconnect).
             */
            NetHandleRecv();
            break;

          /* Not connected. */
          default:      /* CON_STATE_NOT_CONNECTED */
            break;
	}


	/* Manage XSW objects. */
        REngManage();


	/* Redraw graphics. */

        /* Redraw standard bridge graphics. */
        if(next.bridge_std_redraw <= cur_millitime)
        {
	    /* Redraw bridge things that need to be redrawn often. */
	    BridgeDrawSchedualed();

            /* Schedual next time to draw consoles. */
            next.bridge_std_redraw = cur_millitime +
		BRIDGE_STANDARD_REDRAW_INTERVAL;
	}

        /* ************************************************* */
        /* Clean/refresh memory. */
        if(next.memory_clean <= cur_millitime)
        {
            /* Clean, reclaim, memory and schedual next. */
            SWTReclaimGlobalMemory(False);

            /* XSWReclaimGlobalMemory() already scheduals next. */
        }


	return;
}

/*
 *	ShipWars Terminal shutdown procedure.
 *
 *	All resources will be shutdown and global memory deallocated
 *	here. Call this function once at shutdown.
 */
void SWTShutdown(void)
{
	int i;


        /* Close connection properly. */
/*
	SWTDoDisconnect();
 */

	/* Bridge window. */
	BridgeDestroy();


	/* Curses library resources. */
	for(i = 0; i < total_curses_displays; i++)
	{
	    CWShutdown(curses_display[i]);
	}
	free(curses_display);
	curses_display = NULL;
	total_curses_displays = 0;


        /* Reality engine shutdown. */
        REngShutdown();

        /* Shutdown sound server and free sound schemes. */
        SoundShutdown();

        /* Delete all object create script names. */
        OCSDeleteAll();

	/* Delete all scanner contacts. */
        ScDeleteAll();

        /* Delete all objects, this has probably already been
         * done with a call to SWTDoDisconnect(). But we delete
         * them all here just incase.
         */
        DBDeleteAllObjects();

        /* Shutdown universe object management. */
        UNVShutdown();


	return;
}

int main(int argc, char *argv[])
{
        int status;


        /* Runlevel 1: Starting. */
        runlevel = 1;


        /* Initialize. */
        status = SWTInit(argc, argv);
        switch(status)
        {
          /* Success. */
          case 0:
            break;

          /* Just print help or version. */
          case -4:
            SWTShutdown();
            return(0);
            break;
        
          /* Error. */
          default:
            SWTShutdown();
            return(1);
            break;
        }


        /* Runlevel 2: Normal running. */
        runlevel = 2;
        while(runlevel >= 2)
        {
            /* Sleep. */
            usleep(CYCLE_LAPSE_US);

            /* Standard program management. */
            SWTManage();
        }   


        /* Runlevel 1: Begin shutdown sequence. */

        /* Save configuration. */
/*
        if(option.save_on_exit)
            RCSaveToFile(fname.rc);
 */

        SWTShutdown();

        /* Runlevel 0: Finished */
        runlevel = 0;


	return(0);
}