use log::{error, info};
use rand::random;
use space_crush_app::{App, Error};
use space_crush_common::{
    misc::{init_logger, Vfs},
    Dispatcher,
};
use specs::{Builder, Entity, World, WorldExt};

fn main() -> Result<(), Error> {
    let vfs = Vfs::new(&["space-crush-app"])?;

    init_logger(&vfs, "log4rs.yml");

    vfs.log_info();

    info!("Application started");

    if let Err(err) = run(vfs) {
        error!("Error while executing application: {}", err);

        return Err(err);
    }

    info!("Application exited");

    Ok(())
}

fn run(vfs: Vfs) -> Result<(), Error> {
    info!("Application initialization");

    let mut world = World::new();
    world.insert(vfs);

    let player1 = world.create_entity().build();

    let mut common = Dispatcher::new(&mut world);
    let mut app = App::new(&mut world, player1)?;

    create_world(&mut world, player1);

    info!("Application initialized");

    while app.is_running() {
        world.maintain();

        common.process(&world);
        app.process(&world)?;
    }

    Ok(())
}

fn create_world(world: &mut World, player_id: Entity) {
    use glc::{
        matrix::Angle,
        vector::{Vector2f, Vector4f},
    };
    use space_crush_app::components::FleetInfo;
    use space_crush_common::{
        components::{
            Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position,
            Shape, Ship, ShipType, Velocity,
        },
        misc::{PersistWorld, Persistence},
    };
    use specs::{saveload::MarkedBuilder, WriteStorage};

    PersistWorld::setup(world);

    world
        .system_data::<WriteStorage<Player>>()
        .insert(
            player_id,
            Player {
                color: Vector4f::new(0.0, 0.5, 1.0, 0.1),
            },
        )
        .unwrap();

    world
        .create_entity()
        .marked::<<PersistWorld as Persistence>::Marker>()
        .with(Position {
            pos: Vector2f::new(500.0, -500.0),
            shape: Shape::Circle(100.0),
        })
        .with(Fleet {
            orbit_min: 125.0,
            orbit_max: 175.0,
        })
        .with(FleetInfo::default())
        .with(Asteroid {
            type_: AsteroidType::Metal,
        })
        .build();

    world
        .create_entity()
        .marked::<<PersistWorld as Persistence>::Marker>()
        .with(Position {
            pos: Vector2f::new(500.0, 500.0),
            shape: Shape::Circle(100.0),
        })
        .with(Fleet {
            orbit_min: 125.0,
            orbit_max: 175.0,
        })
        .with(FleetInfo::default())
        .with(Asteroid {
            type_: AsteroidType::Crystal,
        })
        .build();

    let planet = world
        .create_entity()
        .marked::<<PersistWorld as Persistence>::Marker>()
        .with(PlayerOwned { owner: player_id })
        .with(Position {
            pos: Vector2f::default(),
            shape: Shape::Circle(250.0),
        })
        .with(Fleet {
            orbit_min: 325.0,
            orbit_max: 425.0,
        })
        .with(FleetInfo::default())
        .with(Planet {})
        .build();

    for i in 0..10 {
        world
            .create_entity()
            .marked::<<PersistWorld as Persistence>::Marker>()
            .with(PlayerOwned { owner: player_id })
            .with(Position {
                pos: Vector2f::new(
                    500.0 * random::<f32>() - 250.0,
                    500.0 * random::<f32>() - 250.0,
                ),
                shape: Shape::Dot,
            })
            .with(Velocity {
                dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(),
                speed: 100.0,
            })
            .with(FleetOwned { owner: planet })
            .with(Ship {
                type_: match i % 3 {
                    0 => ShipType::Fighter,
                    1 => ShipType::Bomber,
                    2 => ShipType::Transporter,
                    _ => unreachable!(),
                },
                agility: Angle::Deg(360.0),
                target_pos: Default::default(),
                target_dir: Default::default(),
            })
            .build();
    }
}
