import java.util.Arrays;


// -------------------------------------------------------------------------
// STATE-BASED EXECUTION
//
// Unlike Volume 1, this model is not driven by boarding ticks.
//
// Volume 1 demonstrates continuous aisle movement, where each boarding tick
// may move many passengers simultaneously.
//
// Volume 2 begins only after the target passenger has reached the assigned
// row. The simulation therefore records logical cabin states rather than
// elapsed boarding ticks.
//
// Each printed state represents a meaningful computational transition:
//
// State 1 : Passenger reaches assigned row.
// State 2 : Blockers stand.
// State 3 : Blockers temporarily occupy aisle.
// State 4 : Target passenger enters assigned seat.
// State 5 : Blockers reseat.
// State 6 : Cabin restored.
//
// This event-driven representation is easier to understand because every
// printed state corresponds to a real cabin operation.
// -------------------------------------------------------------------------





/*
================================================================================
AIRPORT BOARDING SIMULATION - ADVANCED CABIN DYNAMICS (VOLUME 2)
PUBLICATION EDITION REFERENCE IMPLEMENTATION - ANNOTATED CODE
================================================================================
Purpose:
- Reuse the Volume 1 one-dimensional aisle movement backbone.
- Add local economy seat-bank interference.
- Use one independent aisle dataset for each physical aisle.
- Do not allow cross-aisle deviation after the passenger has chosen/entered an aisle.
- Model people movement only before take-off.
- Include single-blocker and multiple-blocker handling.

OUTPUT NOTATION - VOLUME 2
Volume 2 uses two different datasets:

aisle[]
 0 = empty aisle tile
 1 = arriving passenger currently in the aisle
 4 = seated blocker temporarily occupying an aisle tile

seatBank[]
 0 = empty seat
 1 = seated passenger
 2 = target passenger seated
 3 = blocking passenger standing/yielding before entering the aisle

Important distinction:
The same number can appear in both datasets, but it must be read according to
the dataset being printed. For example, aisle value 1 means "arriving passenger
in aisle", while seat value 1 means "seated passenger".

ARCHITECTURAL NOTE - VOLUME 2 DATASETS
Volume 1 uses one movement dataset: aisle[].
Volume 2 adds a second kind of dataset: seatBank[].

These datasets have different meanings:
- aisle[] models movement, temporary congestion and temporary aisle reoccupation.
- seatBank[] models local seat occupancy around the destination row.

Within the airport domain, clustering remains primarily an aisle concept. It
describes passengers becoming locally grouped while movement is blocked or slowed.

The seatBank[] dataset is not a traffic-clustering model. It is a local occupancy
model used to identify seat-access interference: which seated passengers must
temporarily yield so the target passenger can enter the assigned seat.

Therefore, in Volume 2:
- traffic and clustering belong to the aisle dataset;
- occupancy and blocker detection belong to the seat-bank dataset.
================================================================================
*/
public class AirportBoardingAdvancedCabinDynamics_Publication {

    // =========================================================
    // COMMON WITH VOLUME 1 BACKBONE
    // =========================================================
    // The aisle remains a one-dimensional occupancy array.
    // This preserves the original movement principle: an entity occupies a tile,
    // moves only when the next relevant tile is available, and leaves a previous
    // tile empty after movement.
    static final int AISLE_EMPTY = 0;
    static final int ARRIVING_PASSENGER_IN_AISLE = 1;

    // =========================================================
    // NEW IN VOLUME 2 - ADVANCED CABIN DYNAMICS
    // =========================================================
    // A seated passenger can temporarily reoccupy the aisle to allow another
    // passenger to reach an inner seat. This is the main Volume 2 extension.
    static final int TEMPORARY_BLOCKER_IN_AISLE = 4;

    // Seat-bank values are intentionally separate from aisle values.
    // The seat bank does not model moving traffic. It records whether each
    // local seat is empty, seated, occupied by the target passenger, or temporarily
    // standing/yielding during a seat-access interference event.
    static final int SEAT_EMPTY = 0;
    static final int SEATED_PASSENGER = 1;
    static final int TARGET_PASSENGER_SEATED = 2;
    static final int STANDING_YIELDING_PASSENGER = 3;

