/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package d.d;

import d.d.MapPanel.Square;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

/**
 *
 * @author pospis
 */
public class ProcGen {

    static final int GRASS = 1;
    static final int WATER = 2;
    static final int ROCK = 3;
    static final int TREE = 4;
    static final int GROUND_STONE = 5;
    static final int WALL_STONE = 6;

    static final int FIRE = 7;
    static final int TABLE = 8;
    static final int CUPBOARD = 9;
    static final int CHEST = 10;
    static final int LTABLER = 11;
    static final int LTABLEL = 12;
    static final int CHAIR = 13;
    static final int BED = 14;
    static final int ARMORY = 15;

    static final int KITCHEN = 0;
    static final int LIVINGROOM = 1;
    static final int ARMORYROOM = 2;

    int SquareSize = 25;

    MapPanel MP;
    MersenneTwister MT;

    private int seed;

    private int countOfRooms = -1;

    Square[][] squares;

    int countOfOpt;
    int pattern;
    int height = 50;
    int width = 50;

    ArrayList<Room> rooms;

    Square[] room;

    public ProcGen(MapPanel mp) throws IOException {
        MP = mp;
    }

    public void setSquares(Square[][] s) {
        squares = s;
    }

    public void setSize(int w, int h) {
        width = w;
        height = h;
    }

    public void setSeed(int s) {
        MT = new MersenneTwister(s);
        seed = s;
    }

    public void setCountOfRooms(int c) {
        switch (c) {

            case 0:
                countOfRooms = getAbsInt(2) + 3;
                countOfOpt--;
                break;
            case 1:
                countOfRooms = getAbsInt(3) + 4;
                countOfOpt--;
                break;
            case 2:
                countOfRooms = getAbsInt(5) + 6;
                countOfOpt--;
                break;
            case 3:
                countOfRooms = getAbsInt(6) + 10;
                countOfOpt--;
                break;

        }
    }

    public void setCountOfRoomsExactly(int c) {
        countOfRooms = c;
    }

    public void setPattern(int c) {
        pattern = c;
    }

    public void Generate() throws IOException {
        countOfOpt = 50;

        if (countOfRooms == -1) {
            Random rn = new Random();
            int count = rn.nextInt(4);
            setCountOfRooms(count);
            MP.cor.setSelectedIndex(count);
            countOfOpt--;
        }

        for (int i = 0; i < countOfOpt; i++) {
            getAbsInt();
        }

        if (pattern == 0) {
            lab();
        }
        if (pattern == 1) {
            runaway();
        }
        if (pattern == 2) {
            sun();
        }

        MP.repaintMap();
        MP.readKeyboard();

    }

    private void lab() throws IOException {
        rooms = new ArrayList<Room>();
        Room r;
        r = makeRoom(4, 10, 0);
        rooms.add(r);
        int c = 0;
        while (rooms.size() != countOfRooms) {
            r = makeRoom(4, 10, 0);
            for (int j = 0; j < rooms.size(); j++) {
                if (intersection(r, rooms.get(j))) {
                    break;
                }
                if (j == rooms.size() - 1) {
                    rooms.add(r);
                }
            }
            c++;
            if (c > 500) {
                MP.abort();
                return;
            }
        }

        for (int i = 1; i < rooms.size(); i++) {
            makePath(rooms.get(i - 1), rooms.get(i));
        }

        for (int i = 0; i < countOfRooms; i++) {

            rooms.get(i).roomSave();
            int type = getAbsInt(3);

            rooms.get(i).setType(type);

        }
    }

    private void runaway() throws IOException {
        rooms = new ArrayList<Room>();
        Room r;
        r = makeRoom(4, 7, 0);
        rooms.add(r);
        int c = 0;
        while (rooms.size() != countOfRooms) {
            c++;
            r = makeRoom(3, 7, 0);
            for (int j = 0; j < rooms.size(); j++) {
                if (intersection(r, rooms.get(j))) {
                    break;
                }
                if (j == rooms.size() - 1) {
                    rooms.add(r);
                }
            }
            if (c > 500) {
                MP.abort();
                return;
            }
        }

        int[] list = makeRlist();
        for (int i = 1; i < rooms.size(); i++) {
            makePath(rooms.get(list[i - 1]), rooms.get(list[i]));
        }

        for (int i = 0; i < countOfRooms; i++) {

            rooms.get(i).roomSave();
            int type = getAbsInt(3);

            rooms.get(i).setType(type);

        }
    }

