package net.cobem.grizzly.habbohotel.rooms;

import gnu.trove.map.hash.THashMap;
import net.cobem.grizzly.Grizzly;
import net.cobem.grizzly.communication.EventResponse;
import net.cobem.grizzly.communication.HeaderLibrary;
import net.cobem.grizzly.communication.composers.navigation.SendHotelViewComposer;
import net.cobem.grizzly.communication.composers.rooms.*;
import net.cobem.grizzly.habbohotel.pathfinding.AffectedTile;
import net.cobem.grizzly.habbohotel.pathfinding.IPathfinder;
import net.cobem.grizzly.habbohotel.pathfinding.Position;
import net.cobem.grizzly.habbohotel.pathfinding.ghost.Ghost_PathfinderLogic;
import net.cobem.grizzly.habbohotel.pathfinding.ihi.IHI_PathfinderLogic;
import net.cobem.grizzly.habbohotel.rooms.actors.ActorHandler;
import net.cobem.grizzly.habbohotel.rooms.items.FloorItem;
import net.cobem.grizzly.habbohotel.rooms.items.RoomItemCycle;
import net.cobem.grizzly.habbohotel.rooms.items.WallItem;
import net.cobem.grizzly.habbohotel.rooms.models.Model;
import net.cobem.grizzly.habbohotel.rooms.robots.Robot;
import net.cobem.grizzly.habbohotel.rooms.robots.RobotHandler;
import net.cobem.grizzly.habbohotel.sessions.Session;
import net.cobem.grizzly.habbohotel.wired.WiredHandler;
import net.cobem.grizzly.storage.DatabaseResult;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Room {

    public int ID;
    public int Owner;
    public int State;

    public String Floor;
    public String Description;
    public String Model;
    public String Title;
    public String Wallpaper;
    public String Landscape;
    public String OwnerByName;

    private THashMap<Integer, Session> Party = new THashMap<Integer, Session>();
    private List<Integer> RightsHolders = new ArrayList<Integer>();
    private THashMap<Integer, Position> UserPositions = new THashMap<Integer, Position>();

    public Model mModel;

    private IPathfinder Pathfinder;
    private IPathfinder GhostPathfinder;

    private THashMap<Integer, FloorItem> FloorItems = new THashMap<>();
    private THashMap<Integer, WallItem> WallItems = new THashMap<>();

    private ActorHandler ActorHandler;
    private RobotHandler RobotHandler;

    private RoomItemCycle RoomItemCycle;

    private RoomMap RoomMap;

    private WiredHandler WiredHandler;

    public Room(ResultSet Room) {
        //Grizzly.GrabDatabase().SetQuery("SELECT * FROM server_rooms WHERE id = '" + ID + "'");

        //ResultSet Room = Grizzly.GrabDatabase().GrabRow();

        this.FillClass(Room);

        DatabaseResult GrabOwner = Grizzly.getStorage().query("SELECT id FROM users WHERE username = '" + this.OwnerByName + "'");

        this.Owner = GrabOwner.getInt();
    }

    public Room(int ID) {
        DatabaseResult GrabRoom = Grizzly.getStorage().query("SELECT * FROM rooms WHERE id = '" + ID + "'");

        ResultSet Room = GrabRoom.getRow();

        this.FillClass(Room);

        DatabaseResult GrabOwner = Grizzly.getStorage().query("SELECT id FROM users WHERE username = '" + this.OwnerByName + "'");

        this.Owner = GrabOwner.getInt();
    }

    private boolean FillClass(ResultSet Set) {
        try {
            this.ID = Set.getInt("id");
            this.OwnerByName = Set.getString("owner");
            this.State = (Set.getString("state").equals("open") ? 1 : 0);

            this.Floor = Set.getString("floor");
            this.Description = Set.getString("description");
            this.Model = Set.getString("model_name");
            this.Title = Set.getString("caption");
            this.Wallpaper = Set.getString("wallpaper");
            this.Landscape = Set.getString("landscape");

            this.Pathfinder = new IHI_PathfinderLogic();
            this.GhostPathfinder = new Ghost_PathfinderLogic();

            loadRightHolders();

            DatabaseResult GrabItems = Grizzly.getStorage().query("SELECT * FROM items WHERE room_id = '" + this.ID + "'");

            try {
                ResultSet Results = GrabItems.getTable();

                while (Results.next()) {
                    if (Results.getString("wall_pos").length() <= 2) {
                        FloorItems.put(Results.getInt("id"), new FloorItem(Results));
                    } else {
                        WallItems.put(Results.getInt("id"), new WallItem(Results));
                    }
                }
            } catch (SQLException e) {
                //
            }

            this.RoomMap = new RoomMap(this);

            this.ActorHandler = new ActorHandler(this);
            this.RobotHandler = new RobotHandler(this);
            this.RoomItemCycle = new RoomItemCycle(this);

            for (FloorItem Item : FloorItems.values()) {
                if (Item.getBase().Width == 1 && Item.getBase().Length == 1) {
                    if (!Item.getBase().Sitable && !Item.getBase().Walkable && !Item.getBase().Layable) {
                        this.RoomMap.FurniCollisionMap[Item.X][Item.Y] = 0;
                    }

                    if (Item.getBase().Sitable || Item.getBase().Layable) {
                        this.RoomMap.FurniCollisionMap[Item.X][Item.Y] = 2;
                    }
                } else {
                    for (AffectedTile Tile : AffectedTile.GetAffectedTiles(Item.getBase().Length, Item.getBase().Width, Item.X, Item.Y, Item.Rotation)) {
                        if (!Item.getBase().Sitable && !Item.getBase().Walkable && !Item.getBase().Layable) {
                            this.RoomMap.FurniCollisionMap[Tile.X][Tile.Y] = 0;
                        }

                        if (Item.getBase().Sitable || Item.getBase().Layable) {
                            this.RoomMap.FurniCollisionMap[Tile.X][Tile.Y] = 2;
                        }
                    }
                }
            }

            WiredHandler = new WiredHandler(this);

            return true;
        } catch (SQLException Ex) {
            Grizzly.write(Ex.toString());
            return false;
        }
    }

    public THashMap<Integer, Session> getParty() {
        return Party;
    }

    public List<Integer> getRightHolders() {
        return RightsHolders;
    }

    public Model getModel() {
        return Grizzly.getHabboHotel().getModels().get(this.Model);
    }

    public void sendMessage(EventResponse Message) {
        ChannelGroup Room = new DefaultChannelGroup();

        for (Session mSession : getParty().values()) {
            Room.add(mSession.getChannel());
        }

        Room.write(Message);
    }

    public boolean loadRightHolders() {
        DatabaseResult GrabRights = Grizzly.getStorage().query("SELECT * FROM room_rights WHERE room_id = '" + this.ID + "'");

        ResultSet Whatever = GrabRights.getTable();

        try {
            while (Whatever.next()) {
                RightsHolders.add(Whatever.getInt("user_id"));
            }
        } catch (SQLException e) {
        }

        return true;
    }

    public boolean sendStatuses(Session Session) {
        for (Session Actor : this.getParty().values()) {
            Session.sendResponse(Actor.getActor().getStatus());
        }

        return true;
    }

    public EventResponse getActorStatus() {
        return SendRoomUsersComposer.compose(getParty());
    }

    public EventResponse getRoomStatus() {
        EventResponse Message = new EventResponse();


        return Message;
    }

    public THashMap<Integer, Position> getPositions() {
        return UserPositions;
    }

    public ActorHandler getActors() {
        return ActorHandler;
    }

    public RobotHandler getRobots() {
        return RobotHandler;
    }

    public void cleanPositions() {
        List<Integer> PositionsToRemove = new ArrayList<>();

        while (getPositions().entrySet().iterator().hasNext()) //Iterator through all our recorded taken positions
        {
            Map.Entry PositionValues = (Map.Entry) getPositions().entrySet().iterator().next();

            int UserID = (int) PositionValues.getKey();
            Position Position = (Position) PositionValues.getValue();

            Session Session = Grizzly.getHabboHotel().getSessions().getByID(UserID);

            if (Session == null) //This user left, why do we still hold this..?
            {
                PositionsToRemove.add(UserID);
            } else {
                if (Session.getActor().CurrentPosition != Position) {
                    PositionsToRemove.remove(UserID);
                }
            }
        }

        for (int ID : PositionsToRemove) {
            getPositions().remove(ID);
        }
    }

    public void removeUser(Session Session, boolean LeavingHotel) {
        this.getParty().remove(Session.getHabbo().getID());
        this.getPositions().remove(Session.getHabbo().getID());

        if (Grizzly.roleplayEnabled() && LeavingHotel) {
            Session.getRoleplay().startLeaveHotel();
            Session.getActor().CurrentRoom = null;
        } else {
            if (!LeavingHotel) {
                Session.sendResponse(RemoveRoomUserEntityComposer.compose());
            }

            sendMessage(LeaveRoomComposer.compose(Session.getHabbo().getID()));

            if (Session.getActor().GoalRoom == 0 && Session.getChannel().isOpen()) {
                Session.sendResponse(SendHotelViewComposer.compose());
            }

            Session.getActor().CurrentRoom = null;
        }

        if (Grizzly.getConfig().get("grizzly.rooms.log").equals("1")) {
            Grizzly.write(Session.getHabbo().getUsername() + " left room " + this.ID);
        }
    }

    public void removeRobot(Robot Robot) {
        Robot.AI.leave();

        RobotHandler.getRobots().values().remove(Robot);

        EventResponse Message = new EventResponse();

        Message.Initialize(HeaderLibrary.LeaveRoomEvent);
        Message.addString(Robot.ID);

        sendMessage(Message);
    }

    public void removeAllRobots() {
        for (Robot Robot : RobotHandler.getRobots().values()) {
            removeRobot(Robot);
        }
    }

    public boolean refreshRobots() {
        sendMessage(SendRoomRobotsComposer.compose(RobotHandler));
        sendMessage(SendRoomRobotStatusesComposer.compose(RobotHandler));
        return true;
    }

    public boolean generateRobot(Robot Robot) {
        RobotHandler.add(Robot);

        refreshRobots();

        return true;
    }

    public boolean generateItem(int ID) {
        DatabaseResult GrabItems = Grizzly.getStorage().query("SELECT * FROM items WHERE id = '" + ID + "'");

        try {
            ResultSet Results = GrabItems.getTable();

            while (Results.next()) {
                if (Results.getString("wall_pos").length() <= 2) {
                    FloorItems.put(ID, new FloorItem(Results));
                } else {
                    WallItems.put(ID, new WallItem(Results));
                }
            }
        } catch (SQLException e) {
        }

        this.RoomMap.ReGenerateCollisionMap();
        return true;
    }

    public THashMap<Integer, FloorItem> getFloors() {
        return FloorItems;
    }

    public THashMap<Integer, WallItem> getWalls() {
        return WallItems;
    }

    public FloorItem getItemByID(int ID) {
        FloorItem Return = null;

        for (FloorItem Item : FloorItems.values()) {
            if (Item.ID == ID) {
                Return = Item;
            }
        }

        return Return;
    }

    public THashMap<Integer, FloorItem> getItemsByPosition(Position Position) {
        THashMap<Integer, FloorItem> Return = new THashMap<Integer, FloorItem>();

        for (FloorItem Item : FloorItems.values()) {
            if (Item.X == Position.X && Item.Y == Position.Y) {
                Return.put(Item.ID, Item);
            }
        }

        return Return;
    }

    public FloorItem getItemByPosition(Position Position) {
        FloorItem Return = null;

        for (FloorItem Item : FloorItems.values()) {
            if (Item.X == Position.X && Item.Y == Position.Y) {
                Return = Item;
                break;
            }
        }

        return Return;
    }

    public FloorItem getItemByHeight(Position Position, int Key) {
        FloorItem Return = null;

        int ClassKey = 0;

        for (FloorItem Item : FloorItems.values()) {
            if (Item.X == Position.X && Item.Y == Position.Y && ClassKey == Key) {
                Return = Item;
                break;
            }

            ClassKey++;
        }

        return Return;
    }

    public FloorItem getItemForInteraction(Position Position) {
        FloorItem Return = null;

        for (FloorItem Item : getFloors().values()) {
            if (Item.getBase().Width == 1 && Item.getBase().Length == 1) {
                if (Item.X == Position.X && Item.Y == Position.Y) {
                    Return = Item;
                }
            } else {
                for (AffectedTile Tile : AffectedTile.GetAffectedTiles(Item.getBase().Length, Item.getBase().Width, Item.X, Item.Y, Item.Rotation)) {
                    if (Tile.X == Position.X && Tile.Y == Position.Y) {
                        Return = Item;
                    }
                }
            }
        }

        return Return;

    }

    public FloorItem getItemAbove(FloorItem Item) {
        FloorItem Return = null;

        float Height = Item.Height;

        for(FloorItem item : this.getItemsByPosition(new Position(Item.X, Item.Y, 0)).values()) {
            if (item.Height > Height) {
                Return = item;
                break;
            }
        }
        return Return;
    }

    public List<FloorItem> getItemsAbove(FloorItem Item) {
        List<FloorItem> Return = new ArrayList<>();

        float Height = Item.Height;

        for(FloorItem item : this.getItemsByPosition(new Position(Item.X, Item.Y, 0)).values()) {
            if (item.Height > Height) {
                Return.add(item);
            }
        }
        return Return;
    }

    public double getTopHeight(Position Position, FloorItem mItem) {
        double height = 0.0;

        for(FloorItem item : this.getItemsByPosition(Position).values()) {
            if (mItem == null || mItem.ID != item.ID) {
                if (height == 0.0) {
                    if (item.Height == 0.0) {
                        height = item.getBase().StackHeight;
                    } else {
                        height = item.Height + item.getBase().StackHeight;
                    }
                } else if (item.Height > height) {
                    if (item.Height == 0.0) {
                        height = item.getBase().StackHeight;
                    } else {
                        height = item.Height + item.getBase().StackHeight;
                    }
                }
            }
        }

        return height;
    }

    public boolean nonStackableTile(Position Position) {
        for (FloorItem fItem : getItemsByPosition(Position).values()) {
            if (!fItem.getBase().Stackable) {
                return true;
            }
        }

        return false;
    }

    public void cleanTiles() {
        List<Position> removedPositions = new ArrayList<>();

        for(Position Pos : UserPositions.values()) {
            if (!realTileOpen(Pos)) {
                getMap().UserCollisionMap[Pos.X][Pos.Y] = false;

                removedPositions.add(Pos);
            }
        }

        for(Position Pos : removedPositions) {
            UserPositions.remove(Pos);
        }
    }

    private boolean realTileOpen(Position Pos) {
        boolean TileOpen = true;

        for(Session mSession : Party.values()) {
            if (mSession.getChannel().isOpen() && mSession.getActor().CurrentPosition.X == Pos.X && mSession.getActor().CurrentPosition.Y == Pos.Y) {
                TileOpen = false;
            }
        }

        return TileOpen;
    }

    public IPathfinder getPathfinder() {
        return this.Pathfinder;
    }

    public IPathfinder getGhostPathfinder() {
        return this.GhostPathfinder;
    }

    public RoomMap getMap() {
        return RoomMap;
    }

    public WiredHandler getWiredUtility() {
        return WiredHandler;
    }

    public void process() {
        try {
            this.ActorHandler.cycle();
            this.RobotHandler.cycle();
            this.RoomItemCycle.cycle();
        } catch (Exception Ex) {
        }

        for (Position Position : getPositions().values()) {
            getMap().UserCollisionMap[Position.X][Position.Y] = true;
        }

        getMap().GenerateFurniCollisionMap();

        Pathfinder.ApplyCollisionMap(getMap().GenerateCollisionMap(), getMap().GenerateFurniHeightMap());

        this.cleanTiles();
    }
}