    public static void main(String[] args) {
        System.out.println("VOLUME 2 - ADVANCED CABIN DYNAMICS");
        printNotationLegend();
        runSingleAisleExample();
        runWideBodyPerAisleExample();

        // Optional teaching edge case. This is not a new guide requirement; it simply
        // demonstrates that the same method also handles another local bank example.
        // It keeps the same modelling boundary: one chosen aisle, one local seat bank,
        // no cross-aisle negotiation and no concurrent opposite-side passenger.
        runRightSideTeachingEdgeExample();
    }

    static void printNotationLegend() {
        System.out.println("Output notation:");
        System.out.println("aisle[]: 0 = empty aisle tile, 1 = arriving passenger in aisle, 4 = temporary blocker in aisle");
        System.out.println("seatBank[]: 0 = empty seat, 1 = seated passenger, 2 = target seated, 3 = standing/yielding blocker");
        System.out.println("Important: the same number has meaning only within its own dataset.\n");
    }

    static void runSingleAisleExample() {
        // 3-3 narrow-body example:
        // A passenger reaches row 13 from the aisle and wants seat E.
        // Seat D is occupied and blocks access; F is irrelevant on the other side of E.
        //
        // This is the simplest Volume 2 extension:
        // one aisle dataset + one local seat-bank dataset + one blocker.
        int[] cabinAisle = {0, 0, 1, 0, 0, 0};
        char[] rightBankDEF = {'D', 'E', 'F'};
        int[] seats = {SEATED_PASSENGER, SEAT_EMPTY, SEATED_PASSENGER};

        simulateSeatInterference(
                "3-3 narrow-body, passenger to 13E",
                cabinAisle,
                2,
                rightBankDEF,
                seats,
                1,
                true
        );
    }

    static void runWideBodyPerAisleExample() {
        // 3-4-3 example:
        // A B C | LEFT AISLE | D E F G | RIGHT AISLE | H J K
        //
        // The centre bank may theoretically be approached from either side, but
        // this publication deliberately treats the chosen side as fixed for the
        // local interaction. A passenger does not discover a blockage and then
        // switch to the opposite aisle.
        //
        // This example uses the left aisle and target seat G.
        // D and E are occupied blockers.
        // F is empty/pass-through.
        // G is the target seat.
        //
        // Dataset interpretation:
        // leftAisle[] is movement traffic.
        // middleBankDEFG[] is local occupancy.
        int[] leftAisle = {0, 0, 1, 0, 0, 0, 0};
        int[] rightAisle = {0, 0, 0, 0, 0, 0, 0}; // retained to show independent dataset concept

        char[] middleBankDEFG = {'D', 'E', 'F', 'G'};
        int[] seatsFromLeftSide = {
                SEATED_PASSENGER,
                SEATED_PASSENGER,
                SEAT_EMPTY,
                SEAT_EMPTY
        };

        simulateSeatInterference(
                "3-4-3 centre bank approached from LEFT aisle, target G",
                leftAisle,
                2,
                middleBankDEFG,
                seatsFromLeftSide,
                3,
                true
        );
    }

