use js_sys::{Array, JsString};
use wasm_bindgen::{prelude::*, JsCast};
use crate::{
    constants::{Direction, ErrorCode, Part, ResourceType},
    objects::{
        ConstructionSite, Owner, Resource, RoomObject, Store, Structure, StructureController,
    },
    pathfinder::SingleRoomCostResult,
    prelude::*,
    CostMatrix, MoveToOptions, RoomName, RoomPosition,
};
#[cfg(feature = "thorium")]
use crate::objects::Reactor;
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(extends = RoomObject)]
    #[derive(Clone, Debug)]
    pub type Creep;
    #[wasm_bindgen(structural, method, getter = body)]
    fn body_internal(this: &Creep) -> Array;
    #[wasm_bindgen(structural, method, getter = fatigue)]
    fn fatigue_internal(this: &Creep) -> u32;
    #[wasm_bindgen(structural, method, getter = hits)]
    fn hits_internal(this: &Creep) -> u32;
    #[wasm_bindgen(structural, method, getter = hitsMax)]
    fn hits_max_internal(this: &Creep) -> u32;
    #[wasm_bindgen(structural, method, getter = id)]
    fn id_internal(this: &Creep) -> Option<JsString>;
    #[wasm_bindgen(structural, method, getter = memory)]
    fn memory_internal(this: &Creep) -> JsValue;
    #[wasm_bindgen(structural, method, setter = memory)]
    fn set_memory_internal(this: &Creep, val: &JsValue);
    #[wasm_bindgen(structural, method, getter = my)]
    fn my_internal(this: &Creep) -> bool;
    #[wasm_bindgen(structural, method, getter = name)]
    fn name_internal(this: &Creep) -> JsString;
    #[wasm_bindgen(structural, method, getter = owner)]
    fn owner_internal(this: &Creep) -> Owner;
    #[wasm_bindgen(structural, method, getter = saying)]
    fn saying_internal(this: &Creep) -> Option<JsString>;
    #[wasm_bindgen(structural, method, getter = spawning)]
    fn spawning_internal(this: &Creep) -> bool;
    #[wasm_bindgen(structural, method, getter = store)]
    fn store_internal(this: &Creep) -> Store;
    #[wasm_bindgen(structural, method, getter = ticksToLive)]
    fn ticks_to_live_internal(this: &Creep) -> Option<u32>;
    #[wasm_bindgen(final, method, js_name = attack)]
    fn attack_internal(this: &Creep, target: &RoomObject) -> i8;
    #[wasm_bindgen(final, method, js_name = attackController)]
    fn attack_controller_internal(this: &Creep, target: &StructureController) -> i8;
    #[wasm_bindgen(final, method, js_name = build)]
    fn build_internal(this: &Creep, target: &ConstructionSite) -> i8;
    #[wasm_bindgen(final, method, js_name = cancelOrder)]
    fn cancel_order_internal(this: &Creep, target: &JsString) -> i8;
    #[wasm_bindgen(final, method, js_name = claimController)]
    fn claim_controller_internal(this: &Creep, target: &StructureController) -> i8;
    #[cfg(feature = "thorium")]
    #[wasm_bindgen(final, method, js_name = claimReactor)]
    fn claim_reactor_internal(this: &Creep, target: &Reactor) -> i8;
    #[wasm_bindgen(final, method, js_name = dismantle)]
    fn dismantle_internal(this: &Creep, target: &Structure) -> i8;
    #[wasm_bindgen(final, method, js_name = drop)]
    fn drop_internal(this: &Creep, ty: ResourceType, amount: Option<u32>) -> i8;
    #[wasm_bindgen(final, method, js_name = generateSafeMode)]
    fn generate_safe_mode_internal(this: &Creep, target: &StructureController) -> i8;
    #[wasm_bindgen(final, method, js_name = getActiveBodyparts)]
    fn get_active_bodyparts_internal(this: &Creep, ty: Part) -> u8;
    #[wasm_bindgen(final, method, js_name = harvest)]
    fn harvest_internal(this: &Creep, target: &RoomObject) -> i8;
    #[wasm_bindgen(final, method, js_name = heal)]
    fn heal_internal(this: &Creep, target: &RoomObject) -> i8;
    #[wasm_bindgen(final, method, js_name = move)]
    fn move_direction_internal(this: &Creep, direction: Direction) -> i8;
    #[wasm_bindgen(final, method, js_name = move)]
    fn move_pulled_by_internal(this: &Creep, target: &Creep) -> i8;
    #[wasm_bindgen(final, method, js_name = moveByPath)]
    fn move_by_path_internal(this: &Creep, path: &JsValue) -> i8;
    #[wasm_bindgen(final, method, js_name = moveTo)]
    fn move_to_internal(this: &Creep, target: &JsValue, options: &JsValue) -> i8;
    #[wasm_bindgen(final, method, js_name = notifyWhenAttacked)]
    fn notify_when_attacked_internal(this: &Creep, enabled: bool) -> i8;
    #[wasm_bindgen(final, method, js_name = pickup)]
    fn pickup_internal(this: &Creep, target: &Resource) -> i8;
    #[wasm_bindgen(final, method, js_name = pull)]
    fn pull_internal(this: &Creep, target: &Creep) -> i8;
    #[wasm_bindgen(final, method, js_name = rangedAttack)]
    fn ranged_attack_internal(this: &Creep, target: &RoomObject) -> i8;
    #[wasm_bindgen(final, method, js_name = rangedHeal)]
    fn ranged_heal_internal(this: &Creep, target: &RoomObject) -> i8;
    #[wasm_bindgen(final, method, js_name = rangedMassAttack)]
    fn ranged_mass_attack_internal(this: &Creep) -> i8;
    #[wasm_bindgen(final, method, js_name = repair)]
    fn repair_internal(this: &Creep, target: &RoomObject) -> i8;
    #[wasm_bindgen(final, method, js_name = reserveController)]
    fn reserve_controller_internal(this: &Creep, target: &StructureController) -> i8;
    #[wasm_bindgen(final, method, js_name = say)]
    fn say_internal(this: &Creep, message: &str, public: bool) -> i8;
    #[wasm_bindgen(final, method, js_name = signController)]
    fn sign_controller_internal(this: &Creep, target: &StructureController, text: &str) -> i8;
    #[wasm_bindgen(final, method, js_name = suicide)]
    fn suicide_internal(this: &Creep) -> i8;
    #[wasm_bindgen(final, method, js_name = transfer)]
    fn transfer_internal(
        this: &Creep,
        target: &RoomObject,
        ty: ResourceType,
        amount: Option<u32>,
    ) -> i8;
    #[wasm_bindgen(final, method, js_name = upgradeController)]
    fn upgrade_controller_internal(this: &Creep, target: &StructureController) -> i8;
    #[wasm_bindgen(final, method, js_name = withdraw)]
    fn withdraw_internal(
        this: &Creep,
        target: &RoomObject,
        ty: ResourceType,
        amount: Option<u32>,
    ) -> i8;
}
impl Creep {
    pub fn body(&self) -> Vec<BodyPart> {
        self.body_internal().iter().map(BodyPart::from).collect()
    }
    pub fn fatigue(&self) -> u32 {
        self.fatigue_internal()
    }
    pub fn hits(&self) -> u32 {
        self.hits_internal()
    }
    pub fn hits_max(&self) -> u32 {
        self.hits_max_internal()
    }
    pub fn memory(&self) -> JsValue {
        self.memory_internal()
    }
    pub fn set_memory(&self, val: &JsValue) {
        self.set_memory_internal(val)
    }
    pub fn my(&self) -> bool {
        self.my_internal()
    }
    pub fn owner(&self) -> Owner {
        self.owner_internal()
    }
    pub fn saying(&self) -> Option<JsString> {
        self.saying_internal()
    }
    pub fn spawning(&self) -> bool {
        self.spawning_internal()
    }
    pub fn store(&self) -> Store {
        self.store_internal()
    }
    pub fn ticks_to_live(&self) -> Option<u32> {
        self.ticks_to_live_internal()
    }
    pub fn attack<T>(&self, target: &T) -> Result<(), ErrorCode>
    where
        T: ?Sized + Attackable,
    {
        ErrorCode::result_from_i8(self.attack_internal(target.as_ref()))
    }
    pub fn attack_controller(&self, target: &StructureController) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.attack_controller_internal(target))
    }
    pub fn build(&self, target: &ConstructionSite) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.build_internal(target))
    }
    pub fn cancel_order(&self, target: &JsString) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.cancel_order_internal(target))
    }
    pub fn claim_controller(&self, target: &StructureController) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.claim_controller_internal(target))
    }
    #[cfg(feature = "thorium")]
    pub fn claim_reactor(&self, target: &Reactor) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.claim_reactor_internal(target))
    }
    pub fn dismantle<T>(&self, target: &T) -> Result<(), ErrorCode>
    where
        T: ?Sized + Dismantleable,
    {
        ErrorCode::result_from_i8(self.dismantle_internal(target.as_ref()))
    }
    pub fn drop(&self, ty: ResourceType, amount: Option<u32>) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.drop_internal(ty, amount))
    }
    pub fn generate_safe_mode(&self, target: &StructureController) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.generate_safe_mode_internal(target))
    }
    pub fn get_active_bodyparts(&self, ty: Part) -> u8 {
        self.get_active_bodyparts_internal(ty)
    }
    pub fn harvest<T>(&self, target: &T) -> Result<(), ErrorCode>
    where
        T: ?Sized + Harvestable,
    {
        ErrorCode::result_from_i8(self.harvest_internal(target.as_ref()))
    }
    pub fn heal<T>(&self, target: &T) -> Result<(), ErrorCode>
    where
        T: ?Sized + Healable,
    {
        ErrorCode::result_from_i8(self.heal_internal(target.as_ref()))
    }
    pub fn move_direction(&self, direction: Direction) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.move_direction_internal(direction))
    }
    pub fn move_pulled_by(&self, target: &Creep) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.move_pulled_by_internal(target))
    }
    pub fn move_by_path(&self, path: &JsValue) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.move_by_path_internal(path))
    }
    pub fn notify_when_attacked(&self, enabled: bool) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.notify_when_attacked_internal(enabled))
    }
    pub fn pickup(&self, target: &Resource) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.pickup_internal(target))
    }
    pub fn pull(&self, target: &Creep) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.pull_internal(target))
    }
    pub fn ranged_attack<T>(&self, target: &T) -> Result<(), ErrorCode>
    where
        T: ?Sized + Attackable,
    {
        ErrorCode::result_from_i8(self.ranged_attack_internal(target.as_ref()))
    }
    pub fn ranged_heal<T>(&self, target: &T) -> Result<(), ErrorCode>
    where
        T: ?Sized + Healable,
    {
        ErrorCode::result_from_i8(self.ranged_heal_internal(target.as_ref()))
    }
    pub fn ranged_mass_attack(&self) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.ranged_mass_attack_internal())
    }
    pub fn repair(&self, target: &RoomObject) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.repair_internal(target))
    }
    pub fn reserve_controller(&self, target: &StructureController) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.reserve_controller_internal(target))
    }
    pub fn say(&self, message: &str, public: bool) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.say_internal(message, public))
    }
    pub fn sign_controller(
        &self,
        target: &StructureController,
        text: &str,
    ) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.sign_controller_internal(target, text))
    }
    pub fn suicide(&self) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.suicide_internal())
    }
    pub fn upgrade_controller(&self, target: &StructureController) -> Result<(), ErrorCode> {
        ErrorCode::result_from_i8(self.upgrade_controller_internal(target))
    }
}
impl JsCollectionFromValue for Creep {
    fn from_value(val: JsValue) -> Self {
        val.unchecked_into()
    }
}
impl HasHits for Creep {
    fn hits(&self) -> u32 {
        self.hits()
    }
    fn hits_max(&self) -> u32 {
        self.hits_max()
    }
}
impl MaybeHasNativeId for Creep {
    fn try_native_id(&self) -> Option<JsString> {
        self.id_internal()
    }
}
impl HasStore for Creep {
    fn store(&self) -> Store {
        self.store()
    }
}
impl SharedCreepProperties for Creep {
    fn memory(&self) -> JsValue {
        self.memory()
    }
    fn set_memory(&self, val: &JsValue) {
        self.set_memory(val)
    }
    fn my(&self) -> bool {
        self.my()
    }
    fn name(&self) -> String {
        self.name_internal().into()
    }
    fn owner(&self) -> Owner {
        self.owner()
    }
    fn saying(&self) -> Option<JsString> {
        self.saying()
    }
    fn ticks_to_live(&self) -> Option<u32> {
        self.ticks_to_live()
    }
    fn cancel_order(&self, target: &JsString) -> Result<(), ErrorCode> {
        self.cancel_order(target)
    }
    fn drop(&self, ty: ResourceType, amount: Option<u32>) -> Result<(), ErrorCode> {
        self.drop(ty, amount)
    }
    fn move_direction(&self, direction: Direction) -> Result<(), ErrorCode> {
        self.move_direction(direction)
    }
    fn move_by_path(&self, path: &JsValue) -> Result<(), ErrorCode> {
        self.move_by_path(path)
    }
    fn move_to<T>(&self, target: T) -> Result<(), ErrorCode>
    where
        T: HasPosition,
    {
        let target: RoomPosition = target.pos().into();
        ErrorCode::result_from_i8(self.move_to_internal(&target, &JsValue::UNDEFINED))
    }
    fn move_to_with_options<T, F>(
        &self,
        target: T,
        options: Option<MoveToOptions<F>>,
    ) -> Result<(), ErrorCode>
    where
        T: HasPosition,
        F: FnMut(RoomName, CostMatrix) -> SingleRoomCostResult,
    {
        let target: RoomPosition = target.pos().into();
        if let Some(options) = options {
            options.into_js_options(|js_options| {
                ErrorCode::result_from_i8(self.move_to_internal(&target, js_options))
            })
        } else {
            ErrorCode::result_from_i8(self.move_to_internal(&target, &JsValue::UNDEFINED))
        }
    }
    fn notify_when_attacked(&self, enabled: bool) -> Result<(), ErrorCode> {
        self.notify_when_attacked(enabled)
    }
    fn pickup(&self, target: &Resource) -> Result<(), ErrorCode> {
        self.pickup(target)
    }
    fn say(&self, message: &str, public: bool) -> Result<(), ErrorCode> {
        self.say(message, public)
    }
    fn suicide(&self) -> Result<(), ErrorCode> {
        self.suicide()
    }
    fn transfer<T>(
        &self,
        target: &T,
        ty: ResourceType,
        amount: Option<u32>,
    ) -> Result<(), ErrorCode>
    where
        T: Transferable + ?Sized,
    {
        ErrorCode::result_from_i8(self.transfer_internal(target.as_ref(), ty, amount))
    }
    fn withdraw<T>(
        &self,
        target: &T,
        ty: ResourceType,
        amount: Option<u32>,
    ) -> Result<(), ErrorCode>
    where
        T: Withdrawable + ?Sized,
    {
        ErrorCode::result_from_i8(self.withdraw_internal(target.as_ref(), ty, amount))
    }
}
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen]
    pub type BodyPart;
    #[wasm_bindgen(method, getter)]
    pub fn boost(this: &BodyPart) -> Option<ResourceType>;
    #[wasm_bindgen(method, getter = type)]
    pub fn part(this: &BodyPart) -> Part;
    #[wasm_bindgen(method, getter)]
    pub fn hits(this: &BodyPart) -> u32;
}