<commit_before>package au.com.codeka.warworlds.server.handlers;

import org.joda.time.DateTime;
import org.joda.time.Seconds;

import au.com.codeka.common.model.BaseFleet;
import au.com.codeka.common.model.DesignKind;
import au.com.codeka.common.model.ShipDesign;
import au.com.codeka.common.model.Simulation;
import au.com.codeka.common.model.BaseFleet.State;
import au.com.codeka.common.protobuf.Messages;
import au.com.codeka.warworlds.server.EventProcessor;
import au.com.codeka.warworlds.server.RequestException;
import au.com.codeka.warworlds.server.RequestHandler;
import au.com.codeka.warworlds.server.ctrl.EmpireController;
import au.com.codeka.warworlds.server.ctrl.StarController;
import au.com.codeka.warworlds.server.data.DB;
import au.com.codeka.warworlds.server.data.SqlStmt;
import au.com.codeka.warworlds.server.data.Transaction;
import au.com.codeka.warworlds.server.model.DesignManager;
import au.com.codeka.warworlds.server.model.Fleet;
import au.com.codeka.warworlds.server.model.FleetUpgrade;
import au.com.codeka.warworlds.server.model.Sector;
import au.com.codeka.warworlds.server.model.Star;

public class FleetOrdersHandler extends RequestHandler {
    @Override
    protected void post() throws RequestException {
        Messages.FleetOrder fleet_order_pb = getRequestBody(Messages.FleetOrder.class);

        try (Transaction t = DB.beginTransaction()) {
            Simulation sim = new Simulation();
            Star star = new StarController(t).getStar(Integer.parseInt(getUrlParameter("star_id")));
            sim.simulate(star);

            int fleetID = Integer.parseInt(getUrlParameter("fleet_id"));
            int empireID = getSession().getEmpireID();
            for (BaseFleet baseFleet : star.getFleets()) {
                Fleet fleet = (Fleet) baseFleet;
                if (fleet.getID() == fleetID && fleet.getEmpireID() == empireID) {
                    boolean pingEventProcessor = orderFleet(t, star, fleet, fleet_order_pb, sim);
                    new StarController(t).update(star);
                    if (pingEventProcessor) {
                        EventProcessor.i.ping();
                    }
                    break;
                }
            }

            t.commit();
        } catch (Exception e) {
            throw new RequestException(e);
        }
    }

    private boolean orderFleet(Transaction t, Star star, Fleet fleet,
                               Messages.FleetOrder fleet_order_pb,
                               Simulation sim) throws RequestException {
        if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.SET_STANCE) {
            orderFleetSetStance(star, fleet, fleet_order_pb, sim);
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.SPLIT) {
            orderFleetSplit(star, fleet, fleet_order_pb, sim);
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.MERGE) {
            orderFleetMerge(t, star, fleet, fleet_order_pb, sim);
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.MOVE) {
            orderFleetMove(star, fleet, fleet_order_pb, sim);
            return true;
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.BOOST) {
            orderFleetBoost(star, fleet, fleet_order_pb, sim);
        }