    static void runRightSideTeachingEdgeExample() {
        // OPTIONAL EDGE / TEACHING EXAMPLE:
        //
        // This uses a representative right outer bank from a 3-4-3 layout:
        //
        // A B C | LEFT AISLE | D E F G | RIGHT AISLE | H I J
        //
        // Seat lettering note:
        // Seat lettering is illustrative for the purposes of this simulation.
        // Some real airline seat maps skip the letter I to avoid confusion with 1,
        // while others may use sequential lettering. The algorithm does not depend
        // on the airline-specific lettering convention; it depends only on local
        // seat order relative to the chosen aisle.
        //
        // The right outer bank is treated as a local seat bank served by the right
        // aisle. The target passenger wants J. Seats H and I are occupied blockers.
        // Because the aisle is on the left side of this local H-I-J bank, the same
        // left-to-right blocker search is valid:
        //
        // H = blocker closest to aisle
        // I = inner blocker
        // J = target seat
        //
        // This is NOT modelling a simultaneous opposite-aisle conflict. It is still
        // one chosen aisle serving one local seat-bank interaction.
        int[] rightOuterAisle = {0, 0, 1, 0, 0, 0, 0};
        char[] rightOuterBankHIJ = {'H', 'I', 'J'};
        int[] seatsRightOuterBank = {
                SEATED_PASSENGER,
                SEATED_PASSENGER,
                SEAT_EMPTY
        };

        simulateSeatInterference(
                "Teaching edge case: 3-4-3 right outer bank H-I-J, target J",
                rightOuterAisle,
                2,
                rightOuterBankHIJ,
                seatsRightOuterBank,
                2,
                true
        );
    }

    static void simulateSeatInterference(
            String name,
            int[] aisle,
            int rowAisleIndex,
            char[] seatNames,
            int[] seatBank,
            int targetSeatIndex,
            boolean aisleIsLeftOfBank) {

        // These booleans are deliberately explicit.
        // They mirror the Volume 1 style where state flags make the movement
        // interpretation visible rather than hidden inside one large expression.
        boolean hasPassengerReachedRow = true;
        boolean hasSeatInterference = false;
        boolean hasMultipleSeatBlockers = false;
        boolean hasBlockingPassengerEnteredAisle = false;
        boolean hasTargetPassengerEnteredSeat = false;
        boolean hasBlockingPassengerReseated = false;
        boolean hasCabinFlowResumed = false;

        System.out.println("\n--- " + name + " ---");
        printState("Initial", aisle, seatNames, seatBank);

        // Identify which seated passengers block the path from the chosen aisle
        // to the target seat. This is seat-access interference detection, not
        // passenger-traffic clustering.
        //
        // If aisleIsLeftOfBank is true, blockers are all occupied seats before the
        // target index. If false, blockers are all occupied seats after the target.
        int[] blockers = findBlockers(seatBank, targetSeatIndex, aisleIsLeftOfBank);

        hasSeatInterference = blockers.length > 0;
        hasMultipleSeatBlockers = blockers.length > 1;

        if (hasSeatInterference) {
            // First mark blockers as standing/yielding inside the seat dataset.
            // At this instant they have left the seated state but have not yet
            // been placed into temporary aisle tiles.
            for (int i = 0; i < blockers.length; i++) {
                seatBank[blockers[i]] = STANDING_YIELDING_PASSENGER;
            }
            printState("Blockers stand", aisle, seatNames, seatBank);

            // Final Volume 2 update:
            // The blocker closest to the aisle yields farthest rearward.
            // The next inner blocker yields nearer.
            //
            // For two blockers:
            // closest-to-aisle blocker -> +2
            // inner blocker           -> +1
            //
            // This ordering avoids representing two blockers in the same aisle tile
            // and gives the target passenger a logical path into the target seat.
            for (int i = 0; i < blockers.length; i++) {
                int blockerIndex = blockers[i];
                int rearwardOffset = blockers.length - i;
                int yieldTile = rowAisleIndex + rearwardOffset;

                // Boundary guard:
                // The publication examples provide enough aisle space behind the row.
                // This check protects experimental test cases near the rear boundary
                // without changing the intended deterministic logic.
                if (yieldTile < 0 || yieldTile >= aisle.length) {
                    System.out.println("WARNING: blocker from seat " + seatNames[blockerIndex]
                            + " cannot yield to aisle tile +" + rearwardOffset
                            + " because it is outside the local aisle dataset.");
                    continue;
                }

                aisle[yieldTile] = TEMPORARY_BLOCKER_IN_AISLE;
                seatBank[blockerIndex] = SEAT_EMPTY;
                System.out.println("Blocker from seat " + seatNames[blockerIndex]
                        + " yields to aisle tile +" + rearwardOffset);
            }

            hasBlockingPassengerEnteredAisle = true;
            printState("Aisle temporarily reoccupied", aisle, seatNames, seatBank);
        }

        // The arriving passenger leaves the aisle model and enters the seat model.
        //
        // Practical physical note:
        // In reality the arriving passenger may briefly step backward or sideways (it is not a computational state)
        // while blockers stand. That micro-movement is not represented as a separate
        // computational state because it does not change the logical outcome:
        // blockers yield, target sits, blockers reseat, aisle restores.
        aisle[rowAisleIndex] = AISLE_EMPTY;
        seatBank[targetSeatIndex] = TARGET_PASSENGER_SEATED;
        hasTargetPassengerEnteredSeat = true;
        printState("Target passenger seated", aisle, seatNames, seatBank);

        // Reseat in reverse order.
        // Inner blocker returns first; closest-to-aisle blocker returns last.
        // This restores the seatBank[] occupancy and clears the temporary aisle tiles.
        for (int i = blockers.length - 1; i >= 0; i--) {
            int blockerIndex = blockers[i];
            int rearwardOffset = blockers.length - i;
            int yieldTile = rowAisleIndex + rearwardOffset;

            if (yieldTile >= 0 && yieldTile < aisle.length) {
                aisle[yieldTile] = AISLE_EMPTY;
            }

            seatBank[blockerIndex] = SEATED_PASSENGER;
            System.out.println("Blocker returns to seat " + seatNames[blockerIndex]);
        }

        hasBlockingPassengerReseated = true;
        hasCabinFlowResumed = true;
        printState("Cabin flow restored", aisle, seatNames, seatBank);

        // Final boolean summary helps when reading console output.
        // It confirms that the algorithm completed the whole local interaction.
        System.out.println("Boolean summary:");
        System.out.println("hasPassengerReachedRow = " + hasPassengerReachedRow);
        System.out.println("hasSeatInterference = " + hasSeatInterference);
        System.out.println("hasMultipleSeatBlockers = " + hasMultipleSeatBlockers);
        System.out.println("hasBlockingPassengerEnteredAisle = " + hasBlockingPassengerEnteredAisle);
        System.out.println("hasTargetPassengerEnteredSeat = " + hasTargetPassengerEnteredSeat);
        System.out.println("hasBlockingPassengerReseated = " + hasBlockingPassengerReseated);
        System.out.println("hasCabinFlowResumed = " + hasCabinFlowResumed);
    }