    private void sun() throws IOException {
        rooms = new ArrayList<Room>();
        Room r;
        r = makeRoom(10, 15, 1);
        rooms.add(r);
        int c = 0;
        while (rooms.size() != countOfRooms) {
            c++;
            r = makeRoom(4, 7, 0);
            for (int j = 0; j < rooms.size(); j++) {
                if (intersection(r, rooms.get(j))) {
                    break;
                }
                if (j == rooms.size() - 1) {
                    rooms.add(r);
                }
            }
            if (c > 500) {
                MP.abort();
                return;
            }
        }

        for (int i = 1; i < rooms.size(); i++) {
            makePath(rooms.get(0), rooms.get(i));
        }

        for (int i = 0; i < countOfRooms; i++) {

            rooms.get(i).roomSave();
            int type = getAbsInt(3);

            rooms.get(i).setType(type);

        }
    }

    private boolean intersection(Room r1, Room r2) {

        if (r1.startPoint.Xposition == r2.startPoint.Xposition && r1.startPoint.Yposition == r2.startPoint.Yposition) {
            return true;
        }

        if (r1.startPoint.Xposition < r2.startPoint.Xposition) {

            if (r1.startPoint.Xposition + r1.w + 1 < r2.startPoint.Xposition) {
                return false;
            }

            if (r1.startPoint.Yposition < r2.startPoint.Yposition) {
                if (r1.startPoint.Yposition + r1.h + 1 < r2.startPoint.Yposition) {
                    return false;
                }
            } else {
                if (r1.startPoint.Yposition > r2.startPoint.Yposition + r2.h + 1) {
                    return false;
                }
            }
        }

        if (r1.startPoint.Xposition > r2.startPoint.Xposition) {

            if (r1.startPoint.Xposition > r2.startPoint.Xposition + r2.w + 1) {
                return false;
            }

            if (r1.startPoint.Yposition < r2.startPoint.Yposition) {
                if (r1.startPoint.Yposition + r1.h + 1 < r2.startPoint.Yposition) {
                    return false;
                }
            } else {
                if (r1.startPoint.Yposition > r2.startPoint.Yposition + r2.h + 1) {
                    return false;
                }
            }
        }

        return true;
    }

    private Room makeRoom(int min, int max, int opt) {

        int w = width;
        int h = height;
        int x = width;
        int y = height;

        if (opt == 0) {
            while (x + w >= width || w < min || x == 0) {
                w = getAbsInt() % max;
                x = getAbsInt(width);
            }
            while (y + h >= height || h < min || y == 0) {
                h = getAbsInt() % max;
                y = getAbsInt(height);
            }
        }

        if (opt == 1) {
            while (x + w >= width || w < min || x == 0) {
                w = getAbsInt() % max;
                x = 18 + getAbsInt(10);
            }
            while (y + h >= height || h < min || y == 0) {
                h = getAbsInt() % max;
                y = 18 + getAbsInt(10);
            }
        }

        return new Room(h, w, x, y);
    }

    private void makePath(Room r1, Room r2) {

        int x;
        int y;

        if (MT.nextBoolean()) {
            x = r1.startPoint.Xposition + (getAbsInt() % r1.w);
            y = r2.startPoint.Yposition + (getAbsInt() % r2.h);
            if (r2.startPoint.Xposition > x) {
                for (int i = x; i < r2.startPoint.Xposition; i++) {
                    squares[i][y].data = GROUND_STONE;
                }
            } else {
                for (int i = x; i > r2.startPoint.Xposition; i--) {
                    squares[i][y].data = GROUND_STONE;
                }
            }

            if (r1.startPoint.Yposition > y) {
                for (int i = y; i < r1.startPoint.Yposition; i++) {
                    squares[x][i].data = GROUND_STONE;
                }
            } else {
                for (int i = y; i > r1.startPoint.Yposition; i--) {
                    squares[x][i].data = GROUND_STONE;
                }
            }
        } else {
            x = r2.startPoint.Xposition + (getAbsInt() % r2.w);
            y = r1.startPoint.Yposition + (getAbsInt() % r1.h);
            if (r1.startPoint.Xposition > x) {
                for (int i = x; i < r1.startPoint.Xposition; i++) {
                    squares[i][y].data = GROUND_STONE;
                }
            } else {
                for (int i = x; i > r1.startPoint.Xposition; i--) {
                    squares[i][y].data = GROUND_STONE;
                }
            }

            if (r2.startPoint.Yposition > y) {
                for (int i = y; i < r2.startPoint.Yposition; i++) {
                    squares[x][i].data = GROUND_STONE;
                }
            } else {
                for (int i = y; i > r2.startPoint.Yposition; i--) {
                    squares[x][i].data = GROUND_STONE;
                }
            }
        }

    }

