package server;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.io.Serializable;
import constants.GameConstants;
import client.inventory.ItemLoader;
import client.inventory.Item;
import client.MapleClient;
import client.inventory.MapleInventoryType;
import database.DatabaseConnection;
import database.DatabaseException;
import java.util.EnumMap;
import tools.Pair;
import tools.packet.CField.NPCPacket;
public class MapleStorage implements Serializable {
private static final long serialVersionUID = 9179541993413738569L;
private final int id;
private final int accountId;
private final List<Item> items;
private long meso;
private int lastNPC = 0;
private byte slots;
private boolean changed = false;
private final Map<MapleInventoryType, List<Item>> typeItems = new EnumMap<>(MapleInventoryType.class);
private MapleStorage(int id, byte slots, int meso, int accountId) {
this.id = id;
this.slots = slots;
this.items = new LinkedList<>();
this.meso = meso;
this.accountId = accountId;
}
public static int create(int id) throws SQLException {
Connection con = DatabaseConnection.getConnection();
ResultSet rs;
try (PreparedStatement ps = con.prepareStatement("INSERT INTO storages (accountid, slots, meso) VALUES (?, ?, ?)", DatabaseConnection.RETURN_GENERATED_KEYS)) {
ps.setInt(1, id);
ps.setInt(2, 4);
ps.setLong(3, 0);
ps.executeUpdate();
int storageid;
rs = ps.getGeneratedKeys();
if (rs.next()) {
storageid = rs.getInt(1);
ps.close();
rs.close();
return storageid;
}
}
rs.close();
throw new DatabaseException("Inserting char failed.");
}
public static MapleStorage loadStorage(int id) {
MapleStorage ret = null;
int storeId;
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM storages WHERE accountid = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
storeId = rs.getInt("storageid");
ret = new MapleStorage(storeId, rs.getByte("slots"), rs.getInt("meso"), id);
rs.close();
ps.close();
for (Pair<Item, MapleInventoryType> mit : ItemLoader.STORAGE.loadItems(false, id).values()) {
ret.items.add(mit.getLeft());
}
} else {
storeId = create(id);
ret = new MapleStorage(storeId, (byte) 4, 0, id);
rs.close();
ps.close();
}
} catch (SQLException ex) {
System.err.println("Error loading storage" + ex);
}
return ret;
}
public void saveToDB() {
if (!changed) {
return;
}
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?")) {
ps.setInt(1, slots);
ps.setLong(2, meso);
ps.setInt(3, id);
ps.executeUpdate();
}
List<Pair<Item, MapleInventoryType>> listing = new ArrayList<>();
for (final Item item : items) {
listing.add(new Pair<>(item, GameConstants.getInventoryType(item.getItemId())));
}
ItemLoader.STORAGE.saveItems(listing, accountId);
} catch (SQLException ex) {
System.err.println("Error saving storage" + ex);
}
}
public Item takeOut(byte slot) {
if (slot >= items.size() || slot < 0) {
return null;
}
changed = true;
Item ret = items.remove(slot);
MapleInventoryType type = GameConstants.getInventoryType(ret.getItemId());
typeItems.put(type, filterItems(type));
return ret;
}
public void store(Item item) {
changed = true;
items.add(item);
MapleInventoryType type = GameConstants.getInventoryType(item.getItemId());
typeItems.put(type, filterItems(type));
}
public void arrange() { //i believe gms does by itemID
Collections.sort(items, new Comparator<Item>() {
@Override
public int compare(Item o1, Item o2) {
if (o1.getItemId() < o2.getItemId()) {
return -1;
} else if (o1.getItemId() == o2.getItemId()) {
return 0;
} else {
return 1;
}
}
});
for (MapleInventoryType type : MapleInventoryType.values()) {
typeItems.put(type, items);
}
}
public List<Item> getItems() {
return Collections.unmodifiableList(items);
}
private List<Item> filterItems(MapleInventoryType type) {
List<Item> ret = new ArrayList<>();
for (Item item : items) {
if (GameConstants.getInventoryType(item.getItemId()) == type) {
ret.add(item);
}
}
return ret;
}
public byte getSlot(MapleInventoryType type, byte slot) {
// MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
byte ret = 0;
final List<Item> it = typeItems.get(type);
if (it == null || slot >= it.size() || slot < 0) {
return -1;
}
for (Item item : items) {
if (item == it.get(slot)) {
return ret;
}
ret++;
}
return -1;
}
public void sendStorage(MapleClient c, int npcId) {
// sort by inventorytype to avoid confusion
lastNPC = npcId;
Collections.sort(items, new Comparator<Item>() {
@Override
public int compare(Item o1, Item o2) {
if (GameConstants.getInventoryType(o1.getItemId()).getType() < GameConstants.getInventoryType(o2.getItemId()).getType()) {
return -1;
} else if (GameConstants.getInventoryType(o1.getItemId()) == GameConstants.getInventoryType(o2.getItemId())) {
return 0;
} else {
return 1;
}
}
});
for (MapleInventoryType type : MapleInventoryType.values()) {
typeItems.put(type, items);
}
c.getSession().write(NPCPacket.getStorage(npcId, slots, items, meso));
}
public void update(MapleClient c) {
c.getSession().write(NPCPacket.arrangeStorage(slots, items, true));
}
public void sendStored(MapleClient c, MapleInventoryType type) {
c.getSession().write(NPCPacket.storeStorage(slots, type, typeItems.get(type)));
}
public void sendTakenOut(MapleClient c, MapleInventoryType type) {
c.getSession().write(NPCPacket.takeOutStorage(slots, type, typeItems.get(type)));
}
public long getMeso() {
return meso;
}
public Item findById(int itemId) {
for (Item item : items) {
if (item.getItemId() == itemId) {
return item;
}
}
return null;
}
public void setMeso(long meso) {
if (meso < 0) {
return;
}
changed = true;
this.meso = meso;
}
public void sendMeso(MapleClient c) {
c.getSession().write(NPCPacket.mesoStorage(slots, meso));
}
public boolean isFull() {
return items.size() >= slots;
}
public int getSlots() {
return slots;
}
public void increaseSlots(byte gain) {
changed = true;
this.slots += gain;
}
public void setSlots(byte set) {
changed = true;
this.slots = set;
}
public void close() {
typeItems.clear();
}
}