/**
 * Update signals at segments that are at both ends of
 * given (existent or non-existent) track
 *
 * @see UpdateSignalsInBuffer()
 * @param tile tile where we start
 * @param track track at which ends we will update signals
 * @param owner owner whose signals we will update
 */
void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
{
	assert(_globset.IsEmpty());

	AddTrackToSignalBuffer(tile, track, owner);
	UpdateSignalsInBuffer(owner);
}
/**
 * Update signals, starting at one side of a tile
 * Will check tile next to this at opposite side too
 *
 * @see UpdateSignalsInBuffer()
 * @param tile tile where we start
 * @param side side of tile
 * @param owner owner whose signals we will update
 * @return the state of the signal segment
 */
SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
{
	assert(_globset.IsEmpty());
	_globset.Add(tile, side);

	return UpdateSignalsInBuffer(owner);
}
/**
 * Update signals in buffer
 * Called from 'outside'
 */
void UpdateSignalsInBuffer()
{
	if (!_globset.IsEmpty()) {
		UpdateSignalsInBuffer(_last_owner);
		_last_owner = INVALID_OWNER; // invalidate
	}
}
/**
 * Add side of tile to signal update buffer
 *
 * @param tile tile where we start
 * @param side side of tile
 * @param owner owner whose signals we will update
 */
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
{
	/* do not allow signal updates for two companies in one run */
	assert(_globset.IsEmpty() || owner == _last_owner);

	_last_owner = owner;

	_globset.Add(tile, side);

	if (_globset.Items() >= SIG_GLOB_UPDATE) {
		/* too many items, force update */
		UpdateSignalsInBuffer(_last_owner);
		_last_owner = INVALID_OWNER;
	}
}
/**
 * Add track to signal update buffer
 *
 * @param tile tile where we start
 * @param track track at which ends we will update signals
 * @param owner owner whose signals we will update
 */
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
{
	static const DiagDirection _search_dir_1[] = {
		DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
	};
	static const DiagDirection _search_dir_2[] = {
		DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
	};

	/* do not allow signal updates for two companies in one run */
	assert(_globset.IsEmpty() || owner == _last_owner);

	_last_owner = owner;

	_globset.Add(tile, _search_dir_1[track]);
	_globset.Add(tile, _search_dir_2[track]);

	if (_globset.Items() >= SIG_GLOB_UPDATE) {
		/* too many items, force update */
		UpdateSignalsInBuffer(_last_owner);
		_last_owner = INVALID_OWNER;
	}
}
/**
 * Updates blocks in _globset buffer
 *
 * @param owner company whose signals we are updating
 * @return state of the first block from _globset
 * @pre Company::IsValidID(owner)
 */
static SigSegState UpdateSignalsInBuffer(Owner owner)
{
	assert(Company::IsValidID(owner));

	bool first = true;  // first block?
	SigSegState state = SIGSEG_FREE; // value to return

	TileIndex tile;
	DiagDirection dir;

	while (_globset.Get(&tile, &dir)) {
		assert(_tbuset.IsEmpty());
		assert(_tbdset.IsEmpty());

		/* After updating signal, data stored are always MP_RAILWAY with signals.
		 * Other situations happen when data are from outside functions -
		 * modification of railbits (including both rail building and removal),
		 * train entering/leaving block, train leaving depot...
		 */
		switch (GetTileType(tile)) {
			case MP_TUNNELBRIDGE:
				/* 'optimization assert' - do not try to update signals when it is not needed */
				assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL);
				assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile)));
				_tbdset.Add(tile, INVALID_DIAGDIR);  // we can safely start from wormhole centre
				_tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR);
				break;

			case MP_RAILWAY:
				if (IsRailDepot(tile)) {
					/* 'optimization assert' do not try to update signals in other cases */
					assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
					_tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside
					break;
				}
				/* FALL THROUGH */
			case MP_STATION:
			case MP_ROAD:
				if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
					/* only add to set when there is some 'interesting' track */
					_tbdset.Add(tile, dir);
					_tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir));
					break;
				}
				/* FALL THROUGH */
			default:
				/* jump to next tile */
				tile = tile + TileOffsByDiagDir(dir);
				dir = ReverseDiagDir(dir);
				if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) {
					_tbdset.Add(tile, dir);
					break;
				}
				/* happens when removing a rail that wasn't connected at one or both sides */
				continue; // continue the while() loop
		}

		assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items
		assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too

		SigFlags flags = ExploreSegment(owner);

		if (first) {
			first = false;
			/* SIGSEG_FREE is set by default */
			if (flags & SF_PBS) {
				state = SIGSEG_PBS;
			} else if ((flags & SF_TRAIN) || ((flags & SF_EXIT) && !(flags & SF_GREEN)) || (flags & SF_FULL)) {
				state = SIGSEG_FULL;
			}
		}

		/* do not do anything when some buffer was full */
		if (flags & SF_FULL) {
			ResetSets(); // free all sets
			break;
		}

		UpdateSignalsAroundSegment(flags);
	}

	return state;
}