// Copyright (C) Call of Nil contributors
// SPDX-License-Identifier: AGPL-3.0-only

use proc_macro::TokenStream;
use quote::quote;
use syn::DeriveInput;

pub fn impl_unit(ast: &DeriveInput) -> TokenStream {
  let name = &ast.ident;
  let stream = quote! {
    mod __impl_unit {
      use super::#name;
      use crate::infrastructure::building::BuildingId;
      use crate::infrastructure::requirements::InfrastructureRequirements;
      use crate::military::unit::{Unit, UnitBox, UnitChunk, UnitId, UnitKind};
      use crate::military::unit::stats::prelude::*;
      use crate::ranking::score::Score;
      use crate::world::config::WorldConfig;

      impl #name {
        pub fn new_boxed() -> UnitBox {
          UnitBox::new(Box::new(Self))
        }
      }

      #[automatically_derived]
      impl Unit for #name {
        fn id(&self) -> UnitId {
          Self::ID
        }

        fn kind(&self) -> UnitKind {
          Self::KIND
        }

        fn building(&self) -> BuildingId {
          Self::BUILDING
        }

        fn score(&self) -> Score {
          Self::SCORE
        }

        fn stats(&self) -> &UnitStats {
          &Self::STATS
        }

        fn power(&self) -> Power {
          Self::STATS.power()
        }

        fn attack(&self) -> AttackPower {
          Self::STATS.attack()
        }

        fn defense(&self) -> DefensePower {
          Self::STATS.defense()
        }

        fn ranged_debuff(&self) -> RangedDebuff {
          Self::STATS.ranged_debuff()
        }

        fn speed(&self, config: &WorldConfig) -> Speed {
          let base_speed = f64::from(Self::STATS.base_speed());
          Speed::new((base_speed * config.unit_speed()).max(0.1))
        }

        fn haul(&self) -> Haul {
          Self::STATS.haul()
        }

        fn chunk(&self) -> &UnitChunk {
          &Self::CHUNK
        }

        fn infrastructure_requirements(&self) -> &InfrastructureRequirements {
          &Self::INFRASTRUCTURE_REQUIREMENTS
        }
      }

      #[automatically_derived]
      const impl From<#name> for UnitId {
        fn from(_: #name) -> UnitId {
          #name::ID
        }
      }
    }
  };

  stream.into()
}
