#define DIRECTSAILDEBUG	0;
//#20190625-01
//#define DIRECTENCOUNTERCHANCE 25   // chance in percent that an encounter fleet appears every transition
//#define DIRECTENCOUNTERDISTANCE 6250000.0 //2500 ^ 2 distance from player at which random ships appear
//#20181030-01
//From worldMap.playerShipActionRadius = 20.0 * 50.0 = 1000.0
#define DIRECTENCOUNTERRANDDISTANCE 6250000.0 //2500 ^ 2
//#define DIRECTENCOUNTERMINDEGREE 20
//#define DIRECTENCOUNTERRANDDEGREE 25
#define DIR_SAIL_STORM_DIST_REL  12250000.0 // 3500 ^ 2
#define DIR_SAIL_STORMWARN_DIST_REL  25000000.0 // 5000 ^ 2
#define DIR_SAIL_STORM_DIST 3500.0

#define DIR_PORTBOW 1
#define DIR_FORWARD 2
#define DIR_STARBOW 3
#define DIR_STAR 4
#define DIR_PORT 5
#define DIR_PORTSTERN 6
#define DIR_ABAFT 7
#define DIR_STARSTERN 8

#define DIR_FORE 4

//extern in DirSailEnc.c
extern void evtDirSail(aref rRawG, aref encDataForS, int iNumMerchantS, int iNumWarS,
                      float playerShipX, float playerShipZ, float locx, float locay, float locz,
                      float dst, float fScale, string eID);