        return false;
    }

    private void orderFleetSetStance(Star star, Fleet fleet, Messages.FleetOrder fleet_order_pb, Simulation sim) {
        fleet.setStance(BaseFleet.Stance.fromNumber(fleet_order_pb.getStance().getNumber()));

        // TODO: if we just set it to "aggressive" then assume we just arrived at the star
    }

    private void orderFleetSplit(Star star, Fleet fleet, Messages.FleetOrder fleet_order_pb, Simulation sim) throws RequestException {
        if (fleet.getState() != Fleet.State.IDLE) {
            throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                                       "Cannot split a fleet that is not currently idle.");
        }

        float totalShips = fleet.getNumShips();
        int leftShips = fleet_order_pb.getSplitLeft();
        float rightShips = totalShips - leftShips;
        if (rightShips < 1.0f || leftShips <= 0) {
            return; // can't split to less than 1
        }

        Fleet newFleet = fleet.split(rightShips);
        star.getFleets().add(newFleet);
    }

    private void orderFleetMerge(Transaction t, Star star, Fleet fleet,
                                 Messages.FleetOrder fleet_order_pb,
                                 Simulation sim) throws RequestException {
        if (fleet.getState() != Fleet.State.IDLE) {
            throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                                       "Cannot merge a fleet that is not currently idle.");
        }

        for (BaseFleet baseFleet : star.getFleets()) {
            if (baseFleet.getKey().equals(fleet_order_pb.getMergeFleetKey())) {
                Fleet otherFleet = (Fleet) baseFleet;
                if (otherFleet.getState() != Fleet.State.IDLE) {
                    throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                            "Cannot merge a fleet that is not currently idle.");
                }

                if (!otherFleet.getDesignID().equals(fleet.getDesignID())) {
                    throw new RequestException(400, Messages.GenericError.ErrorCode.CannotMergeFleetDifferentDesign,
                            "Cannot merge two fleets of a different design.");
                }

                String notes = fleet.getNotes();
                if (notes == null && otherFleet.getNotes() != null) {
                    notes = otherFleet.getNotes();
                } else if (otherFleet.getNotes() != null) {
                    notes += "\n" + otherFleet.getNotes();
                }
                fleet.setNumShips(fleet.getNumShips() + otherFleet.getNumShips());
                fleet.setNotes(notes);

                // TODO: probably not the best place for this to go...
                String sql = "DELETE FROM fleets WHERE id = ?";
                try (SqlStmt stmt = t.prepare(sql)) {
                    stmt.setInt(1, otherFleet.getID());
                    stmt.update();
                } catch (Exception e) {
                    throw new RequestException(e);
                }
            }
        }
    }

    private void orderFleetMove(Star star, Fleet fleet, Messages.FleetOrder fleet_order_pb, Simulation sim) throws RequestException {
        if (fleet.getState() != Fleet.State.IDLE) {
            throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                                       "Cannot move a fleet that is not currently idle.");
        }

        Star srcStar = star;
        Star destStar = new StarController().getStar(Integer.parseInt(fleet_order_pb.getStarKey()));
        float distanceInParsecs = Sector.distanceInParsecs(srcStar, destStar);

        ShipDesign design = (ShipDesign) DesignManager.i.getDesign(DesignKind.SHIP, fleet.getDesignID());
        float fuelCost = design.getFuelCost(distanceInParsecs, fleet.getNumShips());<commit_msg>Fix bug with merging fleets.<commit_after>package au.com.codeka.warworlds.server.handlers;

import org.joda.time.DateTime;
import org.joda.time.Seconds;

import au.com.codeka.common.model.BaseFleet;
import au.com.codeka.common.model.DesignKind;
import au.com.codeka.common.model.ShipDesign;
import au.com.codeka.common.model.Simulation;
import au.com.codeka.common.model.BaseFleet.State;
import au.com.codeka.common.protobuf.Messages;
import au.com.codeka.warworlds.server.EventProcessor;
import au.com.codeka.warworlds.server.RequestException;
import au.com.codeka.warworlds.server.RequestHandler;
import au.com.codeka.warworlds.server.ctrl.EmpireController;
import au.com.codeka.warworlds.server.ctrl.StarController;
import au.com.codeka.warworlds.server.data.DB;
import au.com.codeka.warworlds.server.data.SqlStmt;
import au.com.codeka.warworlds.server.data.Transaction;
import au.com.codeka.warworlds.server.model.DesignManager;
import au.com.codeka.warworlds.server.model.Fleet;
import au.com.codeka.warworlds.server.model.FleetUpgrade;
import au.com.codeka.warworlds.server.model.Sector;
import au.com.codeka.warworlds.server.model.Star;

public class FleetOrdersHandler extends RequestHandler {
    @Override
    protected void post() throws RequestException {
        Messages.FleetOrder fleet_order_pb = getRequestBody(Messages.FleetOrder.class);

        try (Transaction t = DB.beginTransaction()) {
            Simulation sim = new Simulation();
            Star star = new StarController(t).getStar(Integer.parseInt(getUrlParameter("star_id")));
            sim.simulate(star);

            int fleetID = Integer.parseInt(getUrlParameter("fleet_id"));
            int empireID = getSession().getEmpireID();
            for (BaseFleet baseFleet : star.getFleets()) {
                Fleet fleet = (Fleet) baseFleet;
                if (fleet.getID() == fleetID && fleet.getEmpireID() == empireID) {
                    boolean pingEventProcessor = orderFleet(t, star, fleet, fleet_order_pb, sim);
                    new StarController(t).update(star);
                    if (pingEventProcessor) {
                        EventProcessor.i.ping();
                    }
                    break;
                }
            }

            t.commit();
        } catch (Exception e) {
            throw new RequestException(e);
        }
    }

