use std::collections::HashMap;
use std::default::Default;

use crate::entity::account::*;
use crate::entity::character::*;
use crate::entity::gateway::EntityGateway;
use crate::entity::item::*;

use libpso::character::settings;
use libpso::item;

use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct InMemoryGateway {
    users: Arc<Mutex<HashMap<u32, UserAccount>>>,
    user_settings: Arc<Mutex<HashMap<u32, UserSettings>>>,
    guildcards: Arc<Mutex<HashMap<u32, GuildCard>>>,
    characters: Arc<Mutex<HashMap<u32, Character>>>,
    items: Arc<Mutex<HashMap<ItemEntityId, Item>>>,
}

impl InMemoryGateway {
    pub fn new() -> InMemoryGateway {
        InMemoryGateway {
            users: Arc::new(Mutex::new(HashMap::new())),
            user_settings: Arc::new(Mutex::new(HashMap::new())),
            guildcards: Arc::new(Mutex::new(HashMap::new())),
            characters: Arc::new(Mutex::new(HashMap::new())),
            items: Arc::new(Mutex::new(HashMap::new())),
        }
    }
}

impl EntityGateway for InMemoryGateway {
    fn get_user_by_id(&self, id: u32) -> Option<UserAccount> {
        let users = self.users.lock().unwrap();
        users.get(&id).map(|k| k.clone())
    }

    fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
        let users = self.users.lock().unwrap();
        users
            .iter()
            .find(|(_, k)| k.username == username)
            .map(|(_, k)| k.clone())
    }

    fn set_user(&mut self, user: &UserAccount) {
        let mut users = self.users.lock().unwrap();
        users.insert(user.id, user.clone());
    }

    fn get_user_settings_by_user(&self, user: &UserAccount) -> Option<UserSettings> {
        let user_settings = self.user_settings.lock().unwrap();
        user_settings
            .iter()
            .find(|(_, k)| k.id == user.id)
            .map(|(_, k)| k.clone())
    }

    fn create_user_settings_by_user(&self, user: &UserAccount) -> UserSettings {
        let mut user_settings = self.user_settings.lock().unwrap();
        let id = user_settings
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, *i))
            + 1;
        let new_settings = UserSettings {
            id: id,
            user_id: user.id,
            settings: settings::UserSettings::default(),
        };
        user_settings.insert(id, new_settings.clone());
        new_settings
    }

    fn get_characters_by_user(&self, user: &UserAccount) -> [Option<Character>; 4] {
        let characters = self.characters.lock().unwrap();
        let mut chars = [None; 4];
        characters
            .iter()
            .filter(|(_, c)| c.user_id == user.id)
            .for_each(|(_, c)| chars[c.slot as usize] = Some(c.clone()));
        chars
    }

    fn new_character_by_user(&mut self, user: &UserAccount) -> Character {
        let mut characters = self.characters.lock().unwrap();
        let id = characters 
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, *i))
            + 1;

        let mut c = Character::default();
        c.id = id;
        c.user_id = user.id;
        characters.insert(id, c.clone());
        c
    }

    fn set_character(&mut self, char: &Character) {
        let mut characters = self.characters.lock().unwrap();
        characters.insert(char.id, char.clone());
    }

    fn get_guild_card_data_by_user(&self, _user: &UserAccount) -> GuildCardData {
        GuildCardData::default()
    }

    fn new_item(&mut self, item: ItemDetail, location: ItemLocation) -> Item {
        let mut items = self.items.lock().unwrap();
        let id = items
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
            + 1;
        let new_item = Item {
            id: ItemEntityId(id),
            location: location,
            item: item,
        };
        items.insert(ItemEntityId(id), new_item.clone());
        new_item
    }

    fn set_item(&self, item: &Item) {
        let mut items = self.items.lock().unwrap();
        items.insert(item.id, item.clone());
    }

    fn get_items_by_character(&self, character: &Character) -> Vec<Item> {
        let items = self.items.lock().unwrap();
        items
            .iter()
            .filter(|(_, k)| {
                match k.location {
                    ItemLocation::Inventory{character_id, ..} => character_id == character.id,
                    ItemLocation::Bank{character_id, ..} => character_id == character.id,
                    _ => false
                }
            })
            .map(|(_, k)| {
                k.clone()
            })
            .collect()
    }

    fn create_guild_card_by_character(&self, character: &Character) -> GuildCard {
        let mut guildcards = self.guildcards.lock().unwrap();
        let user = self.get_user_by_id(character.user_id).unwrap();
        let settings = self.get_user_settings_by_user(&user).unwrap();
        let id = guildcards
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, *i))
            + 1;
        let new_guildcard = GuildCard {
            id: id,
            character_id: character.id,
            guildcard: libpso::character::guildcard::GuildCard {
                guildcard: user.guildcard,
                name: libpso::utf8_to_utf16_array!(character.name, 24),
                team: settings.settings.team_name,
                desc: [0; 88], // TODO?
                reserved1: 1,
                language: 0,
                section_id: character.section_id.into(),
                class: character.char_class.into(),
                padding: 0,
                comment: [0; 88], // TODO?
            },
        };
        guildcards.insert(id, new_guildcard.clone());
        new_guildcard
    }

    fn get_guild_card_by_character(&self, character: &Character) -> Option<GuildCard> {
        let guildcards = self.guildcards.lock().unwrap();
        guildcards
            .iter()
            .find(|(_, k)| k.character_id == character.id)
            .map(|(_, k)| k.clone())
    }
}