void CheckIslandChange()
{
    //#20190624-01
    float RTplayerShipX;
    float RTplayerShipZ;
    int scale;
    bool isStorm, isTornado;
    getRTplayerShipXZ(&RTplayerShipX, &RTplayerShipZ, &scale);
    //#20190628-01
    float distToClosestIsland, distStorm;
    bool bScaleDiff;
    bool bCheckEnc = true;
	int nextisland = getRTclosestIsland(RTplayerShipX, RTplayerShipZ, scale, &distToClosestIsland, &bScaleDiff);

	//if (DIRECTSAILDEBUG) trace("CheckIslandChange: nextisland=" + nextisland);
	while(nextisland > -1 && nextisland != FindIsland(worldMap.island))
	{
		//only switch if pretty close
		ref rIsland = GetIslandByIndex(nextisland);//makeref(rIsland, Islands[inum]);
		string sNewIslandId = rIsland.id;
		string sIslandNow = worldMap.island;

		float distToCurIsland;
		if (worldMap.island == WDM_NONE_ISLAND) distToCurIsland = 2500000000.0; //50000.0;
		else distToCurIsland = GetDistance2DRel(RTplayerShipX, RTplayerShipZ, stf(worldMap.islands.(sIslandNow).position.x), stf(worldMap.islands.(sIslandNow).position.z));
		//#20190628-01
		//float distToClosestIsland = GetDistance2DRel(RTplayerShipX, RTplayerShipZ, stf(worldMap.islands.(sNewIslandId).position.x), stf(worldMap.islands.(sNewIslandId).position.z));

		//if (DIRECTSAILDEBUG) trace("CheckIslandChange: distToCurIsland=" + distToCurIsland + ", distToClosestIsland=" + distToClosestIsland);

		//only change if getting close
		if (bScaleDiff) {
            if(distToClosestIsland * 1.12 > distToCurIsland)
                break;
        }
        else {
            if(distToClosestIsland * 1.85 > distToCurIsland)
                break;
		}

		bCheckEnc = false;
		// aborts function if enemyships near, so that you aren't teleported out of an engagement
		//#20190703-01
		aref rSituation;
		makearef(rSituation, pchar.SeaAI.Update.Situation);
		float enemydist = 0.0;
		int nextenemy = 0;
		float enemyDistLimit   = 1000.0;
		float neutralDistLimit = 1000.0;

		nextenemy = sti(rSituation.MinEnemyIndex);
		enemydist = stf(rSituation.MinEnemyDistance);
		//if (DIRECTSAILDEBUG) trace("DirectsailCheck; next enemy: "+nextenemy + " dist: "+enemydist);
		if(nextenemy!= -1 && enemydist<enemyDistLimit )
		{
			//if (DIRECTSAILDEBUG) trace("Directsail aborted due to hostile ship, dist = " + enemydist);	// LDH - 07Jan09
			break;
		}
		// Jan 07, same for neutral ships
		nextenemy = sti(rSituation.MinNeutralIndex);
		enemydist = stf(rSituation.MinNeutralDistance);

		//if (DIRECTSAILDEBUG) trace("DirectsailCheck; next neutral ship: "+nextenemy + " dist: "+enemydist);
		if(nextenemy!= -1 && enemydist<neutralDistLimit && sti(Characters[nextenemy].ship.type) != SHIP_FORT ) // LDH added fort check 08Jan09
		{
		  //if (DIRECTSAILDEBUG) trace("Directsail aborted due to neutral ship, dist = " + enemydist);	// LDH added logit to trace - 07Jan09
		  break;
		}
		// looks like this doesn't always work, so I added another check for being in battle
		if(!bMapEnter) {
			//if (DIRECTSAILDEBUG) trace("Directsail aborted in battle");
			break;
		}		// LDH added logit to trace 07Jan09
		//#20171114-02 Tortuga pirate friendly
		sIslandID = sNewIslandId;
		//#20180813-02
		SetEventHandler("FaderEvent_StartFade", "ChangeSeaMapNew", 0);
        SetEventHandler("FaderEvent_EndFade", "Sea2Sea_Reload", 0);
        CreateEntity(&wdm_fader, "fader");
        float fadeOutTime = 0.5;
        SendMessage(&wdm_fader, "lfl", FADER_OUT, fadeOutTime, true);
        SendMessage(&wdm_fader, "l", FADER_STARTFRAME);
        //#20190118-01
        pchar.loadscreen = rndLoadingScreens(SEA2SEA_SCRN);
        SendMessage(&wdm_fader, "ls",FADER_PICTURE, pchar.loadscreen);
		//ChangeSeaMapNew(sNewIslandId);
		break;
	}
	if(bCheckEnc && userOptionsInt[UOPTION_INT_DISABLEWMTODIRSAIL] == 0) {
        //#20190625-01 If no island change, possible WM encounter
        checkWMEnctr(RTplayerShipX, RTplayerShipZ, scale, &isStorm, &isTornado, &distStorm);
        if(isStorm) {
            if(distStorm <= DIR_SAIL_STORM_DIST_REL) {
                bStorm = isStorm; //Defined in sea.c for Sea_Update
                bTornado = isTornado; //Defined in sea.c for Sea_Update
                WeatherParams.Tornado = isTornado;
                WeatherParams.Storm = true;
                iStormLockSeconds = 60;
                Seafoam.storm = "true";
                Whr_UpdateWeather(true);
                SetSchemeForSea();
            }
            else {
                WeatherParams.Rain.ThisDay = true;
                WeatherParams.Rain.StartTime = MakeInt(GetHour());
                WeatherParams.Rain.Duration = 75.0;
                Whr_UpdateWeather(true);
                if(isTornado) Log_SetStringToLog(xiStr("MSG_sea_13"));
                Log_SetStringToLog(xiStr("MSG_sea_14"));
            }
        }
	}
}
//#20190624-01
void getRTplayerShipXZ(ref RTplayerShipX, ref RTplayerShipZ, ref scale)
{
	float zeroX = MakeFloat(worldMap.zeroX);
	float SeaX = stf(pchar.Ship.Pos.x);
	float zeroZ = MakeFloat(worldMap.zeroZ);
	float SeaZ = stf(pchar.Ship.Pos.z);
	string sIsland = worldMap.island;

	scale = WDM_MAP_TO_SEA_SCALE;
	if (sIsland == "Cuba1" || sIsland == "Cuba2" || sIsland == "Beliz" || sIsland == "SantaCatalina"
		|| sIsland == "PortoBello" || sIsland == "Cartahena" || sIsland == "Maracaibo"
		|| sIsland == "Caracas" || sIsland == "Cumana")
	{
		scale = 25;
	}
	RTplayerShipX = (SeaX/scale) + zeroX;
	RTplayerShipZ = (SeaZ/scale) + zeroZ;
	return;
}

