/*
 * Seven Kingdoms: Ancient Adversaries
 *
 * Copyright 1997,1998 Enlight Software Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

//Filename    : OF_HARB.CPP
//Description : Firm Harbor

#include <OINFO.h>
#include <OVGA.h>
#include <vga_util.h>
#include <ODATE.h>
#include <OHELP.h>
#include <OMOUSE.h>
#include <OSTR.h>
#include <OSYS.h>
#include <OFONT.h>
#include <OBUTTON.h>
#include <OBUTT3D.h>
#include <OVBROWIF.h>
#include <OTERRAIN.h>
#include <OPOWER.h>
#include <OREGIONS.h>
#include <OU_MARI.h>
#include <OGAME.h>
#include <ONATION.h>
#include <OF_HARB.h>
#include <OREMOTE.h>
#include <OBUTTCUS.h>
#include <OSE.h>
#include <OSERES.h>
#include "gettext.h"


//------------- Define coordinations -----------//

enum { SHIP_BROWSE_X1 = INFO_X1,
		 SHIP_BROWSE_Y1 = INFO_Y1+54,
		 SHIP_BROWSE_X2 = INFO_X2,
		 SHIP_BROWSE_Y2 = SHIP_BROWSE_Y1+100
	  };

enum { SHIP_DET_X1 = INFO_X1,
		 SHIP_DET_Y1 = SHIP_BROWSE_Y2+5,
		 SHIP_DET_X2 = INFO_X2,
		 SHIP_DET_Y2 = SHIP_DET_Y1+92
	  };

//---------- Define constant ------------//

enum { HARBOR_MENU_MAIN,
		 HARBOR_MENU_BUILD,
	  };

// ######## begin Gilbert 22/9 #######//
#define BUILD_BUTTON_WIDTH      205
#define BUILD_BUTTON_HEIGHT     48
#define COUNT_BUTTON_OFFSET_X   160
#define COUNT_BUTTON_OFFSET_Y 8
// ######## end Gilbert 22/9 #######//
#define COUNT_BUTTON_WIDTH		32
#define COUNT_BUTTON_HEIGHT	32

//----------- Define static variables ----------//

static VBrowseIF 		browse_ship;
static Button3D		button_build, button_sail;
static Button3D		button_cancel_build;
static ButtonGroup	button_mode(2);
// ###### begin Gilbert 20/9 #######//
static short                 button_unit_id[MAX_SHIP_TYPE];
static ButtonCustom     button_ship[MAX_SHIP_TYPE];
static ButtonCustom     button_queue_ship[MAX_SHIP_TYPE];
static int queue_ship_selected = -1;
// ###### end Gilbert 20/9 #######//
static int				added_count;
static ButtonCustom	button_cancel;
static int				last_ship_count;
static FirmHarbor* 	firm_harbor_ptr;
static char       	harbor_menu_mode;
static char       	disable_refresh=0;
static char				ship_disp_mode=SHIP_MENU_UNIT;

//----------- Define static functions ----------//

static void put_ship_rec(int recNo, int x, int y, int refreshFlag);
static void i_disp_build_button(ButtonCustom *button, int);
static void i_disp_queue_button(ButtonCustom *button, int);

//--------- Begin of function FirmHarbor::FirmHarbor ---------//
//
FirmHarbor::FirmHarbor()
{
	ship_count = 0;
	build_unit_id = 0;
	build_queue_count = 0;

	land_region_id = 0;
	sea_region_id = 0;
	link_checked = 0;
	linked_mine_num = linked_factory_num = linked_market_num = 0;
	memset(linked_mine_array, 0, sizeof(short)*MAX_LINKED_FIRM_FIRM);
	memset(linked_factory_array, 0, sizeof(short)*MAX_LINKED_FIRM_FIRM);
	memset(linked_market_array, 0, sizeof(short)*MAX_LINKED_FIRM_FIRM);
}
//----------- End of function FirmHarbor::FirmHarbor -----------//


//--------- Begin of function FirmHarbor::deinit_derived ---------//
//
void FirmHarbor::deinit_derived()
{
	int shipUnitRecno;

	for( int i=ship_count ; i>0 ; i-- )
	{
		shipUnitRecno = ship_recno_array[i-1];

		//---- mobilize ships in the harbor ----//

		sail_ship( shipUnitRecno, COMMAND_AUTO );

		//--- if the ship does not sailed out due to no limit sea space, delete it  ---//

		if( ship_count==i )		
		{
			del_hosted_ship(shipUnitRecno);			
			unit_array.del(shipUnitRecno);
		}
	}
}
//----------- End of function FirmHarbor::deinit_derived() -----------//


//--------- Begin of function FirmHarbor::init ---------//
//
void FirmHarbor::init(int xLoc, int yLoc, int nationRecno, int firmId, const char* buildCode, short builderRecno)
{
	// ignore raceId and find north, south, west or east harbor

	if( world.get_loc(xLoc+1, yLoc+2)->can_build_harbor(1) )
	{
		// check north harbour
		Firm::init(xLoc, yLoc, nationRecno, firmId, "N", builderRecno);
		land_region_id = world.get_loc(xLoc+1, yLoc+2)->region_id;
		sea_region_id = world.get_loc(xLoc+1, yLoc)->region_id;
	}
	else if( world.get_loc(xLoc+1, yLoc)->can_build_harbor(1) )
	{
		// check south harbour
		Firm::init(xLoc, yLoc, nationRecno, firmId, "S", builderRecno);
		land_region_id = world.get_loc(xLoc+1, yLoc)->region_id;
		sea_region_id = world.get_loc(xLoc+1, yLoc+2)->region_id;
	}
	else if( world.get_loc(xLoc+2, yLoc+1)->can_build_harbor(1) )
	{
		// check west harbour
		Firm::init(xLoc, yLoc, nationRecno, firmId, "W", builderRecno);
		land_region_id = world.get_loc(xLoc+2, yLoc+1)->region_id;
		sea_region_id = world.get_loc(xLoc, yLoc+1)->region_id;
	}
	else if( world.get_loc(xLoc, yLoc+1)->can_build_harbor(1) )
	{
		// check east harbour
		Firm::init(xLoc, yLoc, nationRecno, firmId, "E", builderRecno);
		land_region_id = world.get_loc(xLoc, yLoc+1)->region_id;
		sea_region_id = world.get_loc(xLoc+2, yLoc+1)->region_id;
	}
	else
	{
		err_here();
	}

	region_id = land_region_id;		// set region_id to land_region_id

	//------- update the harbor count of the regions ------//

	region_array.update_region_stat();
}
//----------- End of function FirmHarbor::init -----------//


//------- Begin of function FirmHarbor::assign_unit -----------//
//
void FirmHarbor::assign_unit(int unitRecno)
{
	//------- if this is a construction worker -------//

	if( unit_array[unitRecno]->skill.skill_id == SKILL_CONSTRUCTION )
	{
		set_builder(unitRecno);
		return;
	}

	//---------------------------------------//

	//if( ship_count==MAX_SHIP_IN_HARBOR )
	//	return;
	if(ship_count + (build_unit_id>0) == MAX_SHIP_IN_HARBOR)
		return; // leave a room for the building unit

	add_hosted_ship(unitRecno);

	unit_array[unitRecno]->deinit_sprite();

	UnitMarine *unitPtr = (UnitMarine*) unit_array[unitRecno];
	unitPtr->extra_move_in_beach = NO_EXTRA_MOVE;
	unitPtr->deinit_sprite();

	//------------------------------------------------//

	if( firm_array.selected_recno == firm_recno )
		info.disp();
}
//--------- End of function FirmHarbor::assign_unit -----------//


//--------- Begin of function FirmHarbor::next_day ---------//
//
void FirmHarbor::next_day()
{
	//----- call next_day() of the base class -----//

	Firm::next_day();

	//------- process building -------//

	if( build_unit_id )
		process_build();
	else
		process_queue();

	//-*********** simulate ship movement ************-//
	//if(build_unit_id==0)
	//	build_ship(UNIT_CARAVEL, 0);
	//-*********** simulate ship movement ************-//
}
//----------- End of function FirmHarbor::next_day -----------//


//--------- Begin of function FirmHarbor::put_info ---------//
//
void FirmHarbor::put_info(int refreshFlag)
{
	if( refreshFlag==INFO_REPAINT && !disable_refresh )
		harbor_menu_mode = HARBOR_MENU_MAIN;

	switch( harbor_menu_mode )
	{
		case HARBOR_MENU_MAIN:
			disp_main_menu(refreshFlag);
			break;

		case HARBOR_MENU_BUILD:
			disp_build_menu(refreshFlag);
			break;
	}
}
//----------- End of function FirmHarbor::put_info -----------//


//--------- Begin of function FirmHarbor::detect_info ---------//
//
int FirmHarbor::detect_info()
{
	switch( harbor_menu_mode )
	{
		case HARBOR_MENU_MAIN:
			return detect_main_menu();

		case HARBOR_MENU_BUILD:
			return detect_build_menu();
	}

	return 0;
}
//----------- End of function FirmHarbor::detect_info -----------//


//--------- Begin of function FirmHarbor::disp_main_menu ---------//
//
void FirmHarbor::disp_main_menu(int refreshFlag)
{
	firm_harbor_ptr = this;

	disp_basic_info(INFO_Y1, refreshFlag);

	if( !should_show_harbor_info() )
		return;

	//-------- display browser -----------//

	if( refreshFlag == INFO_REPAINT )
	{
		browse_ship.init( SHIP_BROWSE_X1, SHIP_BROWSE_Y1, SHIP_BROWSE_X2, SHIP_BROWSE_Y2,
								0, 25, ship_count, put_ship_rec );

		browse_ship.open(1);

		put_det(INFO_REPAINT);
	}
	else
	{
		if( last_ship_count != ship_count )
		{
			last_ship_count = ship_count;
			info.disp();
		}
		else
			browse_ship.update();          // update only
	}

	last_ship_count = ship_count;

   //-------------------------------//

	if( !own_firm() )
		return;

	//-------- display buttons ---------//

	if( refreshFlag == INFO_REPAINT )
	{
		button_build.paint( SHIP_DET_X1, SHIP_DET_Y2+4, 'A', "MAKESHIP" );
		button_sail.paint ( SHIP_DET_X1+BUTTON_ACTION_WIDTH, SHIP_DET_Y2+4, 'A', "SAILOUT" );
	}

	if( ship_count > 0 )
		button_sail.enable();
	else
		button_sail.disable();

	//------ display the info of the ship under construction ------//

	disp_build_info(refreshFlag);
}
//----------- End of function FirmHarbor::disp_main_menu -----------//


//--------- Begin of function FirmHarbor::detect_main_menu ---------//
//
int FirmHarbor::detect_main_menu()
{
	firm_harbor_ptr = this;

	if( detect_basic_info() )
		return 1;

	if( !own_firm() )
		return 0;

	if( browse_ship.detect() )
	{
		put_det(INFO_UPDATE);
		return 1;
	}

	if( detect_det() )
		return 1;

	//------- detect the build button ---------//

	if( button_build.detect(GETKEY(KEYEVENT_FIRM_BUILD)) )
	{
		harbor_menu_mode = HARBOR_MENU_BUILD;
		disable_refresh = 1;    // static var for disp_info() only
		info.disp();
		disable_refresh = 0;
		return 1;
	}

	//-------- detect the sail button ---------//

	if( button_sail.detect(GETKEY(KEYEVENT_FIRM_PATROL)) && browse_ship.recno() > 0 )
	{
		sail_ship( ship_recno_array[browse_ship.recno()-1], COMMAND_PLAYER );
		return 1;
	}

	//---------- detect cancel build button -----------//
	if(build_unit_id)
	{
		if(button_cancel_build.detect())
		{
			if( !remote.is_enable() )
				cancel_build_unit();
			else
			{
				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_SKIP_SHIP, sizeof(short));
				shortPtr[0] = firm_recno;
			}
			return 1;
		}
	}

	return 0;
}
//----------- End of function FirmHarbor::detect_main_menu -----------//


//------- Begin of function FirmHarbor::should_show_harbor_info -------//
//
int FirmHarbor::should_show_harbor_info()
{
	if( should_show_info() )
		return 1;

	//--- if any of the ships in the harbor has the spies of the player ---//

	UnitMarine* unitMarine;

	for( int i=0 ; i<ship_count ; i++ )
	{
		unitMarine = (UnitMarine*) unit_array[ ship_recno_array[i] ];

		if( unitMarine->should_show_info() )
			return 1;
	}

	return 0;
}
//------- End of function FirmHarbor::should_show_harbor_info ---------//


//--------- Begin of function FirmHarbor::put_det ---------//
//
void FirmHarbor::put_det(int refreshFlag)
{
	if( refreshFlag == INFO_REPAINT )
		vga_util.d3_panel_up( SHIP_DET_X1, SHIP_DET_Y1, SHIP_DET_X2, SHIP_DET_Y2 );
	else
		vga_util.blt_buf( SHIP_DET_X1, SHIP_DET_Y1, SHIP_DET_X2, SHIP_DET_Y2, 0 );

	if( browse_ship.none_record )
		return;

	refreshFlag = INFO_REPAINT;		// for calling disp_ship_units() and disp_ship_goods()

	//------- display info of the ship --------//

	UnitMarine* shipUnitPtr  = (UnitMarine*) unit_array[ ship_recno_array[browse_ship.recno()-1] ];
	UnitInfo*   shipUnitInfo = unit_res[shipUnitPtr->unit_id];

	//----- if the ship can carry both goods and units, display mode selection button ---//

	if( shipUnitInfo->carry_goods_capacity > 0 &&
		 shipUnitInfo->carry_unit_capacity > 0 )
	{
		button_mode[0].create_text( SHIP_DET_X1+10, SHIP_DET_Y1+3, SHIP_DET_X1+80, SHIP_DET_Y1+19, _("Units") );
		button_mode[1].create_text( SHIP_DET_X1+90, SHIP_DET_Y1+3, SHIP_DET_X2-10, SHIP_DET_Y1+19, _("Goods") );
		button_mode.paint(ship_disp_mode);

		if( ship_disp_mode == SHIP_MENU_UNIT )
			disp_ship_units(shipUnitPtr, SHIP_DET_Y1+22, refreshFlag);
		else
			disp_ship_goods(shipUnitPtr, SHIP_DET_Y1+22, refreshFlag);
	}
	else if( shipUnitInfo->carry_goods_capacity > 0 )
	{
		disp_ship_goods(shipUnitPtr, SHIP_DET_Y1+22, refreshFlag);
	}
	else if( shipUnitInfo->carry_unit_capacity > 0 )
	{
		disp_ship_units(shipUnitPtr, SHIP_DET_Y1+22, refreshFlag);
	}
}
//----------- End of function FirmHarbor::put_det -----------//


//--------- Begin of function FirmHarbor::detect_det ---------//
//
int FirmHarbor::detect_det()
{
	if( browse_ship.none_record )
		return 0;

	UnitMarine* shipUnitPtr  = (UnitMarine*) unit_array[ ship_recno_array[browse_ship.recno()-1] ];
	UnitInfo*   shipUnitInfo = unit_res[shipUnitPtr->unit_id];

	if( shipUnitInfo->carry_goods_capacity > 0 &&
		 shipUnitInfo->carry_unit_capacity > 0 )
	{
		if( button_mode.detect() >= 0 )
		{
			ship_disp_mode = button_mode.button_pressed;
			vga_util.blt_buf( SHIP_DET_X1, SHIP_DET_Y1+22, SHIP_DET_X2, SHIP_DET_Y2, 0 );
			put_det(INFO_UPDATE);
			return 1;
		}
	}

	return 0;
}
//----------- End of function FirmHarbor::detect_det -----------//


//--------- Begin of function FirmHarbor::disp_ship_goods ---------//
//
// Display goods carried by the selected ship.
//
void FirmHarbor::disp_ship_goods(UnitMarine* shipUnit, int dispY1, int refreshFlag)
{
	int	x=INFO_X1+20, y=dispY1+5;
	String str;

	int i;
	for(i=0; i<MAX_RAW; i++, x+=60)
	{
		vga_front.d3_panel_up( x, y, x+RAW_SMALL_ICON_WIDTH+5, y+RAW_SMALL_ICON_HEIGHT+5 );

		raw_res.put_small_raw_icon( x+3, y+3, i+1 );

		font_san.disp( x+25, y+2, shipUnit->raw_qty_array[i], 1, x+59 );
	}

	x =INFO_X1+20;
	y+=19;

	for( i=0; i<MAX_PRODUCT; i++, x+=60)
	{
		vga_front.d3_panel_up( x, y, x+RAW_SMALL_ICON_WIDTH+5, y+RAW_SMALL_ICON_HEIGHT+5 );

		raw_res.put_small_product_icon( x+3, y+3, i+1 );

		font_san.disp( x+25, y+2, shipUnit->product_raw_qty_array[i], 1, x+59 );
	}
}
//---------- End of function FirmHarbor::disp_ship_goods ----------//


//--------- Begin of function FirmHarbor::disp_ship_units ---------//
//
void FirmHarbor::disp_ship_units(UnitMarine* shipUnit, int dispY1, int refreshFlag)
{
	//------ display population composition -------//

	int	  x, y;
	Unit*   unitPtr;
	static  short last_unit_race_array[MAX_UNIT_IN_SHIP];

	dispY1+=5;

	for( int i=0 ; i<MAX_UNIT_IN_SHIP ; i++ )
	{
		x = INFO_X1+10+i%5*37;
		y = dispY1+i/5*29;

		if( i<shipUnit->unit_count )
		{
			unitPtr = unit_array[ shipUnit->unit_recno_array[i] ];

			if( refreshFlag==INFO_REPAINT || last_unit_race_array[i] != unitPtr->race_id )
			{
				vga_front.d3_panel_up( x, y, x+27, y+23, 1 );
				// ####### begin Gilbert 17/10 ##########//
				vga_front.put_bitmap(x+2, y+2, unit_res[unitPtr->unit_id]->get_small_icon_ptr(unitPtr->rank_id));
				// ####### end Gilbert 17/10 ##########//
			}

			//------- set help parameters ---------//

			if( mouse.in_area(x, y, x+27, y+23) )
				help.set_unit_help( unitPtr->unit_id, 0, x, y, x+27, y+23 );

			//------------------------------------//

			last_unit_race_array[i] = unitPtr->race_id;
		}
		else
		{
			if( last_unit_race_array[i] != 0 )
			{
				vga_util.blt_buf( x-2, y-2, x+36, y+25, 0 );
				last_unit_race_array[i] = 0;
			}
		}
	}
}
//----------- End of function FirmHarbor::disp_ship_units -----------//


//--------- Begin of function FirmHarbor::disp_build_menu ---------//
//
void FirmHarbor::disp_build_menu(int refreshFlag)
{
	// ###### begin Gilbert 20/9 ######//
	if( refreshFlag == INFO_UPDATE )
	{
		for( int b=0; b<added_count; ++b )
		{
			button_ship[b].paint(-1, 0);
			// button_queue_ship[b] is called by button_ship[b].paint();
		}
	}
	else if( refreshFlag == INFO_REPAINT )
	{
		added_count=0;
		int 	 unitId, x=INFO_X1, y=INFO_Y1;
		for( unitId=1; unitId<=MAX_UNIT_TYPE ; unitId++ )
		{
			if( unit_res[unitId]->unit_class == UNIT_CLASS_SHIP )
			{
				if( unit_res[unitId]->get_nation_tech_level(nation_recno) > 0 )
				{
					// disp_build_button( y, unitId, 1);
					button_queue_ship[added_count].create(x+COUNT_BUTTON_OFFSET_X, y+COUNT_BUTTON_OFFSET_Y,
						x+COUNT_BUTTON_OFFSET_X+COUNT_BUTTON_WIDTH-1, y+COUNT_BUTTON_OFFSET_Y+COUNT_BUTTON_HEIGHT-1,
						i_disp_queue_button, ButtonCustomPara(this, unitId) );
					button_ship[added_count].paint(x, y, x+BUILD_BUTTON_WIDTH-1, y+BUILD_BUTTON_HEIGHT-1,
						i_disp_build_button, ButtonCustomPara(&button_queue_ship[added_count], unitId) );

					err_when(added_count >= MAX_SHIP_TYPE);
					button_unit_id[added_count++] = unitId;
					y += BUILD_BUTTON_HEIGHT;
				}
			}
		}

		if( !added_count || queue_ship_selected >= added_count)
			queue_ship_selected = -1;

		button_cancel.paint(x, y, x+BUILD_BUTTON_WIDTH-1, y+BUILD_BUTTON_HEIGHT*3/4,
			ButtonCustom::disp_text_button_func, ButtonCustomPara((void*)_("Done"),0) );
	}
	// ###### end Gilbert 20/9 ######//
}
//----------- End of function FirmHarbor::disp_build_menu -----------//


// ####### begin Gilbert 20/9 ########//
/*
//--------- Begin of function FirmHarbor::disp_build_button ---------//
void FirmHarbor::disp_build_button(int y, int unitId, int buttonUp)
{
	int x;
	int y0 = y;
	if(buttonUp)
	{
		vga_util.d3_panel_up(INFO_X1, y, INFO_X2-1, y+BUILD_BUTTON_HEIGHT-2);
		x = INFO_X1;
	}
	else
	{
		vga_util.d3_panel_down(INFO_X1, y, INFO_X2-1, y+BUILD_BUTTON_HEIGHT-2);
		x = INFO_X1+1;
		y++;
	}

	// display unit large icon
	UnitInfo* unitInfo = unit_res[unitId];
	vga_front.put_bitmap(x+6, y+4, unitInfo->get_large_icon_ptr(0));

	//-------- display unit name --------//

	String str;
	str = _(unitInfo->name);
	font_bible.put( x+60, y+14, str );
	disp_queue_button(y0+COUNT_BUTTON_OFFSET_Y, unitId, 1);
}
//----------- End of function FirmHarbor::disp_build_button -----------//
*/