    private int[] makeRlist() {
        int rightValue = 50;
        int right = 1;

        int[] list = new int[rooms.size()];

        for (int i = 0; i < rooms.size(); i++) {
            if (rooms.get(i).startPoint.Xposition < rightValue) {
                right = i;
            }
        }

        Room[] roomsTemp = new Room[rooms.size()];
        for (int i = 0; i < roomsTemp.length; i++) {
            roomsTemp[i] = rooms.get(i);
        }
        Room tmp = roomsTemp[right];
        roomsTemp[right] = null;
        list[0] = right;

        int next;
        double best;
        double v;

        for (int i = 0; i < roomsTemp.length; i++) {
            next = i;
            best = 9999;
            for (int j = 0; j < roomsTemp.length; j++) {
                if (roomsTemp[j] != null) {
                    v = (Math.abs(tmp.startPoint.Xposition - roomsTemp[j].startPoint.Xposition))
                            + (Math.abs(tmp.startPoint.Yposition - roomsTemp[j].startPoint.Yposition));

                    if (1 - v < best) {
                        next = j;
                        best = v;
                    }
                }
            }
            tmp = roomsTemp[next];
            roomsTemp[next] = null;
            list[i] = next;

        }

        return list;

    }

    private int getAbsInt() {
        return Math.abs(MT.nextInt());
    }

    private int getAbsInt(int c) {
        if (c == 0) {
            return 0;
        }
        return Math.abs(MT.nextInt(c));
    }

    private int getInt() {
        return MT.nextInt();
    }

    private int getInt(int c) {
        if (c == 0) {
            return 0;
        }
        return MT.nextInt(c);

    }

    public void changeType(int r, int t) throws IOException {
        rooms.get(r).clearRoom();
        rooms.get(r).setType(t);
        MP.repaintMap();
    }

    class Room {

        ArrayList<Square> roomSquares;
        Square startPoint;
        Square midPoint;
        int w;
        int h;
        Type type;
        ArrayList<Item> items;
        int roomSize;

        private Room(int h, int w, int x, int y) {
            this.h = h;
            this.w = w;
            startPoint = squares[x][y];
            midPoint = squares[x + (w / 2)][y + (h / 2)];
            roomSquares = new ArrayList<Square>();
            items = new ArrayList<Item>();
        }

        private void clearRoom() {
            for (int i = startPoint.Xposition; i < w + startPoint.Xposition; i++) {
                for (int j = startPoint.Yposition; j < h + startPoint.Yposition; j++) {
                    squares[i][j].data = GROUND_STONE;
                }
            }
        }

        private void roomSave() {
            for (int i = startPoint.Xposition; i < w + startPoint.Xposition; i++) {
                for (int j = startPoint.Yposition; j < h + startPoint.Yposition; j++) {
                    roomSquares.add(squares[i][j]);
                    squares[i][j].data = GROUND_STONE;
                }
            }
            roomSize = roomSquares.size();
        }

        private void setType(int t) throws FileNotFoundException, IOException {
            type = new Type(t);
        }

        class Type {

            String[] itemStrings;
            int c;
            int type;

            public Type(int i) throws FileNotFoundException, IOException {
                type = i;
                if (i == KITCHEN) {
                    makeKitchen();
                }

                if (i == LIVINGROOM) {
                    makeLivingRoom();
                }

                if (i == ARMORYROOM) {
                    makeArmory();
                }
            }

            private void makeKitchen() throws FileNotFoundException, IOException {

                items = kitchenItems();
                setLocation();

            }

            private void makeLivingRoom() {
                items = livingRoomItems();
                setLocation();
            }

            private void makeArmory() {
                items = armoryItems();
                setLocation();
            }

            private ArrayList<Item> kitchenItems() {
                ArrayList<Item> a = new ArrayList<Item>();

                for (int i = 0; i < roomSize / 18; i++) {
                    a.add(new Item(FIRE));
                }
                for (int i = 0; i < roomSize / 15; i++) {
                    a.add(new Item(CUPBOARD));
                }
                for (int i = 0; i < roomSize / 4; i++) {
                    if (i % 4 == 0) {
                        a.add(new Item(TABLE));
                    } else {
                        a.add(new Item(CHAIR));
                    }
                }

                return a;
            }