float getRTplayerShipAY()
{
	float RTplayerShipAY = stf(pchar.Ship.Ang.y);
	return RTplayerShipAY;
}
//#20190628-01
int getRTclosestIsland(float RTplayerShipX, float RTplayerShipZ, int curScale, ref iDistanceNow, ref scaleDiff)
{
	//if(DIRECTSAILDEBUG) trace("getRTclosestIsland. curr island: " + worldMap.island);

    //#20190624-01
	bool tempDiff;
	float distance, iBearing, offShip, shipAY, iX, iZ;
	iDistanceNow = 2500000000.0; //50000.0;
    scaleDiff = false;
	int nextisland = -1;
	ref rIsland;
	string islandTemp;
    shipAY = stf(pchar.Ship.Ang.y);
	for (int inum=0; inum<=iNumIslands; inum++)
	{
		rIsland = GetIslandByIndex(inum);//makeref(rIsland, Islands[inum]);
		islandTemp = rIsland.id;
		iX = stf(worldMap.islands.(islandTemp).position.x);
		iZ = stf(worldMap.islands.(islandTemp).position.z);

		iBearing = GetAngleY(iX - RTplayerShipX, iZ - RTplayerShipZ);
        offShip = iBearing - shipAY;
        int nBear = ClosestDirFA(offShip);
        if(nBear != DIR_FORWARD) continue; //Skip islands not heading toward

		//#20190628-01
		tempDiff = false;
		if(curScale == WDM_MAP_TO_SEA_SCALE) {
            if (islandTemp == "Cuba1" || islandTemp == "Cuba2" || islandTemp == "Beliz" || islandTemp == "SantaCatalina"
                || islandTemp == "PortoBello" || islandTemp == "Cartahena" || islandTemp == "Maracaibo"
                || islandTemp == "Caracas" || islandTemp == "Cumana")
            {
                tempDiff = true;
            }
		}
        //#20190628-01
		distance = GetDistance2DRel(RTplayerShipX, RTplayerShipZ, iX, iZ);

		//if(DIRECTSAILDEBUG) trace("getRTclosestIsland. islandTemp=" + islandTemp + ", distance=" + distance + ", iDistanceNow=" + iDistanceNow);

		if (distance < iDistanceNow)
		{
			iDistanceNow = distance
			nextisland = inum;
			scaleDiff = tempDiff;
		}
	}
	//if(DIRECTSAILDEBUG) trace("getRTclosestIsland. closest island: " + Islands[nextisland].id + ", idx=" + nextisland);

	return nextisland;
}
//#20180813-02
void ChangeSeaMapNew() //string sNewIslandId)
{
	//ShipsInit();
	DelEventHandler("FaderEvent_StartFade", "ChangeSeaMapNew");
    PauseAllSounds();
	DeleteSeaEnvironment();
	//SetEventHandler("Sea2Sea_Reload", "Sea2Sea_Reload", 0);
	//PostEvent("Sea2Sea_Reload", 0);
	LayerAddObject(SEA_REALIZE, &wdm_fader, -1);
}