//-------- Begin of function i_disp_build_button --------//
//
static void i_disp_build_button(ButtonCustom *button, int repaintBody)
{
	int x1 = button->x1;
	int y1 = button->y1;
	int x2 = button->x2;
	int y2 = button->y2;
	if( !button->pushed_flag )
	{
		if( repaintBody )
		{
			vga_util.blt_buf(x1, y1, x2, y2, 0);
			vga_util.d3_panel2_up( x1, y1, x2, y2, 1 );
		}
		x2--;
		y2--;
	}
	else
	{
		if( repaintBody )
		{
			vga_util.blt_buf(x1, y1, x2, y2, 0);
			vga_util.d3_panel2_down( x1, y1, x2, y2, 1 );
		}
		x1++;
		y1++;
	}

	ButtonCustom *queueButton = (ButtonCustom *)button->custom_para.ptr;
	if( repaintBody)
	{
		// display unit large icon
		short unitId = button->custom_para.value;
		UnitInfo* unitInfo = unit_res[unitId];
		vga_front.put_bitmap(x1+6, y1+4, unitInfo->get_large_icon_ptr(0));

		err_when( button->custom_para.value != queueButton->custom_para.value);

		//-------- display unit name --------//

		String str;

		str = "";

		if( queue_ship_selected>=0 && button_unit_id[queue_ship_selected] == unitId )
			str += ">";

		str += _(unitInfo->name);

		if( unitInfo->unit_class == UNIT_CLASS_WEAPON )		// add version no.
		{
			FirmHarbor *harbor = (FirmHarbor *)queueButton->custom_para.ptr;
			int techLevel = unitInfo->get_nation_tech_level(harbor->nation_recno);

			if( techLevel > 1 )
			{
				str += " ";
				str += misc.roman_number(techLevel);
			}
		}
		
		if( queue_ship_selected >=0 && button_unit_id[queue_ship_selected] == unitId )
			str += "<";

		font_bible.put( x1+60, y1+13, str );
	}

	// display small button
	queueButton->paint(-1, repaintBody);
}
//--------- End of static function i_disp_build_button ---------//


