/*
 * The MIT License
 *
 * Copyright 2016 MrMcX.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package dungeon;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import util.Counter;
import util.Dice;
import org.jgraph.graph.DefaultEdge;
import org.jgrapht.UndirectedGraph;
import org.jgrapht.graph.SimpleGraph;

/**
 * Stores all information about the dungeon
 * @author MrMcX
 */
public class Dungeon {
    private final List<Room> rooms;
    private final LinkedList<Room> toGenerate;
    private final Counter counter;
    
    /**
     * Stanard constructor, creates a new dungeon and then generates the contens
     * @param size maximum number of rooms
     * @param type how the rooms are to construct
     * @param mode determines room generation sequence
     */
    public Dungeon(int size, Type type, Mode mode){
        rooms = new ArrayList<>();
        toGenerate = new LinkedList<>();
        counter = new Counter();
        generate(size, type, mode);
    }
    
    private void generate(int size, Type type, Mode mode){
        toGenerate.add(Room.RandomRoom(true, Exit.Start(), counter, type));
        while(!toGenerate.isEmpty() && rooms.size() < size){
            Room next;
            switch(mode){
                case STRAIGHT:
                    next = toGenerate.getLast();
                    break;
                case BRANCHED:
                    next = toGenerate.getFirst();
                    break;
                case RANDOM:
                    next = toGenerate.get(Dice.Roll(toGenerate.size(), 1)-1);
                    break;
                default:
                    next = toGenerate.getFirst();
                    break;
            }
            next.generate(counter, type).stream().forEach((r) -> {
                toGenerate.add(r);
            });
            toGenerate.remove(next);
            rooms.add(next);            
        }
    }
    
    @Override
    public String toString(){
        return rooms.stream().map((r) -> r.toLongString() + "\n").reduce("", String::concat);
    }
    
    /**
     * Creates a graph from the dungeon
     * @return UndirectedGraph from JGraphT
     */
    public UndirectedGraph toGraph(){
        UndirectedGraph g = new SimpleGraph(DefaultEdge.class);        
        rooms.stream().forEach((r) -> {
            g.addVertex(r);
        });
        rooms.stream().forEach((r) -> {
            for(Exit e : r.exits){
                if(e.isStart || e.room.number == 0 || g.containsEdge(r, e.room)){
                    continue;
                }
                g.addEdge(r, e.room, e);
            }
        });
        return g;
    }
    
    /**
     * Checks whether a type is natural
     * @param n
     * @return is natural
     */
    public static boolean Natural(Type n){
        return (n == Type.NATURAL || n == Type.EXP_NATURAL);
    }
    
    /**
     * Different room generation sequence modes
     */
    public static enum Mode{

        /**
         * FILO -> straight dungeon
         */
        STRAIGHT,

        /**
         * FIFO -> branched dungeon
         */
        BRANCHED,

        /**
         * Random -> balanced dungeon
         */
        RANDOM,
    }
    
    /**
     * The type of rooms to generate
     */
    public static enum Type{

        /**
         * Nature-made rooms
         */
        NATURAL,

        /**
         * "Human"-made rooms
         */
        ARTIFICIAL,

        /**
         * Nature-made rooms in experimental mode
         */
        EXP_NATURAL,

        /**
         * "Human"-made rooms in experimental mode
         */
        EXP_ARTIFICIAL
    }
}