    static int[] findBlockers(int[] seatBank, int targetSeatIndex, boolean aisleIsLeftOfBank) {
        // ARCHITECTURAL NOTE - SEAT-BANK INTERFERENCE
        // This method does not perform aisle clustering.
        //
        // In Volume 1, clustering belongs to the aisle because the aisle is the
        // movement dataset. In Volume 2, seatBank[] has a different role: it is
        // scanned only to identify occupied seats that interfere with access to
        // the target seat.
        //
        // Those occupied seats become "blockers". They affect aisle traffic only
        // after they stand and temporarily reoccupy aisle tiles.
        int[] temp = new int[seatBank.length];
        int count = 0;

        if (aisleIsLeftOfBank) {
            for (int i = 0; i < targetSeatIndex; i++) {
                if (seatBank[i] == SEATED_PASSENGER) {
                    temp[count++] = i;
                }
            }
        } else {
            for (int i = seatBank.length - 1; i > targetSeatIndex; i--) {
                if (seatBank[i] == SEATED_PASSENGER) {
                    temp[count++] = i;
                }
            }
        }

        return Arrays.copyOf(temp, count);
    }

    static void printState(String label, int[] aisle, char[] names, int[] seats) {
        // Printing both datasets together is intentional.
        // Read aisle[] and seats[] using their own notation legends.
        // The reader can see when a person leaves the seat dataset, enters the
        // aisle dataset, then later returns to the seat dataset.
        System.out.println(label);
        System.out.println("aisle = " + Arrays.toString(aisle));
        System.out.print("seats = ");
        for (int i = 0; i < seats.length; i++) {
            System.out.print(names[i] + ":" + seats[i] + " ");
        }
        System.out.println();
    }
}