/*
//--------- Begin of function FirmHarbor::disp_queue_button ---------//
void FirmHarbor::disp_queue_button(int y, int unitId, int buttonUp)
{
	//----- count the no. of units queued for this ship ------//
	int x=INFO_X1+2+COUNT_BUTTON_OFFSET_X;
	int queuedCount=0;

	for(int i=0; i<build_queue_count; i++)
	{
		if(build_queue_array[i] == unitId)
			queuedCount++;
	}

	if(build_unit_id==unitId)
		queuedCount++;

	if(buttonUp)
		vga_util.d3_panel_up(x, y, x+COUNT_BUTTON_WIDTH-1, y+COUNT_BUTTON_HEIGHT-1);
	else
	{
		vga_util.d3_panel_down(x, y, x+COUNT_BUTTON_WIDTH-1, y+COUNT_BUTTON_HEIGHT-1);
		x++;
		y++;
	}

	font_san.center_put(x, y, x+COUNT_BUTTON_WIDTH-1 , y+COUNT_BUTTON_HEIGHT-1, misc.format(queuedCount));
}
//----------- End of function FirmHarbor::disp_queue_button -----------//
*/


//-------- Begin of static function i_disp_queue_button --------//
//
static void i_disp_queue_button(ButtonCustom *button, int repaintBody)
{
	FirmHarbor *harbor= (FirmHarbor *)button->custom_para.ptr;

	int x1 = button->x1;
	int y1 = button->y1;
	int x2 = button->x2;
	int y2 = button->y2;
	if( !button->pushed_flag )
	{
		if( repaintBody )
		{
			vga_util.blt_buf(x1, y1, x2, y2, 0);
			vga_util.d3_panel2_up( x1, y1, x2, y2, 1, 1);
		}
		x2--;
		y2--;
	}
	else
	{
		if( repaintBody )
		{
			vga_util.blt_buf(x1, y1, x2, y2, 0);
			vga_util.d3_panel2_down( x1, y1, x2, y2, 1, 1);
		}
		x1++;
		y1++;
	}

	//----- count the no. of units queued for this weapon ------//

	short unitId = button->custom_para.value;
	int queuedCount=0;
	for( int i=0 ; i<harbor->build_queue_count ; i++ )
	{
		if( harbor->build_queue_array[i] == unitId )
			queuedCount++;
	}
	if( harbor->build_unit_id == unitId)
		queuedCount++;

	font_mid.center_put( x1+3, y1+3, x2-3, y2-3, misc.format(queuedCount), 1);
}
//--------- End of static function i_disp_queue_button ---------//
// ####### end Gilbert 20/9 #######//


