/*
 * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 *
 *  @author GeyserMC
 *  @link https://github.com/GeyserMC/Geyser
 *
 */

package org.geysermc.connector.utils;

import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;

import java.util.concurrent.TimeUnit;

/**
 * Manages the sending of a cooldown indicator to the Bedrock player as there is no cooldown indicator in Bedrock.
 * Much of the work here is from the wonderful folks from ViaRewind: https://github.com/ViaVersion/ViaRewind
 */
public class CooldownUtils {

    private final static boolean SHOW_COOLDOWN;

    static {
        SHOW_COOLDOWN = GeyserConnector.getInstance().getConfig().isShowCooldown();
    }

    /**
     * Starts sending the fake cooldown to the Bedrock client.
     * @param session GeyserSession
     */
    public static void sendCooldown(GeyserSession session) {
        if (!SHOW_COOLDOWN) return;
        if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used
        // Needs to be sent or no subtitle packet is recognized by the client
        SetTitlePacket titlePacket = new SetTitlePacket();
        titlePacket.setType(SetTitlePacket.Type.SET_TITLE);
        titlePacket.setText(" ");
        session.sendUpstreamPacket(titlePacket);
        session.setLastHitTime(System.currentTimeMillis());
        long lastHitTime = session.getLastHitTime(); // Used later to prevent multiple scheduled cooldown threads
        computeCooldown(session, lastHitTime);
    }

    /**
     * Keeps updating the cooldown until the bar is complete.
     * @param session GeyserSession
     * @param lastHitTime The time of the last hit. Used to gauge how long the cooldown is taking.
     */
    private static void computeCooldown(GeyserSession session, long lastHitTime) {
        if (session.isClosed()) return; // Don't run scheduled tasks if the client left
        if (lastHitTime != session.getLastHitTime()) return; // Means another cooldown has started so there's no need to continue this one
        SetTitlePacket titlePacket = new SetTitlePacket();
        titlePacket.setType(SetTitlePacket.Type.SET_SUBTITLE);
        titlePacket.setText(getTitle(session));
        titlePacket.setFadeInTime(0);
        titlePacket.setFadeOutTime(5);
        titlePacket.setStayTime(2);
        session.sendUpstreamPacket(titlePacket);
        if (hasCooldown(session)) {
            session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
        } else {
            SetTitlePacket removeTitlePacket = new SetTitlePacket();
            removeTitlePacket.setType(SetTitlePacket.Type.SET_SUBTITLE);
            removeTitlePacket.setText(" ");
            session.sendUpstreamPacket(removeTitlePacket);
        }
    }

    private static boolean hasCooldown(GeyserSession session) {
        long time = System.currentTimeMillis() - session.getLastHitTime();
        double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 1.5);
        return cooldown < 1.1;
    }


    private static double restrain(double x, double max) {
        if (x < 0d)
            return 0d;
        return Math.min(x, max);
    }

    private static String getTitle(GeyserSession session) {
        long time = System.currentTimeMillis() - session.getLastHitTime();
        double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 1);

        int darkGrey = (int) Math.floor(10d * cooldown);
        int grey = 10 - darkGrey;
        StringBuilder builder = new StringBuilder("§8");
        while (darkGrey > 0) {
            builder.append("˙");
            darkGrey--;
        }
        builder.append("§7");
        while (grey > 0) {
            builder.append("˙");
            grey--;
        }
        return builder.toString();
    }

}