package electrosphere.game.data;

import java.util.LinkedList;
import java.util.List;

import electrosphere.game.data.audio.SurfaceAudioCollection;
import electrosphere.game.data.biome.BiomeTypeMap;
import electrosphere.game.data.block.BlockData;
import electrosphere.game.data.common.CommonEntityLoader;
import electrosphere.game.data.common.CommonEntityMap;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.crafting.RecipeDataMap;
import electrosphere.game.data.creature.type.CreatureData;
import electrosphere.game.data.creature.type.CreatureTypeLoader;
import electrosphere.game.data.creature.type.attack.AttackMoveResolver;
import electrosphere.game.data.creature.type.model.CreatureTypeMap;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.game.data.foliage.type.FoliageTypeLoader;
import electrosphere.game.data.foliage.type.model.FoliageTypeMap;
import electrosphere.game.data.item.ItemDataMap;
import electrosphere.game.data.projectile.ProjectileTypeHolder;
import electrosphere.game.data.tutorial.HintDefinition;
import electrosphere.game.data.units.UnitDefinitionFile;
import electrosphere.game.data.units.UnitLoader;
import electrosphere.game.data.voxel.VoxelData;
import electrosphere.game.data.voxel.sampler.SamplerFile;
import electrosphere.game.server.race.model.RaceMap;
import electrosphere.game.server.symbolism.model.SymbolMap;
import electrosphere.util.FileUtils;

/**
 * Current configuration for the data of the game
 */
public class Config {
    
    /**
     * The container for all creature definitions
     */
    CreatureTypeLoader creatureTypeLoader;

    /**
     * The container for all item definitions
     */
    ItemDataMap itemMap;

    /**
     * The container for all foliage definitions
     */
    FoliageTypeLoader foliageMap;


    CommonEntityMap objectTypeLoader;
    SymbolMap symbolMap;
    RaceMap raceMap;
    ProjectileTypeHolder projectileTypeHolder;

    //data about every voxel type
    VoxelData voxelData;

    /**
     * The block data
     */
    BlockData blockData;

    //the hints that are defined
    HintDefinition hintData;

    /**
     * The surface audio definitions
     */
    SurfaceAudioCollection surfaceAudioCollection;

    /**
     * The unit loader
     */
    UnitLoader unitLoader;

    /**
     * The crafting recipe map
     */
    RecipeDataMap recipeMap;

    /**
     * The biome map
     */
    BiomeTypeMap biomeMap;

    /**
     * The list of sampler definitions
     */
    List<SamplerFile> samplerDefinitions;
    
    /**
     * Loads the default data
     * @return The config
     */
    public static Config loadDefaultConfig(){
        Config config = new Config();
        config.creatureTypeLoader = Config.loadCreatureTypes("Data/entity/creatures.json");
        config.itemMap = ItemDataMap.loadItemFiles("Data/entity/items.json");
        config.foliageMap = Config.loadFoliageTypes("Data/entity/foliage.json");
        config.objectTypeLoader = Config.loadCommonEntityTypes("Data/entity/objects.json");
        config.symbolMap = FileUtils.loadObjectFromAssetPath("Data/game/symbolism.json", SymbolMap.class);
        config.raceMap = FileUtils.loadObjectFromAssetPath("Data/game/races.json", RaceMap.class);
        config.voxelData = FileUtils.loadObjectFromAssetPath("Data/game/voxelTypes.json", VoxelData.class);
        config.blockData = FileUtils.loadObjectFromAssetPath("Data/game/blockTypes.json", BlockData.class);
        config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/entity/projectile.json", ProjectileTypeHolder.class);
        config.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.class);
        config.surfaceAudioCollection = FileUtils.loadObjectFromAssetPath("Data/audio/surface.json", SurfaceAudioCollection.class);
        config.projectileTypeHolder.init();
        config.unitLoader = UnitLoader.create(FileUtils.loadObjectFromAssetPath("Data/game/units/units.json", UnitDefinitionFile.class));
        config.recipeMap = RecipeDataMap.loadRecipeFiles("Data/game/recipes.json");
        config.biomeMap = BiomeTypeMap.loadBiomeFile("Data/game/biomes.json");
        config.samplerDefinitions = SamplerFile.readSamplerDefinitionFiles("Data/game/voxel");

        //create procedural item types
        ItemDataMap.loadSpawnItems(config.itemMap, config.objectTypeLoader);
        ItemDataMap.generateBlockItems(config.itemMap, config.blockData);

