class TouchVelChange : public RewardFunction {
public:
	Vec last_vel;
	Vec curr_vel;
	

	static constexpr float MAX_EXTRAP_Z = CommonValues::GOAL_HEIGHT + 230.f;
	static constexpr float MIN_SCORE_TIME = 0.9f;
	static constexpr float MIN_TOUCH_VEL_CHANGE = 600.0f;
	static constexpr float MIN_TOUCH_HEIGHT = 200.0f;

	// Constructor takes exp as a parameter
	TouchVelChange()
		: last_vel(Vec(0, 0, 0)), curr_vel(Vec(0, 0, 0)),
		SCALE(SCALE), div(std::pow(CommonValues::CEILING_Z / 2 - CommonValues::BALL_RADIUS, SCALE)) {}

	virtual void Reset(const GameState& initialState) override {
		last_vel = initialState.ball.vel;
		curr_vel = initialState.ball.vel;
	}

	virtual void PreStep(const GameState& state) override {
		last_vel = curr_vel;
		curr_vel = state.ball.vel;
	}


	virtual float GetReward(const PlayerData& player, const GameState& state, const Action& prevAction) override {
		float reward = 0.0f;
		if (player.ballTouchedStep) {
			float VEL_DIFF = ((last_vel - state.ball.vel).Length()) / 4600;
			float TOTAL_BALL_VELOCITY = state.ball.vel.Length() / 6000;

			if (MIN_TOUCH_VEL_CHANGE  < VEL_DIFF || MIN_TOUCH_HEIGHT < state.ball.pos.z) {

				float avg_height = (player.phys.pos.z + state.ball.pos.z) / 2.0f;
				float h0 = HeightActivation(0);
				float h1 = HeightActivation(CommonValues::CEILING_Z);
				float hx = HeightActivation(avg_height);
				float height_factor = std::pow((hx - h0) / (h1 - h0), 2);

				float wall_dist_factor = 1.0f - std::exp(-DistToClosestWall(player.phys.pos.x, player.phys.pos.y) / CommonValues::CAR_MAX_SPEED);

				HEIGHT_FACTOR = height_factor * (1.0f + wall_dist_factor);
				RG_LOG(HEIGHT_FACTOR);
				float REW = (VEL_DIFF + (TOTAL_BALL_VELOCITY * 0.2)) + (HEIGHT_FACTOR * 4);
				//RG_LOG(REW);
				reward = REW;
			}
			else {
				reward = (VEL_DIFF / 4600.0f) * (std::pow(std::min(state.ball.pos[2], CommonValues::CEILING_Z / 2) - CommonValues::BALL_RADIUS, SCALE) / div);
			};
			//RS_LOG(vel_difference);
			
		}

		// Additional reward logic based on extrapolated position
		float goalY = RLConst::SOCCAR_GOAL_SCORE_BASE_THRESHOLD_Y * (player.team == Team::BLUE ? 1 : -1);
		float distToGoalY = std::abs(state.ball.pos.y - goalY);
		float extrapScoreTime = distToGoalY / RS_MAX(std::abs(state.ball.vel.y), 0.01f);
		Vec extrapScorePos = state.ball.pos + state.ball.vel * extrapScoreTime;
		extrapScorePos.z += CommonValues::GRAVITY_Z * (extrapScoreTime * extrapScoreTime) / 2;

		if (extrapScoreTime <= MIN_SCORE_TIME || extrapScorePos.z >= MAX_EXTRAP_Z) {
			reward = 0.0f; // Disable reward if the ball is about to score or go too high
		}

		return reward;
	}
private:

	float div;
	float SCALE = 1.0f;  
	float HEIGHT_FACTOR;
	

	

	static float HeightActivation(float z) {
		return std::cbrt((z - 150.0f) / CommonValues::CEILING_Z);
	}

	static float DistToClosestWall(float x, float y) {
		float dist_side_wall = std::abs(4096.0f - std::abs(x));
		float dist_back_wall = std::abs(5120.0f - std::abs(y));

		// Corner calculation
		float x1 = 4096.0f - 1152.0f, y1 = 5120.0f, x2 = 4096.0f, y2 = 5120.0f - 1152.0f;
		float A = std::abs(x) - x1;
		float B = std::abs(y) - y1;
		float C = x2 - x1;
		float D = y2 - y1;

		float dot = A * C + B * D;
		float len_sq = C * C + D * D;
		float param = (len_sq != 0) ? dot / len_sq : -1;

		float xx, yy;
		if (param < 0) {
			xx = x1;
			yy = y1;
		}
		else if (param > 1) {
			xx = x2;
			yy = y2;
		}
		else {
			xx = x1 + param * C;
			yy = y1 + param * D;
		}

		float dx = std::abs(x) - xx;
		float dy = std::abs(y) - yy;
		float dist_corner_wall = std::sqrt(dx * dx + dy * dy);

		return std::min({ dist_side_wall, dist_back_wall, dist_corner_wall });
	}
};
	 calculate the max height factor nmber possible Floor: 0
Center field: (0, 0)
Side wall: x=±4096
Side wall length: 7936
Back wall: y=±5120
Back wall length: 5888
Ceiling: z=2044