void Sea2Sea_Reload()
{
    //#20180813-02
	//DelEventHandler("Sea2Sea_Reload", "Sea2Sea_Reload");
	DelEventHandler("FaderEvent_EndFade", "Sea2Sea_Reload");
    ReloadProgressStart();
	object seaLoginToSea;

	//#20190624-01
    float CX;
    float CZ;
    int scale = WDM_MAP_TO_SEA_SCALE;
    float fScrap;
    getRTplayerShipXZ(&CX, &CZ, &scale);

	float CAY = getRTplayerShipAY();
	//#20190628-01 //#20190628-01
	bool bScaleDiff;
	int nextisland = getRTclosestIsland(CX, CZ, scale, &fScrap, &bScaleDiff);
	ref rIsland = GetIslandByIndex(nextisland);
	string CIsland = rIsland.id;

	float ix = MakeFloat(worldMap.islands.(CIsland).position.x);
	float iz = MakeFloat(worldMap.islands.(CIsland).position.z);

	/*if (CIsland == "Cuba1" || CIsland == "Cuba2" || CIsland == "Beliz" || CIsland == "SantaCatalina"
		|| CIsland == "PortoBello" || CIsland == "Cartahena" || CIsland == "Maracaibo"
		|| CIsland == "Caracas" || CIsland == "Cumana")
	{
		scale = 25;
	}
	*/
	seaLoginToSea.playerGroup.x = (CX - ix)*scale;
	seaLoginToSea.playerGroup.z = (CZ - iz)*scale;
	seaLoginToSea.playerGroup.ay = CAY;
	seaLoginToSea.island = CIsland;

	seaLoginToSea.imageName = pchar.loadscreen;
    //musicName = "";
    DeleteAttribute(pchar, "pirateFriendly");
	SeaLogin(&seaLoginToSea);
	LayerAddObject(SEA_REALIZE, &wdm_fader, -1);
	ReloadProgressEnd();

	//#20190624-01
    float RTplayerShipX;
    float RTplayerShipZ;
    getRTplayerShipXZ(&RTplayerShipX, &RTplayerShipZ, &scale);
	worldMap.playerShipX = RTplayerShipX;
	worldMap.playerShipZ = RTplayerShipZ;
	worldMap.playerShipAY = getRTplayerShipAY();
	worldMap.island = rIsland.id;
	worldMap.zeroX = ix;
	worldMap.zeroZ = iz;
}
//#20190625-01 Storm, calls alert if ship encounter
void checkWMEnctr(float RTplayerShipX, float RTplayerShipZ, int scale, ref _bStorm, ref _bTornado, ref distStorm)
{
    _bStorm = false;
    distStorm = 0.0;

    if(!bMapEnter)
        return;

    int i;
    ref rSGroup;
    aref WME;
    makearef(WME, worldmap.encounters);
    aref rRawGroup, encDataForSlot, fndGroup;
    int nWME = GetAttributesNum(WME);
    int iNumWarShips = 0;
    int iNumMerchantShips = 0;
    int nShipCnt = 0;
    bool bFound = false;
    float fScale = makefloat(scale);
    float x, z, ay, dist, xs, zs, dists;
    string encID = "";
    _bTornado = false;

    RTplayerShipX *= fScale;
    RTplayerShipZ *= fScale;

    for(i = 0; i < nWME, i++)
    {
        if(_bStorm && bFound) break;
        rRawGroup = GetAttributeN(WME, i);
        if(!CheckAttribute(rRawGroup, "type")) continue;
        if(CheckAttribute(rRawGroup, "needDelete")) continue;
        //#20191123-03
        if(CheckAttribute(rRawGroup, "dirSailEnc")) continue;
        if(rRawGroup.type == "Storm") {
            if(_bStorm) continue;
            xs = stf(rRawGroup.px) * fScale;
            zs = stf(rRawGroup.pz) * fScale;
            dists = GetDistance2DRel(RTplayerShipX, RTplayerShipZ, xs, zs);
            if(dists > DIR_SAIL_STORMWARN_DIST_REL) continue;
            //To do: Loop cloud attributes for intersect
            //if(CheckIntersection(RTplayerShipX, RTplayerShipZ, playerShipActionRadius)) {
                _bStorm = true;
                distStorm = dists;
                if(CheckAttribute(rRawGroup, "isTornado") && sti(rRawGroup.isTornado) != 0)
                    _bTornado = true;
                if(dists <= DIR_SAIL_STORM_DIST_REL)
                    rRawGroup.needDelete = "Reload delete storm encounter";
            //}
        }
        else {
            if(bFound) continue

            if(!CheckAttribute(rRawGroup, "encdata")) continue;

            encID = ""; //Bit of a hack: for some reason encID loses its 'string' type without an assignment
            encID = GetAttributeName(rRawGroup);
            makearef(encDataForSlot, rRawGroup.encdata);
            //#20191015-01
            //Quest encounters prefaced with "Sea_" check for already loaded (see DirSailEnc.c)
            rSGroup = Group_GetGroupByID("Sea_" + encDataForSlot.GroupName);
            if (CheckAttribute(rSGroup, "AlreadyLoaded")) continue;

            nShipCnt = iNumShips;

            if(CheckAttribute(encDataForSlot, "NumMerchantShips"))
            {
                iNumMerchantShips = sti(encDataForSlot.NumMerchantShips);
                nShipCnt += iNumMerchantShips;
            }
            else {
                iNumMerchantShips = 0;
            }
            if(CheckAttribute(encDataForSlot, "NumWarShips"))
            {
                iNumWarShips = sti(encDataForSlot.NumWarShips);
                nShipCnt += iNumWarShips;
            }
            else {
                iNumWarShips = 0;
            }
            if(nShipCnt > MAX_SHIPS_IN_LOCATION) continue;
            x = stf(rRawGroup.x) * fScale;
            z = stf(rRawGroup.z) * fScale;
            ay = stf(rRawGroup.ay);
            dist = GetDistance2DRel(RTplayerShipX, RTplayerShipZ, x, z);

            if(dist > DIRECTENCOUNTERRANDDISTANCE)
                continue;

            fndGroup = rRawGroup;
            bFound = true;
        }
    }
    if(!bFound || encID == "")
        return;

    rRawGroup = fndGroup;
    LaunchDirSailEnc();
    //extern in DirSailEnc.c
    evtDirSail(rRawGroup, encDataForSlot, iNumMerchantShips, iNumWarShips, RTplayerShipX, RTplayerShipZ, x, ay, z, dist, fScale, encID);

    return;
}

