const float RAD2DEG = 57.29578049f;
const float ACCELERATE = 14.0f;
const float FULLSPEED = 8.0f;
const float STOPSPEED = FULLSPEED * 0.67f;
const float DELTATIME = 1.0f / 60.0f;
const float FRICTION = 4.0f;
kVec3 UP = kVec3(0,0,1);

/*#define BC_ATTACK = 1 << 0
#define BC_JUMP = 1 << 1
#define BC_FORWARD = 1 << 2
#define BC_BACKWARD = 1 << 3
#define BC_STRAFELEFT = 1 << 4
#define BC_STRAFERIGHT = 1 << 5
//#define BC_CROUCH = 1 << 6 (Not defined for some reason)
#define BC_ALTFIRE = 1 << 7
#define BC_SCOPEZOOMIN = 1 << 8
#define BC_SCOPEZOOMOUT = 1 << 9*/

kVec3 Cross(kVec3 lhs, kVec3 rhs)
{
    return kVec3(lhs.y * rhs.z - rhs.y * lhs.z,
                lhs.z * rhs.x - rhs.z * lhs.x,
                lhs.x * rhs.y - rhs.x * lhs.y);
}

kVec3 fwdYaw(float& in Yaw)
{
    float x,y,z;
    y = Math::Cos(Yaw);
    x = Math::Sin(Yaw);
    z = 0;
    return kVec3(x, y, z);
}

kVec3 Normalized(kVec3 lhs)
{
    if (lhs.Unit() != 0) {
        return lhs / lhs.Unit();
    }
    return kVec3(0,0,0);
}

kVec3 PlayerFriction(kVec3 velocity, float currentSpeed)
{
    if (currentSpeed == 0) return velocity;
    float drop = (currentSpeed < STOPSPEED ? STOPSPEED : currentSpeed) * FRICTION * DELTATIME;
    float newSpeed = currentSpeed - drop;
    newSpeed = newSpeed < 0 ? 0 : newSpeed;

    newSpeed = newSpeed / currentSpeed;

    return velocity * newSpeed;
}

kVec3 PlayerAccelerate(kVec3 velocity, kVec3 wishDir, float wishSpeed)
{
    float currentSpeed = velocity.Dot(wishDir);
    float addSpeed = wishSpeed - currentSpeed;

    if (addSpeed <= 0) return velocity;

    float accelSpeed = ACCELERATE * DELTATIME * wishSpeed;

    accelSpeed = accelSpeed > addSpeed ? addSpeed : accelSpeed;

    kVec3 addVel = wishDir * accelSpeed;
    velocity = velocity + addVel;

    return velocity;
}

class QuakePlayer : ScriptObject
{
    kActor@ self;
    kPlayer player;
    kVec3 lastPosition = kVec3(0,0,0);
    kVec3 vel = kVec3(0,0,0);
    int jumpedOnTick = -1;
    float jumpSpeed = 0;

    QuakePlayer(kActor@ actor)
    {
        @self = actor;
    }

    void OnTick(void) 
    {
        if (self.MovementComponent().Flags() & MCF_PROCESS_MOVEMENT != 0) return;

        self.Flags() |= AF_CASTSHADOW;

        player = self.CastToPuppet().PlayerOwner();

        /*self.MovementComponent().Friction() = 0;
        self.MovementComponent().AirFriction() = 0;
        self.MovementComponent().WaterFriction() = 0;*/

        float currentSpeed = vel.Unit();

        float v = 0;
        float h = 0;
        bool j = false;

        if (player.Buttons() & BC_FORWARD != 0) v += 1;
        if (player.Buttons() & BC_BACKWARD != 0) v -= 1;
        if (player.Buttons() & BC_STRAFERIGHT != 0) h += 1;
        if (player.Buttons() & BC_STRAFELEFT != 0) h -= 1;
        if (player.Buttons() & BC_JUMP != 0) j = true;

        kVec3 fwd = fwdYaw(self.Yaw());
        kVec3 rgh = fwd.Cross(UP);

        fwd *= v;
        rgh *= h;

        kVec3 wishDir = Normalized(fwd + rgh);
        kVec3 grav = UP * -1 * self.MovementComponent().Gravity() * self.MovementComponent().Mass();

        if (player.Actor().PlayerFlags() & PF_DEAD != 0) {
            jumpedOnTick = -1;
            j = false;
            return;
        }

        if (self.MovementComponent().Flags() & MCF_UNDERWATER  != 0
        || self.WorldComponent().Flags() & WCF_ON_WATER != 0
        || self.WorldComponent().Flags() & WCF_UNDER_WATER != 0
        || self.WorldComponent().Flags() & WCF_ABOVE_WATER != 0
        || self.WorldComponent().Flags() & WCF_IN_ANTIGRAVITY != 0
        ) {
            return;
        }
        if (self.MovementComponent().Flags() & MCF_FALLING == 0) {
            jumpedOnTick = -1;
            jumpSpeed = 0;
            grav = kVec3(0,0,0);
        }
        if (j) if (jumpedOnTick == -1) jumpedOnTick = self.GameTicks();
        if (self.GameTicks() - jumpedOnTick < 4 && j) {
            if (jumpSpeed == 0) jumpSpeed = 9.5744f;
            else jumpSpeed += 2.93115f;
            if (jumpSpeed <= 21.2992f) vel += UP * jumpSpeed;
        }

        vel = PlayerFriction(vel, currentSpeed);
        vel = PlayerAccelerate(vel, wishDir, FULLSPEED);
        
        self.MovementComponent().Velocity() = vel + grav;

        lastPosition = self.Origin();
    }

    void OnLevelLoad(kDictMem@ pDict) 
    {
        lastPosition = self.Origin();
        player = self.CastToPuppet().PlayerOwner();
    }

    void OnSpawn()
    {
        lastPosition = self.Origin();
        player = self.CastToPuppet().PlayerOwner();
    }


}



