/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.bekvon.bukkit.residence.protection;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;

import com.bekvon.bukkit.residence.protection.ResidenceManager.ChunkRef;

/**
 *
 * @author Administrator
 */
public class CuboidArea {
    protected Location highPoints;
    protected Location lowPoints;

    public CuboidArea(final Location startLoc, final Location endLoc) {
        int highx, highy, highz, lowx, lowy, lowz;
        if (startLoc.getBlockX() > endLoc.getBlockX()) {
            highx = startLoc.getBlockX();
            lowx = endLoc.getBlockX();
        } else {
            highx = endLoc.getBlockX();
            lowx = startLoc.getBlockX();
        }
        if (startLoc.getBlockY() > endLoc.getBlockY()) {
            highy = startLoc.getBlockY();
            lowy = endLoc.getBlockY();
        } else {
            highy = endLoc.getBlockY();
            lowy = startLoc.getBlockY();
        }
        if (startLoc.getBlockZ() > endLoc.getBlockZ()) {
            highz = startLoc.getBlockZ();
            lowz = endLoc.getBlockZ();
        } else {
            highz = endLoc.getBlockZ();
            lowz = startLoc.getBlockZ();
        }
        highPoints = new Location(startLoc.getWorld(), highx, highy, highz);
        lowPoints = new Location(startLoc.getWorld(), lowx, lowy, lowz);
    }

    protected CuboidArea() {
    }

    public static CuboidArea load(final DataInputStream in, final int version) throws IOException {
        final CuboidArea newArea = new CuboidArea();
        final Server server = Bukkit.getServer();
        final World world = server.getWorld(in.readUTF());
        final int highx = in.readInt();
        final int highy = in.readInt();
        final int highz = in.readInt();
        final int lowx = in.readInt();
        final int lowy = in.readInt();
        final int lowz = in.readInt();
        newArea.highPoints = new Location(world, highx, highy, highz);
        newArea.lowPoints = new Location(world, lowx, lowy, lowz);
        return newArea;
    }

    public static CuboidArea load(final Map<String, Object> root, final World world) throws Exception {
        if (root == null) {
            throw new Exception("Invalid residence physical location...");
        }
        final CuboidArea newArea = new CuboidArea();
        final int x1 = (Integer) root.get("X1");
        final int y1 = (Integer) root.get("Y1");
        final int z1 = (Integer) root.get("Z1");
        final int x2 = (Integer) root.get("X2");
        final int y2 = (Integer) root.get("Y2");
        final int z2 = (Integer) root.get("Z2");
        newArea.highPoints = new Location(world, x1, y1, z1);
        newArea.lowPoints = new Location(world, x2, y2, z2);
        return newArea;
    }

    public boolean checkCollision(final CuboidArea area) {
        if (!area.getWorld().equals(this.getWorld())) {
            return false;
        }
        if (area.containsLoc(lowPoints) || area.containsLoc(highPoints) || this.containsLoc(area.highPoints) || this.containsLoc(area.lowPoints)) {
            return true;
        }
        return advCuboidCheckCollision(highPoints, lowPoints, area.highPoints, area.lowPoints);
    }

    public boolean containsLoc(final Location loc) {
        if (loc == null) {
            return false;
        }
        if (!loc.getWorld().equals(highPoints.getWorld())) {
            return false;
        }
        if (lowPoints.getBlockX() <= loc.getBlockX() && highPoints.getBlockX() >= loc.getBlockX()) {
            if (lowPoints.getBlockZ() <= loc.getBlockZ() && highPoints.getBlockZ() >= loc.getBlockZ()) {
                if (lowPoints.getBlockY() <= loc.getBlockY() && highPoints.getBlockY() >= loc.getBlockY()) {
                    return true;
                }
            }
        }
        return false;
    }

