package post.office;

import java.util.*;
import post.Postman;
import post.mail.Letter;

/**
 * Definiáljuk a városi hivatalt ábrázoló Municipal osztályt az office csomagban
 * a post csomagon belül! A városi hivatal számon tartja a székhelyét, az
 * országos postahivatalt, a postásait, és emellett van egy postaládája is,
 * ahova a városban élők bedobhatják a leveleiket, melyet a városi hivatal aztán
 * kézbesít.
 *
 * @author Daniel3ICE
 */
public class Municipal {

    public final String settlement;
    /**
     * 3ICE: Instructions did not specify that the superior field has to be
     * final as well. I believe it should be changeable in case the national
     * post office is restructured.
     *
     * @SuppressWarnings to the rescue.
     */
    @SuppressWarnings("FieldMayBeFinal")
    private National superior;

    private final ArrayList<Postman> postmen;

    private final LinkedList<Letter> mailbox;

    /**
     * Definiáljuk a városi hivatalt ábrázoló Municipal osztályt az office
     * csomagban a post csomagon belül! A városi hivatal számon tartja a
     * székhelyét, az országos postahivatalt, a postásait, és emellett van egy
     * postaládája is, ahova a városban élők bedobhatják a leveleiket, melyet a
     * városi hivatal aztán kézbesít. Ne feledkezzünk meg a postások sorozatáról
     * (postmen) és a levelesládáról (mailbox) sem, melyek kezdetben üres
     * lesznek.
     *
     * @param superior paraméterül várja az országos hivatalt és a székhelyet,
     * @param settlement és ezekkel inicializálja a settlement és superior
     * adattagokat
     */
    public Municipal(National superior, String settlement) {
        /**
         * 3ICE: Parameter order mismatch! Code expects superior-settlement, but
         * the instructions read: paraméterül várja az országos hivatalt és a
         * székhelyet, és ezekkel inicializálja a settlement és superior
         * adattagokat.
         */
        this.settlement = settlement;
        this.superior = superior;
        postmen = new ArrayList<>();
        mailbox = new LinkedList<>();
    }

    /**
     * Amennyiben a hivatalnál még nincs olyan postás, akinek az utcája azonos a
     * paraméterül kapott utcanévvel, úgy hozzunk létre egy új Postman
     * objektumot, szúrjuk a postások sorozatának végére, és adjunk vissza
     * logikai igaz értéket!
     *
     * @param street paraméterül vár egy utcanevet
     * @param even és az utcában található legnagyobb páros
     * @param odd és páratlan házszámot
     * @return Ellenkező esetben (tehát, ha már van postása az utcának), adjunk
     * vissza logikai hamis értéket!
     */
    public boolean hirePostman(String street, int odd, int even) {
        /**
         * 3ICE: Parameter order continuity error! Expected odd-even, given
         * even-odd. Instructions read:<br />
         * és az utcában található legnagyobb páros és páratlan házszámot<br />
         * vs.<br />
         * Privát, egész típusú (int) legnagyobb páratlan és páros házszám
         * (lastOdd és lastEven).
         */
        for (Postman p : postmen) {
            //3ICE: Simplified version: if (street.equals(p.getStreet()))
            if (street == null ? p.getStreet() == null : street.equals(p.getStreet())) {
                return false;
            }
        }
        postmen.add(new Postman(street, odd, even));
        return true;
    }

    /**
     * Ha a levél úton van vissza a feladónak (isReturningToSender() igazat ad),
     * akkor megsemmisíti a levelet annak destroy() metódusával. Különben
     * megjelöli a levelet annak backToSender() metódusával, és beteszi a
     * postaládába (mailbox).
     *
     * @param l paraméterül vár egy levelet
     */
    private void returnOrDestroy(Letter l) {
        if (l.isReturningToSender()) {
            l.destroy();
        } else {
            l.backToSender();
            mailbox.add(l);
        }
    }

    /**
     * Keressük meg a postást, akihez tartozik az az utca, ahova a levelet el
     * kell vinni. Ha nem találtunk ilyen postást, akkor minden bizonnyal a cím
     * rossz, visszaküldjük a feladónak vagy megsemmisítjük a returnOrDestroy()
     * metódus meghívásával.
     *
     * <br/>
     *
     * Ha találtunk postást, aki ki tudja vinni a levelet, adjuk oda neki a
     * deliver() metódusának meghívásával! A deliver() visszaadja, hogy sikerült
     * kézbesíteni a levelet vagy sem. Amennyiben sikerült, úgy készen vagyunk.
     * Amennyiben nem sikerült, visszaküldjük a feladónak vagy megsemmisítjük a
     * levelet returnOrDestroy() metódussal.
     *
     * @param l paraméterül vár egy levelet, melyről tudjuk, hogy a városban
     * kell kézbesíteni
     */
    private void dispatchLocally(Letter l) {
        Postman agent = null;
        for (Postman p : postmen) {
            if (l.whereToDeliver().getStreet().equals(p.getStreet())) {
                agent = p;
            }
        }

        if (agent == null) {
            returnOrDestroy(l);
        } else {
            if (!agent.deliver(l)) {
                returnOrDestroy(l);
            }
        }
    }

    /**
     * Ha a levelet a városban kézbesíteni, akkor a dispatchLocally()
     * kézbesítsük. Különben a levelet más országba vagy városba kell
     * eljuttatni, így átadjuk az országos hivatalnak (superior) annak
     * dispatch() metódusával.
     *
     * @param l egy kézbesítendő levelet vár paraméterül
     */
    protected void dispatch(Letter l) {
        /*DEBUG3ICE*/
        System.out.print("Municipal.dispatch called with " + this + " on " + l + " where country=" + l.whereToDeliver().getCountry());

        if (settlement.equals(l.whereToDeliver().getCity())) {
            /*DEBUG3ICE*/
            System.out.println(". Dispatching locally.");
            dispatchLocally(l);
        } else {
            /*DEBUG3ICE*/
            System.out.println(". Non-local mail, dispatching via superior: " + superior + ".");
            superior.dispatch(l);
        }
    }

    /**
     * Minden levelet betesz a postaládába (mailbox)
     *
     * @param letters várja levelek fix méretű tömbjét
     */
    public void post(Letter[] letters) {
        //for (Letter l : letters) {mailbox.add(l);}
        mailbox.addAll(Arrays.asList(letters));
    }

    /**
     * A dispatch() metódust használva kikézbesíti az összes levelesládában
     * (mailbox) található levelet. Ne felejtsük el eltávolítani a
     * levelesládából a kikézbesített leveleket!
     *
     */
    public void deliverLetters() {
        for (Letter l : mailbox) {
            dispatch(l);
        }
        mailbox.clear();
    }

    public String getSettlement() {
        return settlement;
    }

    @Override
    public String toString() {
        return "Mun(" + settlement + ")";
    }

}