//Returns #define DIR_XXX
int ClosestDirE(float dir)
{
    //float aX[8] = {-0.5,0.0,0.5,1.0,-1.0,-0.5,0.0,0.5};
    //float aZ[8] = {1.0,1.0,1.0,0.0,0.0,-1.0,-1.0,-1.0};
    float aX[8];
    float aZ[8];
    //Bug in compiler...array inits don't work in .c files
    aX[0] = -0.5; aX[1] = 0.0; aX[2] = 0.5; aX[3] = 1.0; aX[4] = -1.0; aX[5] = -0.5; aX[6] = 0.0; aX[7] = 0.5;
    aZ[0] = 1.0; aZ[1] = 1.0; aZ[2] = 1.0; aZ[3] = 0.0; aZ[4] = 0.0; aZ[5] = -1.0; aZ[6] = -1.0; aZ[7] = -1.0;

    float maxDot = -99999999.0;
    int nRet = 0;
    for(int i = 0; i < 8; i++) {
        float v = GetAngleY(aX[i], aZ[i]);
        float t = GetDotProduct(v, dir);
        if (t > maxDot) {
             //ret = dir;
             nRet = i + 1;
             maxDot = t;
         }
    }
    return nRet;
}

int ClosestDirFA(float dir)
{
    float aX[6];
    float aZ[6];
    //Bug in compiler...array inits don't work in .c files
    aX[0] = -1.0; aX[1] = 0.0; aX[2] = 1.0; aX[3] = -1.0; aX[4] = 0.0; aX[5] = 1.0;
    aZ[0] = 0.5; aZ[1] = 1.0; aZ[2] = 0.5; aZ[3] = 0.0; aZ[4] = -1.0; aZ[5] = 0.0;

    float maxDot = -99999999.0;
    int nRet = 0;
    for(int i = 0; i < 6; i++) {
        float v = GetAngleY(aX[i], aZ[i]);
        float t = GetDotProduct(v, dir);
        if (t > maxDot) {
             nRet = i + 1;
             maxDot = t;
         }
    }
    if(nRet < 4)
        nRet = DIR_FORWARD;
    else
        nRet = DIR_ABAFT;

    return nRet;
}