//--------- Begin of function FirmHarbor::detect_build_menu ---------//
//
int FirmHarbor::detect_build_menu()
{
	int 	 	 unitId, x=INFO_X1+2, y=INFO_Y1, rc, quitFlag, waitFlag;
	UnitInfo* unitInfo;

	waitFlag = 0;
	for(int b=0; b<added_count; ++b)
	{
		// ###### begin Gilbert 20/9 #########//
		unitId = button_ship[b].custom_para.value;
		// ###### end Gilbert 20/9 #########//
		unitInfo = unit_res[unitId];

		//------ detect pressing on the small queue count button -------//

		// ####### begin Gilbert 20/9 ########//
		rc = 0;
		if( (rc = button_queue_ship[b].detect(0,0,2)) != 0 )		// both button
		{
			quitFlag = 0;		// don't quit the menu right after pressing the button
		}

		//------ detect pressing on the big button -------//
		else if( !button_queue_ship[b].button_wait && ((rc = button_ship[b].detect(0,0,2)) != 0) )
		{
			quitFlag = 1;		// quit the menu right after pressing the button
		}
		// ####### end Gilbert 20/9 ########//

		if( button_queue_ship[b].button_wait || button_ship[b].button_wait )
			waitFlag = 1;

		int shiftPressed = mouse.event_skey_state & SHIFT_KEY_MASK;

		//------- process the action --------//

		if( rc > 0 )
		{
			// Holding shift will use batches of FIRMWAR_BUILD_BATCH_COUNT
			int createRemoveAmount = shiftPressed ? HARBOR_BUILD_BATCH_COUNT : 1;

			if( rc==1 )		// left button
			{
				if( remote.is_enable() )
				{
					// packet structure : <firm recno> <unit Id> <amount>
					short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_BUILD_SHIP, 3*sizeof(short) );
					shortPtr[0] = firm_recno;
					shortPtr[1] = unitId;
					shortPtr[2] = createRemoveAmount;
				}
				else
					add_queue(unitId, createRemoveAmount);
				// ##### begin Gilbert 25/9 ######//
				se_ctrl.immediate_sound("TURN_ON");
				// ##### end Gilbert 25/9 ######//
			}
			else 				// right button - remove queue
			{
				if( remote.is_enable() )
				{
					// packet structure : <firm recno> <unit Id> <amount>
					short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_BUILD_SHIP, 3*sizeof(short) );
					shortPtr[0] = firm_recno;
					shortPtr[1] = -unitId;
					shortPtr[2] = createRemoveAmount;
				}
				else
					remove_queue(unitId, createRemoveAmount);

				// ##### begin Gilbert 25/9 ######//
				se_ctrl.immediate_sound("TURN_OFF");
				// ##### end Gilbert 25/9 ######//
			}

			if( quitFlag )
				info.disp();		// info.disp() will call put_info() which will switch mode back to the main menu mode
			else
			// ######## begin Gilbert 20/9 ########//
				// disp_queue_button(y+COUNT_BUTTON_OFFSET_Y, unitId, 1);
				info.update();
			// ######## end Gilbert 20/9 ########//

			return 1;
		}

		y += BUILD_BUTTON_HEIGHT;
	}

	//------ detect the cancel button --------//

	if( button_cancel.detect() || (!waitFlag && mouse.any_click(1)) )		// press the cancel button or right click
	{
		harbor_menu_mode = HARBOR_MENU_MAIN;
		info.disp();
		// ###### begin Gilbert 26/9 #######//
		se_ctrl.immediate_sound("TURN_OFF");
		// ###### end Gilbert 26/9 #######//
		return 1;
	}

	//------ detect production selecting hotkeys --------//

	if( added_count>0 && ISKEY(KEYEVENT_MANUF_QUEUE_UP) )
	{
		queue_ship_selected--;
		if( queue_ship_selected < 0 )
			queue_ship_selected = added_count-1;
		disp_build_menu(INFO_REPAINT);
		return 1;
	}

	if( added_count>0 && ISKEY(KEYEVENT_MANUF_QUEUE_DOWN) )
	{
		queue_ship_selected++;
		if( queue_ship_selected >= added_count )
			queue_ship_selected = 0;
		disp_build_menu(INFO_REPAINT);
		return 1;
	}

	if( queue_ship_selected>=0 && ISKEY(KEYEVENT_MANUF_QUEUE_ADD) )
	{
		if( remote.is_enable() )
		{
			// packet structure : <firm recno> <unit Id>
			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_BUILD_SHIP, 3*sizeof(short) );
			shortPtr[0] = firm_recno;
			shortPtr[1] = button_unit_id[queue_ship_selected];
			shortPtr[2] = 1;
		}
		else
			add_queue(button_unit_id[queue_ship_selected], 1);

		se_ctrl.immediate_sound("TURN_ON");
		info.update();
		return 1;
	}

	if( queue_ship_selected>=0 && ISKEY(KEYEVENT_MANUF_QUEUE_ADD_BATCH) )
	{
		if( remote.is_enable() )
		{
			// packet structure : <firm recno> <unit Id>
			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_BUILD_SHIP, 3*sizeof(short) );
			shortPtr[0] = firm_recno;
			shortPtr[1] = button_unit_id[queue_ship_selected];
			shortPtr[2] = HARBOR_BUILD_BATCH_COUNT;
		}
		else
			add_queue(button_unit_id[queue_ship_selected], HARBOR_BUILD_BATCH_COUNT);

		se_ctrl.immediate_sound("TURN_ON");
		info.update();
		return 1;
	}

	if( queue_ship_selected>=0 && ISKEY(KEYEVENT_MANUF_QUEUE_REMOVE) )
	{
		if( remote.is_enable() )
		{
			// packet structure : <firm recno> <unit Id>
			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_BUILD_SHIP, 3*sizeof(short) );
			shortPtr[0] = firm_recno;
			shortPtr[1] = -button_unit_id[queue_ship_selected];
			shortPtr[2] = 1;
		}
		else
			remove_queue(button_unit_id[queue_ship_selected], 1);

		se_ctrl.immediate_sound("TURN_OFF");
		info.update();
		return 1;
	}

	if( queue_ship_selected>=0 && ISKEY(KEYEVENT_MANUF_QUEUE_REMOVE_BATCH) )
	{
		if( remote.is_enable() )
		{
			// packet structure : <firm recno> <unit Id>
			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_BUILD_SHIP, 3*sizeof(short) );
			shortPtr[0] = firm_recno;
			shortPtr[1] = -button_unit_id[queue_ship_selected];
			shortPtr[2] = HARBOR_BUILD_BATCH_COUNT;
		}
		else
			remove_queue(button_unit_id[queue_ship_selected], HARBOR_BUILD_BATCH_COUNT);

		se_ctrl.immediate_sound("TURN_OFF");
		info.update();
		return 1;
	}

	return 0;
}
//----------- End of function FirmHarbor::detect_build_menu -----------//


