module menu.browser.replay;

/*
 * This must be refactored: Even though we inherit
 * from BrowserCalledFromMainMenu, we may not implement levelRecent() inout
 * despite the base class's promise. Reason: _matcher requires mutability.
 * For now, we have assert(false) in levelRecent() and thus violate OO.
 * See comment at that method.
 */

import optional;

import basics.globals : dirLevels;
import file.option;
import file.filename;
import file.language;
import file.replay;
import game.harvest;
import gui;
import gui.picker;
import hardware.keyset;
import level.level;
import menu.browser.withlast;
import menu.lastgame;
import menu.repmatch;
import menu.verify;

static import basics.globals;

final class BrowserReplay : BrowserWithLastAndDelete {
private:
    Optional!ReplayToLevelMatcher _matcher; // empty if no replay previewed
    LabelTwo _labelPointedTo;
    TextButton _buttonPlayWithPointedTo;
    TextButton _buttonVerify;

public:
    this()
    {
        super(Lang.browserReplayTitle.transl, basics.globals.dirReplays,
            PickerConfig!(Breadcrumb, ReplayTiler)());
        commonConstructor();
        // Final class calls:
        super.highlight(file.option.replayLastLevel);
    }

    this(Harvest ha, Optional!(const Replay) lastLoaded)
    {
        super(Lang.browserReplayTitle.transl, basics.globals.dirReplays,
            PickerConfig!(Breadcrumb, ReplayTiler)());
        commonConstructor();
        {
            auto stats = new StatsAfterReplay(
                super.newStatsGeom, ha, lastLoaded);
            stats.onSavingManually = (Filename itWasSavedHere) {
                if (itWasSavedHere.guaranteedDirOnly == currentDir) {
                    this.forceReloadOfCurrentDir();
                }
            };
            super.addStatsThenHighlight(stats, file.option.replayLastLevel);
        }
    }

    // Override method with assert(false): Violates fundamental OO principles.
    // We shouldn't inherit from BrowserCalledFromMainMenu as long
    // as that forces us to implement such a levelRecent(). BrowserReplay's
    // caller (the main loop) should get the entire LevelToReplayMatcher
    // instead, then it can start a game from there.
    override @property inout(Level) levelRecent() inout { assert (false); }

    @property ReplayToLevelMatcher matcher()
    in { assert (! _matcher.empty, "call this only when matcher exists"); }
    body { return _matcher.unwrap; }

protected:
    final override void onOnHighlightNone()
    {
        _matcher = null;
        _labelPointedTo.hide();
        _buttonPlayWithPointedTo.hide();
        previewNone();
    }

    final override void onHighlightWithLastGame(Filename fn, bool solved)
    in { assert (fn, "call onHighlightNone() instead"); }
    body {
        _matcher = some(new ReplayToLevelMatcher(fn));
        foreach (lv; matcher.preferredLevel)
            previewLevel(lv);
        _buttonPlayWithPointedTo.shown = matcher.pointedToIsGood;

        if (! solved && ! matcher.pointedToFilename.empty
            && matcher.pointedToFilename.unwrap.rootless.length
            > dirLevels.rootless.length
        ) {
            // We show this even if the level is bad. It's probably
            // most important then
            _labelPointedTo.show();
            _labelPointedTo.value = matcher.pointedToFilename.unwrap.rootless[
                dirLevels.rootless.length .. $];
        }
        else {
            _labelPointedTo.hide();
        }
    }

    final override void onHighlightWithoutLastGame(Filename fn)
    {
        onHighlightWithLastGame(fn, false);
    }

    override void onPlay(Filename fn)
    {
        assert (! _matcher.empty);
        if (matcher.includedIsGood
            // Ideally, we don't choose this silently when included is bad.
            // But how to handle doubleclick on replay then? Thus, for now:
            || matcher.pointedToIsGood
        ) {
            file.option.replayLastLevel = super.fileRecent;
            gotoGame = true;
        }
    }

    override void calcSelf()
    {
        import hardware.mouse;
        super.calcSelf();
        if (_buttonPlayWithPointedTo.execute
            && ! _matcher.empty && matcher.pointedToIsGood
        ) {
            // like onFileSelect, but for pointedTo
            matcher.forcePointedTo();
            file.option.replayLastLevel = super.fileRecent;
            gotoGame = true;
        }
        else if (_buttonVerify.execute) {
            file.option.replayLastLevel = currentDir;
            auto win = new VerifyMenu(currentDir);
            addFocus(win);
        }
    }

    override MsgBox newMsgBoxDelete()
    {
        auto m = new MsgBox(Lang.saveBoxTitleDelete.transl);
        m.addMsg(Lang.saveBoxQuestionDeleteReplay.transl);
        m.addMsg(Lang.saveBoxDirectory.transl~ " " ~ fileRecent.dirRootless);
        m.addMsg(Lang.saveBoxFileName.transl ~ " " ~ fileRecent.file);
        return m;
    }

private:
    void commonConstructor()
    {
        buttonPlayYFromBottom = 100f;
        TextButton newInfo(float x, float y, string caption, KeySet hotkey)
        {
            auto b = new TextButton(new Geom(infoX + x*infoXl/2, y,
                infoXl/2, 40, From.BOTTOM_LEFT));
            b.text = caption;
            b.hotkey = hotkey;
            return b;
        }
        _labelPointedTo = new LabelTwo(new Geom(infoX, infoY + 20f,
            infoXl, 20), "\u27F6"); // unicode long arrow right
        _buttonPlayWithPointedTo = newInfo(1, 100,
            Lang.browserReplayPointedTo.transl, keyMenuEdit);
        _buttonVerify = newInfo(1, 60,
            Lang.browserReplayVerifyDir.transl, keyMenuNewLevel);

        addChildren(_labelPointedTo,
            _buttonPlayWithPointedTo, _buttonVerify);
    }
}
