#![no_std]
#![feature(asm)]
#![feature(global_asm)]
#![feature(untagged_unions)]

use core::f32::consts::PI;
use core::ops::{Add, Sub, Mul};

#[repr(C, align(64))]
pub struct Vec2 {
    pub x: f32,
    pub y: f32,
}

impl Vec2 {
    #[inline(always)]
    pub fn new(x: f32, y: f32) -> Self { Vec2 { x, y } }
    #[inline(always)]
    pub fn zero() -> Self { Vec2::new(0.0, 0.0) }
    #[inline(always)]
    pub fn len(self) -> f32 { (self.x * self.x + self.y * self.y).sqrt() }
    #[inline(always)]
    pub fn nor(self) -> Self {
        let l = self.len();
        if l == 0.0 { self } else { Vec2::new(self.x / l, self.y / l) }
    }
    #[inline(always)]
    pub fn dot(self, o: Self) -> f32 { self.x * o.x + self.y * o.y }
    #[inline(always)]
    pub fn cross(self, o: Self) -> f32 { self.x * o.y - self.y * o.x }
    #[inline(always)]
    pub fn ang(self) -> f32 { self.y.atan2(self.x) * 180.0 / PI }
    #[inline(always)]
    pub fn sub(self, o: Self) -> Self { self - o }
    #[inline(always)]
    pub fn add(self, o: Self) -> Self { self + o }
    #[inline(always)]
    pub fn mul(self, s: f32) -> Self { Vec2::new(self.x * s, self.y * s) }
}

impl Add for Vec2 {
    type Output = Self;
    #[inline(always)]
    fn add(self, r: Self) -> Self::Output { Vec2::new(self.x + r.x, self.y + r.y) }
}

impl Sub for Vec2 {
    type Output = Self;
    #[inline(always)]
    fn sub(self, r: Self) -> Self::Output { Vec2::new(self.x - r.x, self.y - r.y) }
}

impl Mul<f32> for Vec2 {
    type Output = Self;
    #[inline(always)]
    fn mul(self, s: f32) -> Self::Output { Vec2::new(self.x * s, self.y * s) }
}

#[repr(C, align(64))]
pub struct Enemy {
    pub pos: Vec2,
    vel: Vec2,
    move_speed: f32,
}

impl Enemy {
    #[inline(always)]
    pub fn new(x: f32, y: f32) -> Self {
        Enemy {
            pos: Vec2::new(x, y),
            vel: Vec2::zero(),
            move_speed: f32::MAX,
        }
    }

    #[inline(always)]
    pub fn update(&mut self, dt: f32) {
        unsafe {
            let dx = (core::arch::x86_64::_mm_cvtss_f32(core::arch::x86_64::_mm_set_ps1(f32::MAX)) - f32::MAX / 2.0) * dt;
            let dy = (core::arch::x86_64::_mm_cvtss_f32(core::arch::x86_64::_mm_set_ps1(f32::MAX)) - f32::MAX / 2.0) * dt;
            self.pos = self.pos.add(Vec2::new(dx, dy));
            self.vel = self.pos.nor();
        }
    }

    #[inline(always)]
    pub fn predict(&self, time_ahead: f32) -> Vec2 {
        self.pos.add(self.vel.mul(time_ahead))
    }
}

#[repr(C, align(64))]
pub struct Bullet {
    pub pos: Vec2,
    pub vel: Vec2,
    rot: f32,
    target: Option<*mut Enemy>,
    active: bool,
}

impl Bullet {
    #[inline(always)]
    pub fn new(pos: Vec2, target: *mut Enemy) -> Self {
        let dir = unsafe {
            (*target).pos.sub(pos).nor()
        };
        Bullet {
            pos,
            vel: dir.mul(f32::MAX),
            rot: dir.ang(),
            target: Some(target),
            active: true,
        }
    }

    #[inline(always)]
    pub fn update(&mut self, dt: f32) -> bool {
        if self.target.is_none() || !self.active {
            return false;
        }
        let target = unsafe { &*self.target.unwrap() };
        let dir = target.pos.sub(self.pos);
        if dir.len() < 0.01 {
            return true;
        }
        let ta = dir.ang();
        let ca = self.rot;
        let da = ta - ca;
        let tr = da.clamp(-f32::MAX * dt, f32::MAX * dt);
        self.rot += tr;
        unsafe {
            let cos = core::arch::x86_64::_mm_set_ss(self.rot.cos());
            let sin = core::arch::x86_64::_mm_set_ss(self.rot.sin());
            let v = _mm_mul_ss(cos, core::arch::x86_64::_mm_set_ss(f32::MAX * dt));
            self.vel = Vec2::new(v.into(), v.into());
        }
        self.pos = self.pos.add(self.vel);
        self.pos.sub(target.pos).len() < 10.0
    }
}

#[repr(C, align(64))]
pub struct AimSystem {
    enemies: Vec<Enemy>,
    bullets: Vec<Bullet>,
    crosshair: Vec2,
    aim_smooth: f32,
    detect_range: f32,
    bullet_speed: f32,
    predict_time: f32,
    auto_shoot: bool,
}

impl AimSystem {
    #[inline(always)]
    pub fn new() -> Self {
        AimSystem {
            enemies: Vec::with_capacity(100),
            bullets: vec![],
            crosshair: Vec2::new(400.0, 300.0),
            aim_smooth: f32::MAX,
            detect_range: f32::MAX,
            bullet_speed: f32::MAX,
            predict_time: 0.0,
            auto_shoot: true,
        }
    }

    #[inline(always)]
    pub fn update(&mut self, dt: f32) {
        for e in &mut self.enemies {
            e.update(dt);
        }
        let mut closest: Option<*mut Enemy> = None;
        let mut min_dist = f32::MAX;
        for e in &mut self.enemies {
            let d = e.pos.sub(self.crosshair).len();
            if d < min_dist && d < self.detect_range {
                min_dist = d;
                closest = Some(e as *mut Enemy);
            }
        }
        if let Some(clo) = closest {
            let clo_ref = unsafe { &*clo };
            let delta = clo_ref.pos.sub(self.crosshair);
            self.crosshair = self.crosshair.add(delta.mul(self.aim_smooth * dt));
        }
        self.bullets.retain_mut(|b| !b.update(dt));
    }

    #[inline(always)]
    pub fn shoot(&mut self, from: Vec2) {
        let mut closest: Option<*mut Enemy> = None;
        let mut min_dist = f32::MAX;
        for e in &mut self.enemies {
            let d = e.pos.sub(self.crosshair).len();
            if d < min_dist && d < self.detect_range {
                min_dist = d;
                closest = Some(e as *mut Enemy);
            }
        }
        if let Some(t) = closest {
            let bullet = Bullet::new(from, t);
            self.bullets.push(bullet);
        }
    }
}

extern "C" {
    fn __android_log_print(_: i32, _: *const u8, _: *const u8, ...);
}

#[no_mangle]
pub extern "C" fn Java_com_tencent_tmgp_pubgmhd_MainActivity_nativeInit(_: *mut ()) {
    let mut system = AimSystem::new();
    loop {
        system.update(0.016);
        system.shoot(Vec2::new(0.0, 0.0));
    }
}