use std::{fmt::Display, rc::Rc};

use data::math_def::{Coordinate, Vector3};
use proto::{MotionState, ProtEntityType, Retcode, SceneEntityInfo};

use crate::player::Player;

#[allow(dead_code)]
pub struct MotionContext {
    pub scene_time_ms: u64,
    pub reliable_seq: u32,
    pub is_reliable: bool,
    pub exclude_uid: u32,
    pub is_notify: bool,
    pub interval_velocity: u32,
    pub sync_uid_vec: Vec<u32>,
    pub is_do_move: bool,
}

pub trait Entity {
    fn get_entity_type(&self) -> ProtEntityType;
    fn get_group_id(&self) -> u32;
    fn entity_id(&self) -> u32;
    fn set_entity_id(&mut self, id: u32);
    fn position(&self) -> &Vector3;
    fn rotation(&self) -> &Vector3;
    fn set_position(&mut self, pos: Vector3);
    fn set_rotation(&mut self, rot: Vector3);
    fn set_coordinate(&mut self, _coordinate: Coordinate) {}
    fn get_coordinate(&self) -> Coordinate {
        Coordinate::default()
    }
    fn get_owner_player(&self) -> Option<Rc<Player>>;

    fn is_on_scene(&self) -> bool {
        self.entity_id() != 0
    }

    fn can_enter_region(&self) -> bool {
        false
    }

    fn get_desc(&self) -> String {
        let pos = self.position();
        format!(
            "[entity_id:{},group_id:{},pos:{},{},{}]",
            self.entity_id(),
            self.get_group_id(),
            pos.x,
            pos.y,
            pos.z
        )
    }

    fn get_player_uid(&self) -> u32 {
        match self.get_owner_player() {
            Some(player) => player.uid,
            None => 0,
        }
    }

    fn set_motion_info(
        &mut self,
        motion_info: &proto::MotionInfo,
        motion_context: &MotionContext,
    ) -> Result<(), Retcode> {
        self.set_motion_info_base(motion_info, motion_context)
    }

    fn set_motion_info_base(
        &mut self,
        motion_info: &proto::MotionInfo,
        _motion_context: &MotionContext,
    ) -> Result<(), Retcode> {
        match motion_info.state() {
            MotionState::MotionNone => (),
            _ => {
                let _speed = motion_info.speed.clone().unwrap_or_default();
                let rot = motion_info.rot.as_ref().unwrap();
                let pos = motion_info.pos.as_ref().unwrap();

                self.set_rotation(Vector3::from_client(rot));
                self.set_position(Vector3::from_client(pos));
                // TODO: Entity::checkMoveSpeed
            }
        }

        // TODO: broadcast

        Ok(())
    }

    fn to_client(&self, _scene_entity_info: &mut SceneEntityInfo) {}
}

impl Display for dyn Entity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.get_desc())
    }
}