    private boolean orderFleet(Transaction t, Star star, Fleet fleet,
                               Messages.FleetOrder fleet_order_pb,
                               Simulation sim) throws RequestException {
        if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.SET_STANCE) {
            orderFleetSetStance(star, fleet, fleet_order_pb, sim);
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.SPLIT) {
            orderFleetSplit(star, fleet, fleet_order_pb, sim);
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.MERGE) {
            orderFleetMerge(t, star, fleet, fleet_order_pb, sim);
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.MOVE) {
            orderFleetMove(star, fleet, fleet_order_pb, sim);
            return true;
        } else if (fleet_order_pb.getOrder() == Messages.FleetOrder.FLEET_ORDER.BOOST) {
            orderFleetBoost(star, fleet, fleet_order_pb, sim);
        }

        return false;
    }

    private void orderFleetSetStance(Star star, Fleet fleet, Messages.FleetOrder fleet_order_pb, Simulation sim) {
        fleet.setStance(BaseFleet.Stance.fromNumber(fleet_order_pb.getStance().getNumber()));

        // TODO: if we just set it to "aggressive" then assume we just arrived at the star
    }

    private void orderFleetSplit(Star star, Fleet fleet, Messages.FleetOrder fleet_order_pb, Simulation sim) throws RequestException {
        if (fleet.getState() != Fleet.State.IDLE) {
            throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                                       "Cannot split a fleet that is not currently idle.");
        }

        float totalShips = fleet.getNumShips();
        int leftShips = fleet_order_pb.getSplitLeft();
        float rightShips = totalShips - leftShips;
        if (rightShips < 1.0f || leftShips <= 0) {
            return; // can't split to less than 1
        }

        Fleet newFleet = fleet.split(rightShips);
        star.getFleets().add(newFleet);
    }

    private void orderFleetMerge(Transaction t, Star star, Fleet fleet,
                                 Messages.FleetOrder fleet_order_pb,
                                 Simulation sim) throws RequestException {
        if (fleet.getState() != Fleet.State.IDLE) {
            throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                                       "Cannot merge a fleet that is not currently idle.");
        }

        for (BaseFleet baseFleet : star.getFleets()) {
            if (baseFleet.getKey().equals(fleet_order_pb.getMergeFleetKey())) {
                Fleet otherFleet = (Fleet) baseFleet;
                if (otherFleet.getState() != Fleet.State.IDLE) {
                    throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                            "Cannot merge a fleet that is not currently idle.");
                }

                if (!otherFleet.getDesignID().equals(fleet.getDesignID())) {
                    throw new RequestException(400, Messages.GenericError.ErrorCode.CannotMergeFleetDifferentDesign,
                            "Cannot merge two fleets of a different design.");
                }

                String notes = fleet.getNotes();
                if (notes == null && otherFleet.getNotes() != null) {
                    notes = otherFleet.getNotes();
                } else if (otherFleet.getNotes() != null) {
                    notes += "\n" + otherFleet.getNotes();
                }
                fleet.setNumShips(fleet.getNumShips() + otherFleet.getNumShips());
                fleet.setNotes(notes);

                // TODO: do something better than just clearing out all upgrades. e.g. keep upgrades that both
                // fleets had
                fleet.getUpgrades().clear();

                // TODO: probably not the best place for this to go...
                String sql = "DELETE FROM fleet_upgrades WHERE fleet_id = ?";
                try (SqlStmt stmt = t.prepare(sql)) {
                    stmt.setInt(1, otherFleet.getID());
                    stmt.update();
                } catch (Exception e) {
                    throw new RequestException(e);
                }

                sql = "DELETE FROM fleets WHERE id = ?";
                try (SqlStmt stmt = t.prepare(sql)) {
                    stmt.setInt(1, otherFleet.getID());
                    stmt.update();
                } catch (Exception e) {
                    throw new RequestException(e);
                }
            }
        }
    }

    private void orderFleetMove(Star star, Fleet fleet, Messages.FleetOrder fleet_order_pb, Simulation sim) throws RequestException {
        if (fleet.getState() != Fleet.State.IDLE) {
            throw new RequestException(400, Messages.GenericError.ErrorCode.CannotOrderFleetNotIdle,
                                       "Cannot move a fleet that is not currently idle.");
        }

        Star srcStar = star;
        Star destStar = new StarController().getStar(Integer.parseInt(fleet_order_pb.getStarKey()));
        float distanceInParsecs = Sector.distanceInParsecs(srcStar, destStar);

        ShipDesign design = (ShipDesign) DesignManager.i.getDesign(DesignKind.SHIP, fleet.getDesignID());
        float fuelCost = design.getFuelCost(distanceInParsecs, fleet.getNumShips());
{'fraction_non_alphanumeric': 0.10002640961309917, 'fraction_numerical': 0.00270698534266473, 'mean_word_length': 2.2365384615384616, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6021892', 'n_tokens_mistral': 4519, 'n_tokens_neox': 4358, 'n_words': 884}