package cicaprojekt;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Dungeon {
    private int ZPMsToWin = 0;
    private long timeLimit = 0;
    private int mapWidth = 0;
    private int mapHeight = 0;


    /* NOTE: this function assumes that the parameter input is a well-formatted dungeon file.
     * Such file looks like as follows:
     *
     * <map width>x<map height>
     * <time limit in seconds>
     * <empty line>
     * <map matrix line 1>
     * ...
     * ...
     * <map matrix line <map height>>
     * <empty line>
     * <scale y>-<scale x>-<gate y>-<gate x>-<scale trigger weight>
     * ...
     * ...
     * <scale y>-<scale x>-<gate y>-<gate x>-<scale trigger weight>
     *
     * where the map matrix is a matrix of the following chars:
     * W: Wall
     * F: Field
     * Z: Field with a ZPM
     * B: Field with a Box
     * O: Field with ONeill
     * J: Field with Jaffa
     * G: Gate
     * S: Scale
     * X: Gap
     * R: Replicator                                                                            */
    Map<String, Tile> buildDungeon(File input, Display display) throws IOException
    {
        Tile defaultTile = new Field();
        defaultTile.setX(-666);
        defaultTile.setY(-666);

        Tile oneilllocation = defaultTile;
        Tile jaffalocation = defaultTile;
        Tile replicatorlocation = defaultTile;
        try(BufferedReader reader = new BufferedReader(new FileReader(input)))
        {
            String[] sizedata = reader.readLine().split("x");       // read size data at beginning of file
            timeLimit = Long.parseLong(reader.readLine()) * 1000;   // read time limit
            reader.readLine();                                      // throw empty line away
            int width = Integer.parseInt(sizedata[0]);
            int height = Integer.parseInt(sizedata[1]);

            mapWidth = width;   // remember this value
            mapHeight = height;

            Tile[][] dungeon = new Tile[height][width];
            Map<Point, AbstractDrawer> gatescalemap = new HashMap<>();

            String line;
            Gate tempgate = new Gate();
            Scale tempscale = new Scale(tempgate, Integer.MAX_VALUE);
            int scalecount = 0;
            for (int y = 0; y < height; ++y)
            {
                line = reader.readLine().replaceAll("\\s","");  // read line and remove whitespaces
                for (int x = 0; x < width; ++x)
                {
                    Tile tile = null;
                    switch (line.charAt(x))                     // set the dungeon up
                    {
                        case 'W':
                            tile = new Wall();
                            display.addVisual(new WallDrawer((Wall) tile));
                            break;

                        case 'F':
                            tile = new Field();
                            display.addVisual(new FieldDrawer((Field) tile));
                            break;

                        case 'Z':
                            tile = new Field();
                            display.addVisual(new FieldDrawer((Field) tile));
                            tile.setZPMOnTile(new ZPM());
                            ZPMsToWin++;
                            break;

                        case 'B':
                            tile = new Field();
                            display.addVisual(new FieldDrawer((Field) tile));
                            tile.putABox(new Box());
                            break;

                        case 'O':
                            tile = new Field();
                            display.addVisual(new FieldDrawer((Field) tile));
                            oneilllocation = tile;
                            break;

                        case 'J':
                            tile = new Field();
                            display.addVisual(new FieldDrawer((Field) tile));
                            jaffalocation = tile;
                            break;

                        case 'G':
                            tile = tempgate;
                            AbstractDrawer vg = new GateDrawer((Gate) tile);
                            display.addVisual(vg);
                            gatescalemap.put(new Point(x, y), vg);
                            break;

                        case 'S':
                            tile = tempscale;
                            AbstractDrawer vs = new ScaleDrawer((Scale) tile);
                            display.addVisual(vs);
                            gatescalemap.put(new Point(x, y), vs);
                            scalecount++;
                            break;

                        case 'X':
                            tile = new Gap();
                            display.addVisual(new GapDrawer((Gap) tile));
                            break;

                        case 'R':
                            tile = new Field();
                            display.addVisual(new FieldDrawer((Field) tile));
                            replicatorlocation = tile;
                            break;
                    }
                    tile.setY(y); tile.setX(x);
                    dungeon[y][x] = tile;
                }
            }

            reader.readLine();                                  // throw empty line away

            for (int i = 0; i < scalecount; ++i)    // set up scale-gate connections
            {
                String[] scaledata = reader.readLine().split("-");

                int sy = Integer.parseInt(scaledata[0]);
                int sx = Integer.parseInt(scaledata[1]);
                int gy = Integer.parseInt(scaledata[2]);
                int gx = Integer.parseInt(scaledata[3]);
                int triggerweight = Integer.parseInt(scaledata[4]);

                Gate gate = new Gate();
                ((GateDrawer)gatescalemap.get(new Point(gx, gy))).setGate(gate);
                gate.setY(gy);
                gate.setX(gx);

                Scale scale = new Scale(gate, triggerweight);
                ((ScaleDrawer)gatescalemap.get(new Point(sx, sy))).setScale(scale);
                scale.setY(sy);
                scale.setX(sx);

                dungeon[gy][gx] = gate;
                dungeon[sy][sx] = scale;
            }

            /* setting up Tile cross references */
            for (int y = 0; y < height; ++y)
            {
                for (int x = 0; x < width; ++x)
                {
                    if (x-1 >= 0)       // leftwards Tile reference
                        dungeon[y][x].setAdajacentTile(dungeon[y][x-1], Direction.WEST);
                    else
                        dungeon[y][x].setAdajacentTile(null, Direction.WEST);

                    if (x+1 < width)    // rightwards Tile reference
                        dungeon[y][x].setAdajacentTile(dungeon[y][x+1], Direction.EAST);
                    else
                        dungeon[y][x].setAdajacentTile(null, Direction.EAST);

                    if (y+1 < height)   // downwards Tile reference
                        dungeon[y][x].setAdajacentTile(dungeon[y+1][x], Direction.SOUTH);
                    else
                        dungeon[y][x].setAdajacentTile(null, Direction.SOUTH);

                    if (y-1 >= 0)       // upwards Tile reference
                        dungeon[y][x].setAdajacentTile(dungeon[y-1][x], Direction.NORTH);
                    else
                        dungeon[y][x].setAdajacentTile(null, Direction.NORTH);
                }
            }
        }
        catch (Exception e)
        {
            JOptionPane.showMessageDialog(null, "Hiba a dungeon fájl beolvasásakor! Biztosan jól formázott az a fájl? Az alkalmazás leáll.");
            System.exit(1);
            // TODO manage to recover from this error without exiting the application
        }

        Map<String, Tile> playermap = new HashMap<>();
        playermap.put("oneill", oneilllocation);
        playermap.put("jaffa", jaffalocation);
        playermap.put("replicator", replicatorlocation);

        return playermap;
    }

    public int getZPMsToWin() {
        return ZPMsToWin;
    }

    public void setZPMsToWin(int newvalue) {
        this.ZPMsToWin = newvalue;
    }

    public long getTimeLimit() {
        return timeLimit;
    }

    public int getMapWidth() {
        return mapWidth;
    }

    public int getMapHeight() {
        return mapHeight;
    }
}