//--------- Begin of function FirmHarbor::build_ship ---------//
//

void FirmHarbor::build_ship(int unitId, char)
{
	if(ship_count>=MAX_SHIP_IN_HARBOR)
		return;

	Nation* nationPtr = nation_array[nation_recno];

	if( nationPtr->cash < unit_res[unitId]->build_cost )
		return;

	nationPtr->add_expense( EXPENSE_SHIP, unit_res[unitId]->build_cost);

	build_unit_id  = unitId;
	start_build_frame_no = sys.frame_count;
}

//----------- End of function FirmHarbor::build_ship -----------//


//--------- Begin of function FirmHarbor::sail_ship ---------//
//
void FirmHarbor::sail_ship(int unitRecno, char remoteAction)
{
	if( !remoteAction && remote.is_enable() )
	{
		// packet structure : <firm recno> <browseRecno>
		short *shortPtr = (short *)remote.new_send_queue_msg(MSG_F_HARBOR_SAIL_SHIP, 2*sizeof(short) );
		shortPtr[0] = firm_recno;
		shortPtr[1] = unitRecno;
		return;
	}

	//----- get the browse recno of the ship in the harbor's ship_recno_array[] ----//

	int browseRecno=0;

	for( int i=0 ; i<ship_count ; i++ )
	{
		if( ship_recno_array[i] == unitRecno )
		{
			browseRecno = i+1;
			break;
		}
	}

	err_when( !browseRecno );
	// ###### begin Gilbert 10/11 ########//
	if( !browseRecno )
		return;
	// ###### end Gilbert 10/11 ########//

	//------------------------------------------------------------//

	Unit* unitPtr = unit_array[unitRecno];

	err_when( unitPtr->unit_mode != UNIT_MODE_IN_HARBOR || unitPtr->unit_mode_para != firm_recno );

	SpriteInfo*	spriteInfo = unitPtr->sprite_info;
	int 			xLoc=loc_x1; // xLoc & yLoc are used for returning results
	int 			yLoc=loc_y1;

	if(!world.locate_space(&xLoc, &yLoc, loc_x2, loc_y2, spriteInfo->loc_width, spriteInfo->loc_height, UNIT_SEA, sea_region_id))
		return;

	unitPtr->init_sprite(xLoc, yLoc);

	del_hosted_ship(ship_recno_array[browseRecno-1]);

	//-------- selected the ship --------//

	if( firm_array.selected_recno == firm_recno &&
		 nation_recno == nation_array.player_recno )
	{
		power.reset_selection();
		unitPtr->selected_flag = 1;
		unit_array.selected_recno = unitPtr->sprite_recno;
		unit_array.selected_count = 1;

		info.disp();
	}
}
//----------- End of function FirmHarbor::sail_ship -----------//


