use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};

use libpso::packet::ship::*;
use libpso::packet::messages::*;

#[path = "common.rs"]
mod common;
use common::*;


#[async_std::test]
async fn test_use_monomate() {
    let mut entity_gateway = InMemoryGateway::new();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;

    for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
        for _ in 0..2 {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: tool
                        }
                    ),
                    location: item::ItemLocation::Inventory {
                        character_id: char1.id,
                        slot: slot,
                        equipped: false,
                    }
                }).await;
        }
    }

    let mut ship = ShipServerState::new(entity_gateway.clone());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    join_lobby(&mut ship, ClientId(1)).await;
    create_room(&mut ship, ClientId(1), "room", "").await;

    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10000,
    })))).await.unwrap().for_each(drop);

    let items = entity_gateway.get_items_by_character(&char1).await;
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::Monomate
        }
        else {
            false
        }
    }).count() == 1);
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::Monofluid
        }
        else {
            false
        }
    }).count() == 2);
}

#[async_std::test]
async fn test_use_monomate_twice() {
    let mut entity_gateway = InMemoryGateway::new();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;

    for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
        for _ in 0..3 {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: tool
                        }
                    ),
                    location: item::ItemLocation::Inventory {
                        character_id: char1.id,
                        slot: slot,
                        equipped: false,
                    }
                }).await;
        }
    }

    let mut ship = ShipServerState::new(entity_gateway.clone());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    join_lobby(&mut ship, ClientId(1)).await;
    create_room(&mut ship, ClientId(1), "room", "").await;

    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10000,
    })))).await.unwrap().for_each(drop);
    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10000,
    })))).await.unwrap().for_each(drop);

    let items = entity_gateway.get_items_by_character(&char1).await;
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::Monomate
        }
        else {
            false
        }
    }).count() == 1);
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::Monofluid
        }
        else {
            false
        }
    }).count() == 3);
}

#[async_std::test]
async fn test_use_last_monomate() {
    let mut entity_gateway = InMemoryGateway::new();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;

    for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
        for _ in 0..1 {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: tool
                        }
                    ),
                    location: item::ItemLocation::Inventory {
                        character_id: char1.id,
                        slot: slot,
                        equipped: false,
                    }
                }).await;
        }
    }

    let mut ship = ShipServerState::new(entity_gateway.clone());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    join_lobby(&mut ship, ClientId(1)).await;
    create_room(&mut ship, ClientId(1), "room", "").await;

    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10000,
    })))).await.unwrap().for_each(drop);

    let items = entity_gateway.get_items_by_character(&char1).await;
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::Monomate
        }
        else {
            false
        }
    }).count() == 0);
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::Monofluid
        }
        else {
            false
        }
    }).count() == 1);
}

#[async_std::test]
async fn test_use_nonstackable_tool() {
    let mut entity_gateway = InMemoryGateway::new();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;

    entity_gateway.create_item(
        item::NewItemEntity {
            item: item::ItemDetail::Tool(
                item::tool::Tool {
                    tool: item::tool::ToolType::MagicStoneIritista,
                }
            ),
            location: item::ItemLocation::Inventory {
                character_id: char1.id,
                slot: 0,
                equipped: false,
            }
        }).await;

    let mut ship = ShipServerState::new(entity_gateway.clone());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    join_lobby(&mut ship, ClientId(1)).await;
    create_room(&mut ship, ClientId(1), "room", "").await;

    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10000,
    })))).await.unwrap().for_each(drop);

    let items = entity_gateway.get_items_by_character(&char1).await;
    assert!(items.len() == 0);
}

#[async_std::test]
async fn test_use_materials() {
    let mut entity_gateway = InMemoryGateway::new();

    let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;

    for (slot, tool) in vec![item::tool::ToolType::PowerMaterial, item::tool::ToolType::LuckMaterial].into_iter().enumerate() {
        for _ in 0..5 {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: tool
                        }
                    ),
                    location: item::ItemLocation::Inventory {
                        character_id: char1.id,
                        slot: slot,
                        equipped: false,
                    }
                }).await;
        }
    }

    let mut ship = ShipServerState::new(entity_gateway.clone());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    join_lobby(&mut ship, ClientId(1)).await;
    create_room(&mut ship, ClientId(1), "room", "").await;

    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10000,
    })))).await.unwrap().for_each(drop);
    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10001,
    })))).await.unwrap().for_each(drop);
    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
        client: 0,
        target: 0,
        item_id: 0x10001,
    })))).await.unwrap().for_each(drop);

    let items = entity_gateway.get_items_by_character(&char1).await;
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::PowerMaterial
        }
        else {
            false
        }
    }).count() == 4);
    assert!(items.iter().filter(|item| {
        if let item::ItemDetail::Tool(t) = item.item {
            t.tool == item::tool::ToolType::LuckMaterial
        }
        else {
            false
        }
    }).count() == 3);

    let characters = entity_gateway.get_characters_by_user(&user1).await;
    let char = characters[0].as_ref().unwrap();

    assert!(char.materials.power == 1);
    assert!(char.materials.luck == 2);
}