    public List<ChunkRef> getChunks() {
        final List<ChunkRef> chunks = new ArrayList<ChunkRef>();
        final Location high = this.highPoints;
        final Location low = this.lowPoints;
        final int lowX = ChunkRef.getChunkCoord(low.getBlockX());
        final int lowZ = ChunkRef.getChunkCoord(low.getBlockZ());
        final int highX = ChunkRef.getChunkCoord(high.getBlockX());
        final int highZ = ChunkRef.getChunkCoord(high.getBlockZ());

        for (int x = lowX; x <= highX; x++) {
            for (int z = lowZ; z <= highZ; z++) {
                chunks.add(new ChunkRef(x, z));
            }
        }
        return chunks;
    }

    public Location getHighLoc() {
        return highPoints;
    }

    public Location getLowLoc() {
        return lowPoints;
    }

    public long getSize() {
        final int xsize = (highPoints.getBlockX() - lowPoints.getBlockX()) + 1;
        final int ysize = (highPoints.getBlockY() - lowPoints.getBlockY()) + 1;
        final int zsize = (highPoints.getBlockZ() - lowPoints.getBlockZ()) + 1;
        return xsize * ysize * zsize;
    }

    public World getWorld() {
        return highPoints.getWorld();
    }

    public int getXSize() {
        return (highPoints.getBlockX() - lowPoints.getBlockX()) + 1;
    }

    public int getYSize() {
        return (highPoints.getBlockY() - lowPoints.getBlockY()) + 1;
    }

    public int getZSize() {
        return (highPoints.getBlockZ() - lowPoints.getBlockZ()) + 1;
    }

    public boolean isAreaWithinArea(final CuboidArea area) {
        return (this.containsLoc(area.highPoints) && this.containsLoc(area.lowPoints));
    }

    public Map<String, Object> save() {
        final Map<String, Object> root = new LinkedHashMap<String, Object>();
        root.put("X1", this.highPoints.getBlockX());
        root.put("Y1", this.highPoints.getBlockY());
        root.put("Z1", this.highPoints.getBlockZ());
        root.put("X2", this.lowPoints.getBlockX());
        root.put("Y2", this.lowPoints.getBlockY());
        root.put("Z2", this.lowPoints.getBlockZ());
        return root;
    }

    public void save(final DataOutputStream out, final int version) throws IOException {
        out.writeUTF(highPoints.getWorld().getName());
        out.writeInt(highPoints.getBlockX());
        out.writeInt(highPoints.getBlockY());
        out.writeInt(highPoints.getBlockZ());
        out.writeInt(lowPoints.getBlockX());
        out.writeInt(lowPoints.getBlockY());
        out.writeInt(lowPoints.getBlockZ());
    }

    private boolean advCuboidCheckCollision(final Location A1High, final Location A1Low, final Location A2High, final Location A2Low) {
        final int A1HX = A1High.getBlockX();
        final int A1LX = A1Low.getBlockX();
        final int A1HY = A1High.getBlockY();
        final int A1LY = A1Low.getBlockY();
        final int A1HZ = A1High.getBlockZ();
        final int A1LZ = A1Low.getBlockZ();
        final int A2HX = A2High.getBlockX();
        final int A2LX = A2Low.getBlockX();
        final int A2HY = A2High.getBlockY();
        final int A2LY = A2Low.getBlockY();
        final int A2HZ = A2High.getBlockZ();
        final int A2LZ = A2Low.getBlockZ();
        if ((A1HX >= A2LX && A1HX <= A2HX) || (A1LX >= A2LX && A1LX <= A2HX) || (A2HX >= A1LX && A2HX <= A1HX) || (A2LX >= A1LX && A2LX <= A1HX)) {
            if ((A1HY >= A2LY && A1HY <= A2HY) || (A1LY >= A2LY && A1LY <= A2HY) || (A2HY >= A1LY && A2HY <= A1HY) || (A2LY >= A1LY && A2LY <= A1HY)) {
                if ((A1HZ >= A2LZ && A1HZ <= A2HZ) || (A1LZ >= A2LZ && A1LZ <= A2HZ) || (A2HZ >= A1LZ && A2HZ <= A1HZ) || (A2LZ >= A1LZ && A2LZ <= A1HZ)) {
                    return true;
                }
            }
        }
        return false;
    }
}
