import java.util.*;
import java.util.concurrent.*;
import java.util.random.RandomGenerator;

class Ship implements Runnable{

    public String _name;
    public int _capacity;
    public int _anboard;
    public  ShipPort _shipPort;
    public boolean isLoading; //0 - выгружается

    public Ship(String _name, int _capacity, int _anboard, ShipPort shipPort, boolean isLoading) {
        this._name = _name;
        this._capacity = _capacity;
        this._anboard = _anboard;
        this._shipPort = shipPort;
        this.isLoading = isLoading;

        new Thread(this).start();
    }

    @Override
    public void run() {
        if(isLoading){
            _shipPort.Load(this);
        }
        else
            _shipPort.Unload(this);
    }
}

class ShipPort {
    private int _capacity;
    private Integer _storage;
    public final Semaphore _semaphore = new Semaphore(3);

    public ShipPort(int _storage, int _capacity) {
        this._capacity = _capacity;
        this._storage = _storage;
    }

    public void Unload(Ship ship) {

        try {
            System.out.printf("!!!!!!!!!!!!!!'%s' выгрузка %s\n", ship._name, ship._anboard);
            _semaphore.acquire();
            Thread.sleep(2000);

            synchronized (_storage) {
                int maxLoad = 0;
                if (this._storage + ship._anboard > _capacity) {
                    maxLoad = this._capacity - this._storage;
                    this._storage = this._capacity;
                } else {
                    maxLoad = ship._anboard;
                    this._storage += ship._anboard;
                }

                System.out.printf(
                        "'%s' выгрузили %s" +
                                "\n" +
                                "в порту: %s \n" +
                                "очередь = %s \n" +
                                "------------------------\n",
                        ship._name, maxLoad, this._storage, _semaphore.getQueueLength());
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            _semaphore.release();
        }
    }

    public void Load(Ship ship) {

        try {
            System.out.printf("!!!!!!!!!!!!'%s' загрузить %s\n", ship._name, ship._capacity - ship._anboard);
            _semaphore.acquire();

            Thread.sleep(2000);
            synchronized (_storage) {
                int maxLoad = ship._capacity - ship._anboard;

                if (this._storage - maxLoad < 0) {
                    maxLoad -= this._storage;
                    if(this._storage == 0)
                        maxLoad = 0;

                    this._storage = 0;
                } else {
                    ship._anboard += maxLoad;
                    this._storage -= maxLoad;
                }

                System.out.printf(
                    "'%s' загрузили %s" +
                            "\n" +
                            "в порту: %s \n" +
                            "очередь = %s \n" +
                            "------------------------\n",
                    ship._name, maxLoad, this._storage, _semaphore.getQueueLength()
                );
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            _semaphore.release();
        }
    }

}

public class Main {
    public static void main(String[] args) {
        Random random = new Random();

        ShipPort shipPort = new ShipPort(50, 50);
        new Ship("Корабль" + 0, 10, 10, shipPort, false);
        for (int i = 1; i<=10; i++){
            int shipCapacity = random.nextInt(2, 60);
            new Ship("Корабль" + i, shipCapacity, random.nextInt(1, shipCapacity), shipPort, random.nextBoolean());

            //new Ship("Корабль" + i, i*2, i, shipPort, random.nextBoolean());
            //new Ship("Корабль" + i, i*2, i, shipPort, true);
        }
    }
}