//--------- Begin of function FirmHarbor::disp_build_info ---------//
//
void FirmHarbor::disp_build_info(int refreshFlag)
{
	if( !build_unit_id || nation_recno!=nation_array.player_recno )
		return;

	int dispY1 = INFO_Y1+26;
	int x=MSG_X1+3, y=MSG_Y1+3;

	if( refreshFlag == INFO_REPAINT )
	{
		vga_util.d3_panel_up( MSG_X1, MSG_Y1, MSG_X2, MSG_Y2 );

		vga_util.d3_panel_down(x, y, x+UNIT_SMALL_ICON_WIDTH+3, y+UNIT_SMALL_ICON_HEIGHT+3 );
		vga_front.put_bitmap(x+2, y+2, unit_res[build_unit_id]->get_small_icon_ptr(RANK_SOLDIER) );
	}

	vga_front.indicator( 0, x+UNIT_SMALL_ICON_WIDTH+6, y,
		(float)(sys.frame_count-start_build_frame_no),
		(float)unit_res[build_unit_id]->build_days * FRAMES_PER_DAY, VGA_GRAY );

	button_cancel_build.paint(MSG_X2-27, MSG_Y1+2, "V_X-U", "V_X-D");
	button_cancel_build.set_help_code( "CANCELSHP" );
}
//----------- End of function FirmHarbor::disp_build_info -----------//


