package de.duehl.swing.ui.io.lock;

/*
 * Copyright 2021 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import de.duehl.basics.io.lock.Lock;
import de.duehl.swing.ui.io.lock.ui.NoLockGainedDialog;

/**
 * Diese Klasse erweitert ein vorhandenes Lock-Objekt um eine bequeme Methode zum wiederholten
 * Versuchen das Lock zu erhalten.
 *
 * @version 1.01     2021-02-09
 * @author Christian Dühl
 */

public abstract class InteractiveProgramLock<T extends Lock> implements Lock {

    /** Verwaltet das Locking der Datei. */
    private final T lock;

    /**
     * Konstruktor.
     *
     * @param lock
     *            Das zu verwendende Lock.
     */
    public InteractiveProgramLock(T lock) {
        this.lock = lock;
    }

    /**
     * Versucht einen Lock auf die Datei zu erhalten.
     *
     * @return Erfolg
     */
    @Override
    public boolean lock() {
        return lock.lock();
    }

    /**
     * Versucht den vorhandenen Lock wieder zu lösen.
     *
     * @return Erfolg.
     */
    @Override
    public boolean unlock() {
        return lock.unlock();
    }

    /** Getter für die Datei, die gelockt wird oder die zum Lock benutzt wird. */
    @Override
    public String getFilename() {
        return lock.getFilename();
    }

    /**
     * Erstellt die Lockdatei.
     *
     * @param successRunnable
     *            Code der ausgeführt wird, wenn die Lockdatei erfolgreich angelegt werden konnte.
     * @param finallyNoLockGainesRunnable
     *            Code der ausgeführt wird, wenn abschließend keine Lockdatei angelegt werden
     *            konnte.
     * @return Gibt an, ob der Lock erhalten wurde: Der Wert false bedeutet, die Lockdatei ist
     *         bereits vorhanden, also läuft ein anderer Prozess. Falls es die Lockdatei noch nicht
     *         gab und diese angelegt werden konnte, wird true zurückgeliefert. Gab es den Lock
     *         nicht, aber konnte er auch nicht angelegt werden, so wird eine Ausnahme geworfen.
     * @throws IORuntimeException
     *             Wenn die Datei nicht vorhanden ist und trotzdem nicht angelegt werden konnte.
     */
    public void lock(Runnable successRunnable, Runnable finallyNoLockGainesRunnable) {
        boolean lockGained = lock();
        if (lockGained) {
            successRunnable.run();
        }
        else {
            boolean retry = handleNoLockGained();
            if (retry) {
                lock(successRunnable, finallyNoLockGainesRunnable);
            }
            else {
                finallyNoLockGainesRunnable.run();
            }
        }
    }

    private boolean handleNoLockGained() {
        NoLockGainedDialog dialog = new NoLockGainedDialog(
                createNoLockGainedDialogTitle(),
                createNoLockGainedDialogMessage());
        dialog.setVisible(true);
        switch (dialog.getAnswer()) {
            default:
            case CANCEL:
                return false;
            case REMOVE_LOCK_AND_RETRY:
                unlock();
                return true;
            case RETRY_WITHOUT_REMOVING_LOCK:
                return true;
        }
    }

    protected abstract String createNoLockGainedDialogTitle();

    protected abstract String createNoLockGainedDialogMessage();

}
