/*
 * 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.LinkedList;
import java.util.List;
import util.Counter;
import util.Dice;

/**
 *
 * @author MrMcX
 */
public class Room {

    /**
     * The unique number of the room
     */
    public int number;

    /**
     * The list of exits in this room
     */
    public Exit[] exits;

    /**
     * The room generation type
     */
    public Dungeon.Type type;
    
    /**
     * Whether it is a room or a corridor
     */
    public boolean isRoom,

    /**
     * Whether it has monsters,
     */
    hasMonster,

    /**
     * traps,
     */
    hasTrap,

    /**
     * magic,
     */
    hasMagic,

    /**
     * specials,
     */
    hasSpecial,

    /**
     * or treasures
     */
    hasTreasure = false;

    /**
     * The enemy in this room
     */
    public Enemy monster;

    /**
     * The treasure in this room
     */
    public Treasure treasure;

    /**
     * The trap in this room
     */
    public Trap trap;
    
    /**
     * Room description
     */
    public String desc,

    /**
     * magic description
     */
    magicPhenomenon,

    /**
     * special description
     */
    specialPhenomenon = null;
    
    /**
     * Constructs a room with a given predecessor, number of exits, description and counter
     * @param predecessor Predecessor room
     * @param numberOfExits Number of exits
     * @param room Whether it is a room
     * @param desc Description of room
     * @param c Room counter
     */
    public Room(Exit predecessor, int numberOfExits, boolean room, String desc, Counter c){
        exits = new Exit[numberOfExits];
        exits[0] = predecessor;
        this.isRoom = room;
        this.desc = desc;
        number = 0;
    }
    
    /**
     * Generates the content of the room
     * @param c Room counter
     * @param type Environment type
     * @return Returns more rooms to generate
     */
    public List<Room> generate(Counter c, Dungeon.Type type){
        number = c.Next();
        List<Room> list = new LinkedList();
        for(int i = 0; i < exits.length; i++){
            if(exits[i] == null){
                Exit predecessor = new Exit(this);
                Room newRoom = RandomRoom(false, predecessor, c, type);
                exits[i] = new Exit(newRoom, predecessor.type);
                list.add(newRoom);
            }
        }
        SetContents(type);
        return list;
    }
    
    private void SetContents(Dungeon.Type type){
        if(isRoom){
            switch(Dice.Roll(6, 2)){
                case 2:
                case 3: return;
                case 4:
                    hasMonster = true;
                    monster = new Enemy(type);
                    return;
                case 5:
                    hasTrap = true;
                    trap = new Trap();
                case 6:
                    hasTreasure = true;
                    treasure = new Treasure();
                    return;
                case 7:
                    hasMagic = true;
                    // TODO: generate magic here
                    return;
                case 8:
                    hasSpecial = true;
                    // TODO: generate special here
                    return;
                case 9:
                    hasMonster = true;
                    hasTrap = true;
                    monster = new Enemy(type);
                    trap = new Trap();
                    return;
                case 10:
                    hasMonster = true;
                    hasTreasure = true;
                    monster = new Enemy(type);
                    treasure = new Treasure();
                    return;
                case 11:
                    hasMonster = true;
                    hasMagic = true;
                    monster = new Enemy(type);
                    // TODO: generate magic here
                    return;
                case 12:
                    hasTrap = true;
                    hasTreasure = true;
                    trap = new Trap();
                    treasure = new Treasure();
                    return;
            }
        } else {
            switch(Dice.Roll(6, 1)){
                case 1:
                case 2:
                case 3: return;
                case 4: 
                    hasMonster = true;
                    monster = new Enemy(type);
                    return;
                case 5:
                    hasTrap = true;
                    trap = new Trap();
                    return;
                case 6:
                    hasTreasure = true;
                    treasure = new Treasure();
                    return;
            }
        }
        this.type = type;
    }
    
    @Override
    public String toString(){
        if(!isRoom){
            return "Gang " + number;
        } else if(Dungeon.Natural(type)){
            return "Höhle " + number;                    
        } else {
            return "Raum " + number;
        }        
    }
    
    /**
     * Returns a long description
     * @return Long description string
     */
    public String toLongString(){
        String out = "";
        if(isRoom){
            out += "Raum ";
        } else {
            out += "Gang ";
        }
        out += "Nummer " + number + ": " + desc + "\n" + 
                "Ausgänge:\n"; 
        for(Exit x : exits){
            out += x.toLongString();
        }
        if(hasMonster){
            out += "Gegner:\n- " + monster.toString() + "\n";
        }
        if(hasTrap){
            out += "Fallen:\n- " + trap + "\n";
        }
        if(hasMagic){
            out += "Magische Phänomene:\n- " + magicPhenomenon + "\n";
        }
        if(hasSpecial){
            out += "Besondere Phänomene:\n- " + specialPhenomenon + "\n";
        }
        if(hasTreasure){
            out += "Fundsache:\n- " + treasure + "\n";
        }
        return out;
    }
    
    /**
     * Returns a short description
     * @return Short description string
     */
    public String toShortString(){
        if(isRoom){
            return "Raum Nummer " + number;
        } else {
            return "Gang Nummer " + number;
        }
    }
    