//--------- Begin of function FirmHarbor::process_build ---------//
//
void FirmHarbor::process_build()
{
	int totalBuildDays = unit_res[build_unit_id]->build_days;

	err_when( !build_unit_id );

	if( (int)(sys.frame_count-start_build_frame_no) / FRAMES_PER_DAY >= totalBuildDays )
	{
		int unitRecno = unit_array.add_unit( build_unit_id, nation_recno );

		add_hosted_ship(unitRecno);

		if( own_firm() )
			se_res.far_sound(center_x, center_y, 1, 'F', firm_id, "FINS", 'S', unit_res[build_unit_id]->sprite_id);

		build_unit_id = 0;

		// ##### begin Gilbert 20/9 ########//
		if( firm_array.selected_recno == firm_recno )
		{
			disable_refresh = 1;
			info.disp();
			disable_refresh = 0;
		}
		// ##### end Gilbert 20/9 ########//

		//-*********** simulate ship movement ************-//
		//sail_ship(ship_recno_array[0], 0);
		//-*********** simulate ship movement ************-//
	}
}
//----------- End of function FirmHarbor::process_build -----------//


//--------- Begin of function FirmHarbor::cancel_build_unit ---------//
void FirmHarbor::cancel_build_unit()
{
	build_unit_id = 0;

	if( firm_array.selected_recno == firm_recno )
	{
		disable_refresh = 1;
		info.disp();
		disable_refresh = 0;
	}
}
//----------- End of function FirmHarbor::cancel_build_unit -----------//


//--------- Begin of function FirmHarbor::add_hosted_ship ---------//
//
void FirmHarbor::add_hosted_ship(int shipRecno)
{
	err_when( ship_count == MAX_SHIP_IN_HARBOR );

	ship_recno_array[ship_count++] = shipRecno;

	//---- set the unit_mode of the ship ----//

	err_when( firm_id != FIRM_HARBOR );

	unit_array[shipRecno]->set_mode(UNIT_MODE_IN_HARBOR, firm_recno);

	//---------------------------------------//

	if( firm_recno == firm_array.selected_recno )
		put_info(INFO_UPDATE);
}
//----------- End of function FirmHarbor::add_hosted_ship -----------//


//--------- Begin of function FirmHarbor::del_hosted_ship ---------//
//
void FirmHarbor::del_hosted_ship(int delUnitRecno)
{
	//---- reset the unit_mode of the ship ----//

	unit_array[delUnitRecno]->set_mode(0);

	//-----------------------------------------//

	int i;
	for( i=0 ; i<ship_count ; i++ )
	{
		if( ship_recno_array[i] == delUnitRecno )
		{
			err_when( ship_count > MAX_SHIP_IN_HARBOR );

			misc.del_array_rec( ship_recno_array, ship_count, sizeof(ship_recno_array[0]), i+1 );
			break;
		}
	}

	err_when( i==ship_count );

	ship_count--;

	if( firm_recno == firm_array.selected_recno )
		put_info(INFO_UPDATE);
}
//----------- End of function FirmHarbor::del_hosted_ship -----------//


