/*
OrpheusMS: MapleStory Private Server based on OdinMS
Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net>
Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import net.server.Channel;
import net.server.Server;
import net.server.World;
import client.MapleCharacter;
import client.MapleStock;
import com.mysql.jdbc.Connection;
import constants.ServerConstants;
import tools.DatabaseConnection;
import tools.MapleLogger;
import tools.Output;
import tools.Pair;
/**
* @author Aaron Weiss
*/
public class MapleStocks {
private static MapleStocks instance;
private ArrayList<MapleStock> stocks = new ArrayList<MapleStock>();
private ArrayList<Pair<String, Integer>> oldTotals = null;
public void add(String name, String ticker, int count, int value) {
this.add(new MapleStock(name, ticker, count, value, 0));
}
public void add(MapleStock ms) {
try {
Connection con = (Connection) DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO maplestocks (`name`, `ticker`, `count`, `value`, `change`) VALUES (?, ?, ?, ?, ?)");
ps.setString(1, ms.getName());
ps.setString(2, ms.getTicker());
ps.setInt(3, ms.getCount());
ps.setInt(4, ms.getValue());
ps.setInt(5, ms.getChange());
} catch (SQLException e) {
Output.print("MapleStocks failed to add a new stock. Check exceptions.log.");
MapleLogger.print(MapleLogger.EXCEPTION_CAUGHT, e);
}
stocks.add(ms);
}
public String tickerOf(int id) {
return stocks.get(id - 1).getTicker();
}
public int indexOf(String ticker) {
int n = 0;
for (MapleStock ms : stocks) {
if (ms.getTicker() == ticker) {
return n;
}
n++;
}
return -1;
}
public int indexOf(MapleStock ms) {
return stocks.indexOf(ms);
}
public int idOf(String ticker) {
return indexOf(ticker) + 1;
}
public int idOf(MapleStock ms) {
return indexOf(ms) + 1;
}
public ArrayList<MapleStock> getStocks() {
return stocks;
}
public int getTotalSold(String ticker) {
int sum = 0;
for (World w : Server.getInstance().getWorlds()) {
for (Channel c : w.getChannels()) {
for (MapleCharacter chr : c.getPlayerStorage().getAllCharacters()) {
for (Pair<String, Integer> pair : chr.getStockPortfolio().toArrayList()) {
if (pair.getLeft() == ticker) {
sum += pair.getRight();
}
}
}
}
}
return sum;
}
public String getNameByTicker(String ticker) {
for (MapleStock ms : stocks) {
if (ms.getTicker().equalsIgnoreCase(ticker)) {
return ms.getName();
}
}
return null;
}
public MapleStock getStock(String ticker) {
for (MapleStock ms : stocks) {
if (ms.getTicker().equalsIgnoreCase(ticker)) {
return ms;
}
}
return null;
}
public boolean stockExists(String ticker) {
for (MapleStock ms : stocks) {
if (ms.getTicker().equalsIgnoreCase(ticker)) {
return true;
}
}
return false;
}
public void loadAll() {
try {
Connection con = (Connection) DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM maplestocks ORDER BY stockid");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
stocks.add(new MapleStock(rs.getString("name"), rs.getString("ticker"), rs.getInt("count"), rs.getInt("value"), rs.getInt("change")));
}
} catch (SQLException ex) {
Output.print("MapleStocks failed to load. Check exceptions.log.");
MapleLogger.print(MapleLogger.EXCEPTION_CAUGHT, ex);
}
}
public void calculateUpdate() {
ArrayList<Pair<String, Integer>> totals = new ArrayList<Pair<String, Integer>>();
for (World w : Server.getInstance().getWorlds()) {
for (Channel c : w.getChannels()) {
for (MapleCharacter chr : c.getPlayerStorage().getAllCharacters()) {
for (Pair<String, Integer> pair : chr.getStockPortfolio().toArrayList()) {
boolean add = true;
for (Pair<String, Integer> totalPair : totals) {
if (pair.getLeft() == totalPair.getLeft()) {
add = false;
totalPair.update(totalPair.getLeft(), totalPair.getRight() + pair.getRight());
}
}
if (add) {
totals.add(pair);
}
}
}
}
}
for (Pair<String, Integer> pair : totals) {
for (Pair<String, Integer> oldPair : oldTotals) {
if (oldPair.getLeft() == pair.getLeft()) {
for (MapleStock ms : stocks) {
if (pair.getLeft() == ms.getTicker()) {
double changeTotal = (pair.getRight() - oldPair.getRight());
if ((((double) Math.abs(changeTotal) / oldPair.getRight()) <= ServerConstants.STOCK_CRASH_THRESHOLD) && changeTotal < 0) {
crashStock(ms);
} else if (((double) Math.abs(changeTotal) / oldPair.getRight()) < ServerConstants.STOCK_DECLINE_THRESHOLD && changeTotal < 0) {
decreaseStock(ms);
} else if (((double) Math.abs(changeTotal) / oldPair.getRight()) > ServerConstants.STOCK_DECLINE_THRESHOLD) {
increaseStock(ms);
}
}
}
}
}
}
oldTotals = totals;
}
public void pushUpdate() {
try {
Connection con = (Connection) DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM maplestocks ORDER BY stockid");
ResultSet rs = ps.executeQuery();
for (MapleStock ms : stocks) {
rs.next();
ps = con.prepareStatement("UPDATE maplestocks SET count = ?, value = ?, change = ? WHERE stockid = ?");
ps.setInt(1, ms.getCount());
ps.setInt(2, ms.getValue());
ps.setInt(3, (ms.getValue() - rs.getInt("value")));
ps.setInt(4, rs.getInt("stockid"));
Output.print(ps.toString());
ps.executeUpdate();
ms.update((ms.getValue() - rs.getInt("value")));
}
} catch (SQLException ex) {
Output.print("MapleStocks failed to update. Check exceptions.log.");
MapleLogger.print(MapleLogger.EXCEPTION_CAUGHT, ex);
}
}
public void crashStock(MapleStock ms) {
if (ServerConstants.ALLOW_STOCK_CRASHES) {
int value = (int) (0.50 * ms.getValue());
if (value < 1) {
value = 1;
}
ms.update(value, value - ms.getValue());
} else {
decreaseStock(ms);
}
}
public void increaseStock(MapleStock ms) {
int value = (int) (1.05 * ms.getValue());
if (value > ServerConstants.STOCK_VALUE_CAP) {
value = ServerConstants.STOCK_VALUE_CAP;
}
ms.update(value, value - ms.getValue());
}
public void decreaseStock(MapleStock ms) {
int value = (int) (0.95 * ms.getValue());
if (value < 1) {
value = 1;
}
ms.update(value, value - ms.getValue());
}
public void clear() {
stocks = null;
System.gc();
stocks = new ArrayList<MapleStock>();
}
public static MapleStocks getInstance() {
return MapleStocks.getInstance(true);
}
public static MapleStocks getInstance(boolean loadFromDb) {
if (instance == null) {
instance = new MapleStocks();
if (loadFromDb) {
instance.loadAll();
}
}
return instance;
}
public static void clearInstance() {
instance.clear();
System.gc();
}
}