    private static Room RoomBigExits(Exit predecessor, Counter c, Dungeon.Type type){
        int rand = Dice.Roll(3, 1);
        if(Dungeon.Natural(type)){
            return new Room(predecessor, rand + 1, true, "große Höhle mit " + rand + " Ausgängen", c);
        } else {
            return new Room(predecessor, rand + 1, true, "großer Raum mit " + rand + " Ausgängen", c);
        }        
    }
    
    private static Room RoomSmallExits(Exit predecessor, Counter c, Dungeon.Type type){
        int rand = Dice.Roll(3, 1);
        if(Dungeon.Natural(type)){
            return new Room(predecessor, rand + 1, true, "kleine Höhle mit " + rand + " Ausgängen", c);
        } else {
            return new Room(predecessor, rand + 1, true, "kleiner Raum mit " + rand + " Ausgängen", c);
        }        
    }
    
    private static Room RoomBigNoExits(Exit predecessor, Counter c, Dungeon.Type type){
        if(Dungeon.Natural(type)){
            return new Room(predecessor, 1, true, "große Höhle ohne Ausgang", c);
        } else {
            return new Room(predecessor, 1, true, "großer Raum ohne Ausgang", c);
        }        
    }
    
    private static Room RoomBigExitsStair(Exit predecessor, Counter c, Dungeon.Type type){
        int rand = Dice.Roll(2, 1);
        if(Dungeon.Natural(type)){
            return new Room(predecessor, rand + 2, true, "große Höhle mit " + rand + " Ausgängen und einer Treppe", c);
        } else {
            return new Room(predecessor, rand + 2, true, "großer Raum mit " + rand + " Ausgängen und einer Treppe", c);
        }        
    }
    
    private static Room RoomSmallNoExits(Exit predecessor, Counter c, Dungeon.Type type){
        if(Dungeon.Natural(type)){
            return new Room(predecessor, 1, true, "kleine Höhle ohne Ausgang", c);
        } else {
            return new Room(predecessor, 1, true, "kleiner Raum ohne Ausgang", c);
        }        
    }
    
    private static Room RoomSmallExitsStair(Exit predecessor, Counter c, Dungeon.Type type){
        int rand = Dice.Roll(2, 1);
        if(Dungeon.Natural(type)){
            return new Room(predecessor, rand + 2, true, "kleine Höhle mit " + rand + " Ausgängen und einer Treppe", c);
        } else {
            return new Room(predecessor, rand + 2, true, "kleiner Raum mit " + rand + " Ausgängen und einer Treppe", c);
        }        
    }
    
    private static Room RoomGiant(Exit predecessor, Counter c, Dungeon.Type type){
        int rand = Dice.Roll(2, 1);
        if(Dungeon.Natural(type)){
            return new Room(predecessor, rand + 1, true, "gigantische Höhle mit " + rand + " Ausgängen", c);
        } else {
            return new Room(predecessor, rand + 1, true, "gigantischer Raum mit " + rand + " Ausgängen", c);
        }        
    }
    
    private static Room CorridorStraight(Exit predecessor, Counter c){
        return new Room(predecessor, 2, false, "gerader Gang", c);
    }
    
    private static Room CorridorRight(Exit predecessor, Counter c){
        return new Room(predecessor, 2, false, "rechts abbiegender Gang", c);
    }
    
    private static Room CorridorLeft(Exit predecessor, Counter c){
        return new Room(predecessor, 2, false, "links abbiegender Gang", c);
    }
    
    private static Room CrossingT(Exit predecessor, Counter c){
        return new Room(predecessor, 3, false, "T-Kreuzung mit Abzweigungen nach links und rechts", c);
    }
    
    private static Room Crossing(Exit predecessor, Counter c){
        return new Room(predecessor, 4, false, "Kreuzung, die in alle Richtungen weitergeht", c);
    }
    
    private static Room DeadEnd(Exit predecessor, Counter c){
        return new Room(predecessor, 1, false, "Sackgasse", c);
    }
    
    /**
     * Generates a random room
     * @param first Whether it is the first room
     * @param predecessor Predecessor room
     * @param c Room counter
     * @param type Environment type
     * @return random room
     */
    public static Room RandomRoom(boolean first, Exit predecessor, Counter c, Dungeon.Type type){
        int number;
        if(first){
            number = Dice.Roll(6, 1);
        } else {
            number = Dice.Roll(20, 1);
        }
        switch(number){
            case 1:
            case 2:
            case 3: return RoomBigExits(predecessor, c, type);
            case 4:
            case 5:
            case 6: return RoomSmallExits(predecessor, c, type);
            case 7: return RoomBigNoExits(predecessor, c, type);
            case 8:
            case 9: return RoomBigExitsStair(predecessor, c, type);
            case 10: return RoomSmallNoExits(predecessor, c, type);
            case 11:
            case 12: return RoomSmallExitsStair(predecessor, c, type);
            case 13:
            case 14: return RoomGiant(predecessor, c, type);
            case 15: return CorridorStraight(predecessor, c);
            case 16: return CorridorRight(predecessor, c);
            case 17: return CorridorLeft(predecessor, c);
            case 18: return CrossingT(predecessor, c);
            case 19: return Crossing(predecessor, c);
            case 20: return DeadEnd(predecessor, c);
        }
        return null;
    }
}
