/*
 * Copyright 2013 MovingBlocks
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.terasology.engine.core.modes.loadProcesses;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.engine.config.Config;
import org.terasology.engine.context.Context;
import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.core.modes.SingleStepLoadProcess;
import org.terasology.engine.entitySystem.Component;
import org.terasology.engine.entitySystem.entity.EntityManager;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.engine.game.GameManifest;
import org.terasology.engine.network.NetworkComponent;
import org.terasology.engine.world.WorldComponent;
import org.terasology.engine.world.chunks.ChunkProvider;
import org.terasology.engine.world.generator.WorldConfigurator;
import org.terasology.engine.world.generator.WorldGenerator;

import java.util.Iterator;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 */
public class CreateWorldEntity extends SingleStepLoadProcess {

    private static final Logger logger = LoggerFactory.getLogger(CreateWorldEntity.class);

    private final Context context;
    private final GameManifest gameManifest;

    //TODO: figure out dependencies at some point ....
    protected EntityManager entityManager;
    protected WorldGenerator worldGenerator;
    protected WorldConfigurator worldConfigurator;
    protected Config config;
    protected ChunkProvider chunkProvider;

    public CreateWorldEntity(Context context, GameManifest gameManifest) {
        this.context = context;
        this.gameManifest = gameManifest;
    }

    @Override
    public String getMessage() {
        return "Creating World Entity...";
    }

    @Override
    public boolean step() {
        this.entityManager = context.get(EntityManager.class);
        this.worldGenerator = context.get(WorldGenerator.class);
        this.config = context.get(Config.class);
        this.chunkProvider = context.get(ChunkProvider.class);
        this.worldConfigurator = worldGenerator.getConfigurator();

        Iterator<EntityRef> worldEntityIterator = entityManager.getEntitiesWith(WorldComponent.class).iterator();
        if (worldEntityIterator.hasNext()) {
            EntityRef worldEntity = worldEntityIterator.next();
            worldEntityIterator.forEachRemaining(w -> logger.warn("Ignored extra world {}", w));
            chunkProvider.setWorldEntity(worldEntity);

            // replace the world generator values from the components in the world entity
            setProperties(worldConfigurator, (key, clazz) -> worldEntity.getComponent(clazz));
        } else {
            EntityRef worldEntity = createWorldPoolsAndEntity();
            chunkProvider.setWorldEntity(worldEntity);

            // transfer all world generation parameters from Config to WorldEntity
            SimpleUri generatorUri = worldGenerator.getUri();
            setProperties(worldConfigurator, (key, clazz) -> config.getModuleConfig(generatorUri, key, clazz));
        }

        return true;
    }

    private static void setProperties(WorldConfigurator configurator, WorldConfiguratorConfigurator provider) {
        checkNotNull(configurator).getProperties().forEach((key, currentComponent) -> {
            Component configuredComponent = provider.getComponentOfWorldEntity(key, currentComponent.getClass());
            if (configuredComponent != null) {
                configurator.setProperty(key, configuredComponent);
            }
        });
    }

    private EntityRef createWorldPoolsAndEntity() {
        entityManager.createWorldPools(gameManifest);

        EntityRef worldEntity = entityManager.create();
        worldEntity.addComponent(new WorldComponent());
        NetworkComponent networkComponent = new NetworkComponent();
        networkComponent.replicateMode = NetworkComponent.ReplicateMode.ALWAYS;
        worldEntity.addComponent(networkComponent);
        return worldEntity;
    }

    @FunctionalInterface
    private interface WorldConfiguratorConfigurator {
        Component getComponentOfWorldEntity(String key, Class<? extends Component> clazz);
    }

    @Override
    public int getExpectedCost() {
        return 1;
    }
}

@@ -1439,24 +1439,46 @@
 il.Iterator;
+%0Aimport java.util.Map;
 %0A%0Aimport sta
@@ -2803,24 +2803,25 @@
 asNext()) %7B%0A
+%0A
             
@@ -3123,37 +3123,36 @@
             
-setProperties
+checkNotNull
 (worldConfig
@@ -3161,76 +3161,373 @@
 ator
-, (key, clazz) -%3E worldEntity.getComponent(clazz));%0A        %7D else %7B
+).getProperties().forEach((key, currentComponent) -%3E %7B%0A                Component component = worldEntity.getComponent(currentComponent.getClass());%0A                if (component != null) %7B%0A                    worldConfigurator.setProperty(key, component);%0A                %7D%0A            %7D);%0A%0A        %7D else %7B%0A            // create world entity if one does not exist.
 %0A   
@@ -3801,29 +3801,28 @@
         
-setProperties
+checkNotNull
 (worldCo
@@ -3835,25 +3835,186 @@
 ator
-, (key, clazz) -%3E
+).getProperties().forEach((key, currentComponent) -%3E %7B%0A                Class%3C? extends Component%3E clazz = currentComponent.getClass();%0A                Component moduleComponent =
  con
@@ -4058,17 +4058,16 @@
 , clazz)
-)
 ;%0A      
@@ -4072,19 +4072,16 @@
     
-%7D%0A%0A
         
 retu
@@ -4080,212 +4080,145 @@
     
-return true;
+if (moduleComponent != null) %7B
 %0A    
-%7D%0A%0A
     
-private static void setProperties(WorldConfigurator configurator, WorldConfiguratorConfigurator provider) %7B%0A        checkNotNull(configurator).getProperties().forEach((key, current
+            // configure entity from component%0A                    worldEntity.addComponent(module
 Comp
@@ -4215,37 +4215,33 @@
 moduleComponent)
- -%3E %7B
+;
 %0A            Com
@@ -4241,80 +4241,51 @@
     
-Component configuredComponent = provider.getComponentOfWorldEnti
+        worldConfigurator.setProper
 ty(key, 
 curr
@@ -4276,31 +4276,30 @@
 operty(key, 
-current
+module
 Component.ge
@@ -4291,35 +4291,24 @@
 uleComponent
-.getClass()
 );%0A         
@@ -4314,99 +4314,73 @@
     
-if (configuredComponent != null) %7B%0A                configurator.setProperty(key, configured
+    %7D else %7B%0A                    worldEntity.addComponent(current
 Comp
@@ -4379,32 +4379,36 @@
 rentComponent);%0A
+    
             %7D%0A  
@@ -4413,18 +4413,54 @@
         
-%7D)
+    %7D);%0A        %7D%0A%0A        return true
 ;%0A    %7D%0A
@@ -4918,185 +4918,8 @@
  %7D%0A%0A
-    @FunctionalInterface%0A    private interface WorldConfiguratorConfigurator %7B%0A        Component getComponentOfWorldEntity(String key, Class%3C? extends Component%3E clazz);%0A    %7D%0A%0A
     

streamline implemention