public bool Cross(DataBallState comeFrom, ref DataBallState resultBounce)
    {
        var increment = comeFrom.Speed * comeFrom.DeltaTime;
        var next      = comeFrom.Position + increment;
        var paddlePos = Composition.ControllerSim.GetLocalPaddleReflectorPos();
        var bounce    = paddlePos.y + Composition.ControllerSim.BallRadius;
        // positives
        var distanceNoBounce = comeFrom.Position.y - next.y;
        var distanceBounce   = comeFrom.Position.y - bounce;

        distanceNoBounce = distanceNoBounce > 0f ? distanceNoBounce : 0f;
        distanceBounce   = distanceBounce > 0f ? distanceBounce : 0f;
        if (distanceBounce < distanceNoBounce)
        {
            // no zero here, cause: zero is vertical movement (both are positives and is upper)
            var factor    = distanceBounce / distanceNoBounce;
            var bouncePos = new Vector2(
                comeFrom.Position.x + increment.x * factor,
                comeFrom.Position.y + distanceBounce);
            resultBounce.Position = bouncePos;
            resultBounce.Speed    = new Vector2(
                comeFrom.Speed.x,
                -comeFrom.Speed.y);
            resultBounce.DeltaTime = comeFrom.DeltaTime / (1f - factor);

            Debug.Log($"bouncing {GetType().Name}");

            return(true);
        }

        return(false);
    }
    public bool Cross(DataBallState comeFrom, ref DataBallState resultBounce)
    {
        var increment = comeFrom.Speed * comeFrom.DeltaTime;
        var next      = comeFrom.Position + increment;
        var bounce    = -.5f * Composition.DataMeta.CourtRatio + Composition.ControllerSim.BallRadius;
        // positives
        var distanceNoBounce = comeFrom.Position.x - next.x;
        var distanceBounce   = comeFrom.Position.x - bounce;

        distanceNoBounce = distanceNoBounce > 0f ? distanceNoBounce : 0f;
        distanceBounce   = distanceBounce > 0f ? distanceBounce : 0f;
        if (distanceBounce < distanceNoBounce)
        {
            // no zero here, cause: zero is vertical movement (both are positives and is upper)
            var factor = distanceBounce / distanceNoBounce;
            resultBounce.Position = new Vector2(
                comeFrom.Position.x - distanceBounce,
                comeFrom.Position.y + increment.y * factor);
            resultBounce.Speed = new Vector2(
                -comeFrom.Speed.x,
                comeFrom.Speed.y);
            resultBounce.DeltaTime = comeFrom.DeltaTime / (1f - factor);

            Debug.Log($"bouncing {GetType().Name}");

            return(true);
        }

        return(false);
    }
    private bool FindBounce(ref DataBallState bounce, IObstacle[] obstacles)
    {
        var index = 0;

        for (; index < obstacles.Length && !obstacles[index].Cross(bounce, ref bounce); index++)
        {
        }
        return(index != obstacles.Length);
    }
    public bool Bounce(DataBallState comeFrom, ref DataBallState resultBounce)
    {
        var increment = comeFrom.Speed * comeFrom.DeltaTime;
        var next      = comeFrom.Position + increment;

        resultBounce.Position  = next;
        resultBounce.Speed     = comeFrom.Speed;
        resultBounce.DeltaTime = 0f;

        Debug.Log($"bouncing {GetType().Name}");

        return(true);
    }
    public bool Bounce(DataBallState comeFrom, ref DataBallState resultBounce)
    {
        var increment = comeFrom.Speed * comeFrom.DeltaTime;
        var next      = comeFrom.Position + increment;
        var paddlePos = Composition.ControllerSim.GetRemotePaddleReflectorPos();
        var bounce    = paddlePos.y - Composition.ControllerSim.BallRadius;
        // positives
        var distanceNoBounce = next.y - comeFrom.Position.y;
        var distanceBounce   = bounce - comeFrom.Position.y;

        distanceNoBounce = distanceNoBounce > 0f ? distanceNoBounce : 0f;
        distanceBounce   = distanceBounce > 0f ? distanceBounce : 0f;
        if (distanceBounce < distanceNoBounce)
        {
            // no zero here, cause: zero is vertical movement (both are positives and is upper)
            var factor = distanceBounce / distanceNoBounce;
            // more resources
            //var bouncePos = comeFrom.Position + increment * factor;
            var bouncePos = new Vector2(
                comeFrom.Position.x + increment.x * factor,
                comeFrom.Position.y + distanceBounce);
            // more resources
            //var paddleBoundDistance = (paddlePos - bouncePos).magnitude;
            var paddleBoundDistance = bouncePos.x > paddlePos.x
                                ? bouncePos.x - paddlePos.x
                                : paddlePos.x - bouncePos.x;

            if (paddleBoundDistance < Composition.ControllerSim.Frame.PaddleRemoteReflectorHalfSize)
            {
                resultBounce.Position = bouncePos;
                resultBounce.Speed    = new Vector2(
                    comeFrom.Speed.x,
                    -comeFrom.Speed.y);
                resultBounce.DeltaTime = comeFrom.DeltaTime * (1f - factor);

                //var screenBouncePos = ((Vector3)bouncePos + Vector3.up * Composition.ControllerSim.BallRadius) * Composition.ModelScaleFactor;
                //var screenPaddlePos = (Vector3)paddlePos * Composition.ModelScaleFactor;
                //Debug.DrawLine(screenPaddlePos, screenBouncePos, Color.magenta, 20f);

                Debug.Log($"bouncing {GetType().Name}");

                return(true);
            }
        }

        return(false);
    }
    public ControllerSimPhase(DataFrameState stateFrame, DataBallState stateBall, IObstacle[] obstacles)
    {
        Debug.Log("predicting");

        var maxPath      = Mathf.Sqrt(1f + Composition.DataMeta.CourtRatio) * 2f;
        var currentSpeed = stateBall.Speed.magnitude;

        stateBall.DeltaTime = maxPath / currentSpeed;
        var bounce = stateBall;

        if (FindBounce(ref bounce, obstacles))
        {
            _timeTarget      = DateTime.FromBinary(stateFrame.Time) + TimeSpan.FromSeconds(bounce.DeltaTime);
            _targetBallPos   = bounce.Position;
            _targetBallSpeed = bounce.Speed;

            Debug.DrawLine(_targetBallPos, stateBall.Position, Color.red, 20f);
        }
        else
        {
            throw new Exception("undef");
        }
    }
    public void Update(IControllerInput controllerInput)
    {
        DataFrameState lastRemote;

        lock (_framesRemote)
        {
            lastRemote = _framesRemote[_indexCurrent];
        }

        var paddleLocalMovement = controllerInput.IsMovingLeft ? -1f : 0f;

        paddleLocalMovement += controllerInput.IsMovingRight ? 1f : 0f;

        var leftBound  = -.5f * Composition.DataMeta.CourtRatio;
        var rightBound = .5f * Composition.DataMeta.CourtRatio;

        var lastLocalDelta = _frame.Time;

        Composition.NetState.SetTimeStamp(ref _frame);
        var bufferedDeltaTime = (float)(DateTime.FromBinary(_frame.Time) - DateTime.FromBinary(lastRemote.Time)).TotalSeconds;
        var simDeltaTime      = (float)(DateTime.FromBinary(_frame.Time) - DateTime.FromBinary(lastLocalDelta)).TotalSeconds;

        // update paddle local
        _frame.PaddleLocalSpeed     = Composition.DataMeta.PaddleSpeed * paddleLocalMovement;
        _frame.PaddleLocalPosition += Frame.PaddleLocalSpeed * simDeltaTime;
        var leftPaddleLocalBound  = _frame.PaddleLocalPosition - _frame.PaddleLocalReflectorHalfSize;
        var rightPaddleLocalBound = _frame.PaddleLocalPosition + _frame.PaddleLocalReflectorHalfSize;

        // limit
        _frame.PaddleLocalPosition = leftBound > leftPaddleLocalBound
                        ? leftBound + _frame.PaddleLocalReflectorHalfSize
                        : Frame.PaddleLocalPosition;
        _frame.PaddleLocalPosition = rightBound < rightPaddleLocalBound
                        ? rightBound - _frame.PaddleLocalReflectorHalfSize
                        : Frame.PaddleLocalPosition;

        // update paddle remote
        _frame.PaddleRemoteSpeed     = lastRemote.PaddleRemoteSpeed;
        _frame.PaddleRemotePosition  = lastRemote.PaddleRemotePosition;
        _frame.PaddleRemotePosition += lastRemote.PaddleRemoteSpeed * bufferedDeltaTime;
        var leftPaddleRemoteBound  = _frame.PaddleRemotePosition - _frame.PaddleRemoteReflectorHalfSize;
        var rightPaddleRemoteBound = _frame.PaddleRemotePosition + _frame.PaddleRemoteReflectorHalfSize;

        // limit
        _frame.PaddleRemotePosition = leftBound > leftPaddleRemoteBound
                        ? leftBound + _frame.PaddleRemoteReflectorHalfSize
                        : Frame.PaddleRemotePosition;
        _frame.PaddleRemotePosition = rightBound < rightPaddleRemoteBound
                        ? rightBound - _frame.PaddleRemoteReflectorHalfSize
                        : Frame.PaddleRemotePosition;

        // update ball
        var ballSpeed    = new Vector2(Frame.BallSpeedX, Frame.BallSpeedY);
        var ballPosition = new Vector2(Frame.BallPositionX, Frame.BallPositionY);

        // debug
        if (simDeltaTime < bufferedDeltaTime)
        {
            ((Vector3)ballPosition * Composition.ModelScaleFactor).DebugDrawPoint(Color.yellow, .01f, 20f);
        }

        // - init bounce calc buffers
        _bouncedTotal   = 0;
        _bounced        = _bounced ?? new IObstacle[_bouncers.Length];
        _frameBallTrail = _frameBallTrail ?? new DataBallState[_bouncers.Length + 1];
        _frameBallTrail[_bouncedTotal] = new DataBallState
        {
            Position  = ballPosition,
            Speed     = ballSpeed,
            DeltaTime = simDeltaTime,
        };

        // - calc bounce
        for (var index = 0; index < _bouncers.Length; index++)
        {
            // exclude obstacle been bounced
            var indexBounced = 0;
            for (; indexBounced < _bouncedTotal; indexBounced++)
            {
                if (ReferenceEquals(_bouncers[index], _bounced[indexBounced]))
                {
                    break;
                }
            }
            if (indexBounced != _bouncedTotal)
            {
                continue;
            }

            // check bounce (default should the last one)
            if (_bouncers[index].Bounce(_frameBallTrail[_bouncedTotal], ref _frameBallTrail[_bouncedTotal + 1]))
            {
                _bounced[_bouncedTotal] = _bouncers[index];
                index = 0;
                _bouncedTotal++;
            }
        }

        // - calc out, with default move
        //for(var index = 0; index < _finishers.Length; index++)
        //{
        //	if(_finishers[index].Bounce(_frameBallTrail[indexTrail], ref _frameBallTrail[indexTrail + 1]))
        //	{
        //		indexTrail++;
        //	}
        //}

        _frame.BallPositionX = _frameBallTrail[_bouncedTotal].Position.x;
        _frame.BallPositionY = _frameBallTrail[_bouncedTotal].Position.y;
        _frame.BallSpeedX    = _frameBallTrail[_bouncedTotal].Speed.x;
        _frame.BallSpeedY    = _frameBallTrail[_bouncedTotal].Speed.y;

        //_phase =
        //	_phase ??
        //	new ControllerSimPhase(
        //		_frame,
        //		_frameBallTrail[_bouncedTotal],
        //		_bouncers);
        //if(!_phase.ApplyCorrection(ref _frame))
        //{
        //	_phase = new ControllerSimPhase(
        //		_frame,
        //		_frameBallTrail[_bouncedTotal],
        //		_bouncers);
        //}
    }
 public bool Cross(DataBallState comeFrom, ref DataBallState resultBounce)
 {
     return(Bounce(comeFrom, ref resultBounce));
 }