            private ArrayList<Item> livingRoomItems() {
                ArrayList<Item> a = new ArrayList<Item>();

                for (int i = 0; i < roomSize / 54; i++) {
                    a.add(new Item(FIRE));
                }
                for (int i = 0; i < roomSize / 15; i++) {
                    a.add(new Item(CUPBOARD));
                }
                for (int i = 0; i < roomSize / 3; i++) {
                    if (i % 4 == 0) {
                        a.add(new Item(CHEST));
                    } else {
                        a.add(new Item(BED));
                    }
                }

                return a;
            }

            private ArrayList<Item> armoryItems() {
                ArrayList<Item> a = new ArrayList<Item>();

                for (int i = 0; i < roomSize / 8; i++) {
                    a.add(new Item(ARMORY));
                }
                for (int i = 0; i < roomSize / 15; i++) {
                    a.add(new Item(CUPBOARD));
                }
                for (int i = 0; i < roomSize / 12; i++) {
                    if (i % 4 > 1) {
                        a.add(new Item(CHEST));
                    } else {
                        a.add(new Item(TABLE));
                        a.add(new Item(CHAIR));
                    }
                }

                return a;
            }

            private void setLocation() {
                ArrayList<Item> itlist = (ArrayList<Item>) items.clone();
                Item it;
                int counter = 0;
                int b = 0;
                int x = 0;
                int y = 0;
                while (!itlist.isEmpty()) {
                    counter++;
                    it = itlist.get(0);

                    if (it.name == FIRE) {
                        x = midPoint.Xposition + (getInt((w / 3)));
                        y = midPoint.Yposition + (getInt((h / 3)));

                    }

                    if (it.name == CUPBOARD || it.name == ARMORY) {

                        int tmp = getAbsInt() % 4;

                        if (tmp == 0) {
                            x = startPoint.Xposition;
                            y = midPoint.Yposition + getInt() % (h / 2);
                        }

                        if (tmp == 1) {
                            x = startPoint.Xposition + w - 1;
                            y = midPoint.Yposition + getInt() % (h / 2);
                        }
                        if (tmp == 2) {
                            x = midPoint.Xposition + getInt() % (w / 2);
                            y = startPoint.Yposition;
                        }
                        if (tmp == 3) {
                            x = midPoint.Xposition + getInt() % (w / 2);
                            y = startPoint.Yposition + h - 1;
                        }

                    }

                    if (it.name == TABLE || it.name == BED) {
                        int in = getAbsInt(roomSize);
                        x = roomSquares.get(in).Xposition;
                        y = roomSquares.get(in).Yposition;
                        b = 1;
                    }

                    if (it.name == CHAIR || it.name == CHEST) {
                        if (b == 5) {
                            items.remove(itlist.remove(0));
                        }

                        if (b == 4) {
                            x++;
                            y--;
                        }
                        if (b == 3) {
                            x--;
                            y--;
                        }
                        if (b == 2) {
                            x--;
                            y++;
                        }
                        if (b == 1) {
                            x++;
                        }
                        b++;
                    }

                    if (counter > 200) {
                        int in = getAbsInt(roomSize);
                        x = roomSquares.get(in).Xposition;
                        y = roomSquares.get(in).Yposition;
                    }

                    if (x > 0 && y > 0 && squares[x][y].data < 6 && roomSquares.contains(squares[x][y])) {
                        it.x = x;
                        it.y = y;
                        squares[x][y].data = it.name;
                        itlist.remove(0);
                        counter = 0;
                    }

                }
            }

            private void saveItem(Item it) {
                Square s;
                for (int i = 0; i < roomSize; i++) {
                    s = roomSquares.get(i);
                    if (s.Xposition == it.x && s.Yposition == it.y) {
                        s.data = it.name;
                    }
                }
            }

        }

    }

    class Item {

        int x;
        int y;

        int name;
        Item[] succ;

        public Item(String s) {
            String[] strs;
            strs = s.split(",");
            name = Integer.parseInt(strs[0]);
            succ = new Item[Integer.parseInt(strs[1])];
        }

        public Item(int i) {
            name = i;
        }
    }

}