        //validate
        ConfigValidator.valdiate(config);

        
        return config;
    }

    /**
     * Reads a child entity defintion file
     * @param filename The filename
     * @return The list of entities in the file
     */
    static List<CommonEntityType> recursiveReadEntityLoader(String filename){
        List<CommonEntityType> typeList = new LinkedList<CommonEntityType>();
        CommonEntityLoader loaderFile = FileUtils.loadObjectFromAssetPath(filename, CommonEntityLoader.class);
        //push the types from this file
        for(CommonEntityType type : loaderFile.getEntities()){
            typeList.add(type);
        }
        //push types from any other files
        for(String filepath : loaderFile.getFiles()){
            List<CommonEntityType> parsedTypeList = recursiveReadEntityLoader(filepath);
            for(CommonEntityType type : parsedTypeList){
                typeList.add(type);
            }
        }
        return typeList;
    }

    /**
     * Loads all common entity definition files recursively
     * @param initialPath The initial path to recurse from
     * @return The common entity defintion interface
     */
    static CommonEntityMap loadCommonEntityTypes(String initialPath) {
        CommonEntityMap rVal = new CommonEntityMap();
        List<CommonEntityType> typeList = recursiveReadEntityLoader(initialPath);
        for(CommonEntityType type : typeList){
            rVal.putType(type.getId(), type);
        }
        return rVal;
    }

    /**
     * Reads a child creature defintion file
     * @param filename The filename
     * @return The list of creatures in the file
     */
    static List<CreatureData> readCreatureTypeFile(String filename){
        List<CreatureData> typeList = new LinkedList<CreatureData>();
        CreatureTypeMap typeMap = FileUtils.loadObjectFromAssetPath(filename, CreatureTypeMap.class);
        //push the types from this file
        for(CreatureData type : typeMap.getCreatures()){
            typeList.add(type);
        }
        //push types from any other files
        for(String filepath : typeMap.getFiles()){
            List<CreatureData> parsedTypeList = Config.readCreatureTypeFile(filepath);
            for(CreatureData type : parsedTypeList){
                typeList.add(type);
            }
        }
        return typeList;
    }

    /**
     * Loads all creature definition files recursively
     * @param initialPath The initial path to recurse from
     * @return The creature defintion interface
     */
    static CreatureTypeLoader loadCreatureTypes(String initialPath) {
        CreatureTypeLoader loader = new CreatureTypeLoader();
        List<CreatureData> typeList = Config.readCreatureTypeFile(initialPath);
        for(CreatureData type : typeList){
            if(type.getAttackMoves() != null){
                type.setAttackMoveResolver(new AttackMoveResolver(type.getAttackMoves()));
            }
            loader.putType(type.getId(), type);
            //loop through all creatures and add ones with PLAYABLE token to list of playable races
            for(String token : type.getTokens()){
                if(token.contains("PLAYABLE")){
                    loader.putPlayableRace(type.getId());
                }
            }
        }
        return loader;
    }

     /**
     * Reads a child foliage defintion file
     * @param filename The filename
     * @return The list of foliage in the file
     */
    static List<FoliageType> readFoliageTypeFile(String filename){
        List<FoliageType> typeList = new LinkedList<FoliageType>();
        FoliageTypeMap typeMap = FileUtils.loadObjectFromAssetPath(filename, FoliageTypeMap.class);
        //push the types from this file
        for(FoliageType foliage : typeMap.getFoliageList()){
            typeList.add(foliage);
        }
        //push types from any other files
        for(String filepath : typeMap.getFiles()){
            List<FoliageType> parsedTypeList = Config.readFoliageTypeFile(filepath);
            for(FoliageType type : parsedTypeList){
                typeList.add(type);
            }
        }
        return typeList;
    }

    /**
     * Loads all creature definition files recursively
     * @param initialPath The initial path to recurse from
     * @return The creature defintion interface
     */
    static FoliageTypeLoader loadFoliageTypes(String initialPath) {
        FoliageTypeLoader loader = new FoliageTypeLoader();
        List<FoliageType> typeList = Config.readFoliageTypeFile(initialPath);
        for(FoliageType type : typeList){
            loader.putType(type.getId(), type);
        }
        return loader;
    }

    /**
     * Gets the interface for creature definitions loaded into memory
     * @return The interface
     */
    public CreatureTypeLoader getCreatureTypeLoader() {
        return creatureTypeLoader;
    }

    /**
     * Gets the data on all item types in memory
     * @return the data on all items
     */
    public ItemDataMap getItemMap() {
        return itemMap;
    }

    /**
     * Gets the data on all foliage types in memory
     * @return The foliage data
     */
    public FoliageTypeLoader getFoliageMap() {
        return foliageMap;
    }
    
    /**
     * Gets the symbolism map
     * @return The symbolism map
     */
    public SymbolMap getSymbolMap() {
        return symbolMap;
    }
    
    /**
     * Gets the data that defines all creature races in the engine
     * @return The data on all races
     */
    public RaceMap getRaceMap() {
        return raceMap;
    }

    /**
     * Gets the definitions of all object entities in memory
     * @return The objects
     */
    public CommonEntityMap getObjectTypeMap() {
        return objectTypeLoader;
    }

    /**
     * Gets the definitions all projectiles
     * @return the projectile data
     */
    public ProjectileTypeHolder getProjectileMap(){
        return projectileTypeHolder;
    }

    /**
     * Gets the voxel data
     * @return The voxel data
     */
    public VoxelData getVoxelData(){
        return voxelData;
    }

    /**
     * Gets the block data
     * @return The block data
     */
    public BlockData getBlockData(){
        return blockData;
    }

    /**
     * The tutorial hints data
     * @return The hints
     */
    public HintDefinition getHintData(){
        return hintData;
    }

    /**
     * Gets the surface audio collection
     * @return The surface audio collection
     */
    public SurfaceAudioCollection getSurfaceAudioCollection(){
        return this.surfaceAudioCollection;
    }

    /**
     * Gets the unit loader
     * @return The unit loader
     */
    public UnitLoader getUnitLoader(){
        return unitLoader;
    }

    /**
     * Gets the recipe map
     * @return The recipe map
     */
    public RecipeDataMap getRecipeMap(){
        return recipeMap;
    }

    /**
     * Gets the biome map
     * @return The biome map
     */
    public BiomeTypeMap getBiomeMap(){
        return biomeMap;
    }

    /**
     * Gets the list of all sampler files
     * @return The list of all sampler files
     */
    public List<SamplerFile> getSamplerFiles(){
        return this.samplerDefinitions;
    }
    
}