//--------- Begin of function FirmHarbor::update_linked_firm_info ---------//
// Note: when ai is finished, ai_link_checked may be used as am indicator
//			for link_checked.  Performance should be improved in this way.
//
void FirmHarbor::update_linked_firm_info()
{
	if(link_checked)
		return; // no need to check again
	
	linked_mine_num = linked_factory_num = linked_market_num = 0;
	memset(linked_mine_array, 0, sizeof(short)*MAX_LINKED_FIRM_FIRM);
	memset(linked_factory_array, 0, sizeof(short)*MAX_LINKED_FIRM_FIRM);
	memset(linked_market_array, 0, sizeof(short)*MAX_LINKED_FIRM_FIRM);

	Firm *firmPtr;
	Nation *nationPtr;
	for(int i=linked_firm_count-1; i>=0; i--)
	{
		if(linked_firm_enable_array[i]!=LINK_EE)
			continue;

		firmPtr = firm_array[linked_firm_array[i]];
		if(!nation_array[nation_recno]->get_relation(firmPtr->nation_recno)->trade_treaty)
			continue;

		switch(firmPtr->firm_id)
		{
			case FIRM_MINE:
					nationPtr = nation_array[firmPtr->nation_recno];
					//if(nationPtr->get_relation_status(nation_recno)<=NATION_TENSE)
					//	continue;

					linked_mine_array[linked_mine_num] = firmPtr->firm_recno;
					linked_mine_num++;
					break;

			case FIRM_FACTORY:
					nationPtr = nation_array[firmPtr->nation_recno];
					//if(nationPtr->get_relation_status(nation_recno)<=NATION_TENSE)
					//	continue;

					linked_factory_array[linked_factory_num] = firmPtr->firm_recno;
					linked_factory_num++;
					break;

			case FIRM_MARKET:
					nationPtr = nation_array[firmPtr->nation_recno];
					//if(nationPtr->get_relation_status(nation_recno)<=NATION_TENSE)
					//	continue;

					linked_market_array[linked_market_num] = firmPtr->firm_recno;
					linked_market_num++;
					break;
		}
	}
}
//----------- End of function FirmHarbor::update_linked_firm_info -----------//


//--------- Begin of function FirmHarbor::process_queue ---------//
void FirmHarbor::process_queue()
{
	if(build_queue_count>0)
	{
		build_ship(build_queue_array[0], COMMAND_AUTO);

		// remove the queue no matter build_ship success or not

		misc.del_array_rec( build_queue_array, build_queue_count, sizeof(build_queue_array[0]), 1 );
		build_queue_count--;

		if( firm_array.selected_recno == firm_recno )
		{
			disable_refresh = 1;
			info.disp();
			disable_refresh = 0;
		}
	}
}
//----------- End of function FirmHarbor::process_queue -----------//


//--------- Begin of function FirmHarbor::add_queue ---------//
void FirmHarbor::add_queue(int unitId, int amount)
{
	err_when(amount < 0);
	if (amount < 0) return;

	int queueSpace = MAX_BUILD_SHIP_QUEUE - build_queue_count  - (build_unit_id>0);
	int enqueueAmount = MIN(queueSpace, amount);

	for (int i = 0; i < enqueueAmount; ++i)
		build_queue_array[build_queue_count++] = unitId;
}
//----------- End of function FirmHarbor::add_queue -----------//


//--------- Begin of function FirmHarbor::remove_queue ---------//
void FirmHarbor::remove_queue(int unitId, int amount)
{
	err_when(amount < 1);
	if (amount < 1) return;

	for( int i=build_queue_count-1 ; i>=0 ; i-- )
	{
		if( build_queue_array[i] == unitId )
		{
			err_when( build_queue_count > MAX_BUILD_SHIP_QUEUE );

			misc.del_array_rec( build_queue_array, build_queue_count, sizeof(build_queue_array[0]), i+1 );

			build_queue_count--;
			amount--;

			if (amount <= 0) return;
		}
	}

	// If there were less units of unitId in the queue than were requested to be removed then
	// also cancel build unit
	if(build_unit_id==unitId)
		cancel_build_unit();
}
//----------- End of function FirmHarbor::remove_queue -----------//


//-------- Begin of static function put_ship_rec --------//
//
static void put_ship_rec(int recNo, int x, int y, int refreshFlag)
{
	UnitMarine* unitPtr  = (UnitMarine*) unit_array[ firm_harbor_ptr->ship_recno_array[recNo-1] ];
	UnitInfo*   unitInfo = unit_res[unitPtr->unit_id];

	//-------- display unit icon -------//

	vga_front.d3_panel_down(x+1, y+1, x+UNIT_SMALL_ICON_WIDTH+4, y+UNIT_SMALL_ICON_HEIGHT+4, 2, 0 );
	// ###### begin Gilbert 17/10 #######//
	vga_front.put_bitmap(x+3, y+3, unit_res[unitPtr->unit_id]->get_small_icon_ptr(unitPtr->rank_id) );
	// ###### end Gilbert 17/10 #######//

	y+=6;

	//---------- display unit name ----------//

	font_san.put( x+32 , y, _(unitInfo->name), 1, x+119 );		// 1-use short words

	//------- display unit hit points -------//

	String str;

	str  = (int) unitPtr->hit_points;
	str += "/";
	str += unitPtr->max_hit_points;

	font_san.put( x+125, y, str, 1, x+browse_ship.rec_width-3 );
}
//----------- End of static function put_ship_rec -----------//

