package engine.weapons;

import engine.Entity;
import log.IGLog;
import sound.MSound;

import java.util.*;

public class Weapon {

    private static final Map<String, Weapon> map = new HashMap<>();

    public static final int RANGED = 0;
    public static final int MELEE  = 1;

    private String name;

    private Entity owner;

    private int type;

    private List<MSound> sounds;
    private int soundsCpt;

    private double range;

    private int minDamage;

    private int maxDamage;

    private long cooldown;
    private boolean cooldownReady;

    private Weapon(String name, int type, List<MSound> sounds, double range,
                  int minDamage, int maxDamage, long cooldown) {
        this.name = name;
        owner = null;
        this.type = type;
        this.sounds = sounds;
        soundsCpt = 0;
        this.range = range;
        this.minDamage = minDamage;
        this.maxDamage = maxDamage;
        this.cooldown = cooldown;
        cooldownReady = true;
    }

    private Weapon(Weapon weapon) {
        name = weapon.name;
        owner = null;
        type = weapon.type;
        sounds = new LinkedList<>(weapon.sounds);
        soundsCpt = 0;
        range = weapon.range;
        minDamage = weapon.minDamage;
        maxDamage = weapon.maxDamage;
        cooldown = weapon.cooldown;
        cooldownReady = true;
    }

    public static void register(String name, int type, Map<String, String> sounds, double range,
                                int minDamage, int maxDamage, long cooldown) {
        if (map.containsKey(name)) {
            throw new IllegalArgumentException("Weapon::register -> " +
                    name + " already registered.");
        }

        if (type != RANGED && type != MELEE) {
            throw new IllegalArgumentException("Weapon::register -> " +
                    name + ", illegal type argument."
            );
        }

        if (minDamage > maxDamage) {
            throw new IllegalArgumentException("Weapon::register -> " +
                    name + ", minimal damage < maximal damage."
            );
        }

        LinkedList<MSound> createdSounds = new LinkedList<>();

        for (String key : sounds.keySet()) {
            String path = sounds.get(key);

            try {
                createdSounds.addLast(new MSound(key, path));
            } catch (Exception e) {
                IGLog.error("Unable to load <" + key + "> at \"" + path + "\"");
            }
        }

        map.put(name, new Weapon(name, type, createdSounds, range, minDamage,
                maxDamage, cooldown));
    }

    public static Weapon make(String name) {
        if (!map.containsKey(name)) {
            throw new IllegalArgumentException("Weapon::make -> " +
                    name + " doesn't exists."
            );
        }

        return new Weapon(map.get(name));
    }

    private void playNextSound() {
        ++soundsCpt;
        if (soundsCpt >= sounds.size())
            soundsCpt = 0;

        if (soundsCpt < sounds.size())
            sounds.get(soundsCpt).play();
    }

    private void launchCooldown() {
        if (cooldownReady) {
            new Thread() {
                @Override
                public void run() {
                    cooldownReady = false;
                    Date wanted = new Date(new Date().getTime() + cooldown);
                    do {
                        try {
                            Thread.sleep(cooldown);
                            break;
                        } catch (InterruptedException e) {
                            if (new Date().after(wanted))
                                break;
                        }
                    } while (true);
                    cooldownReady = true;
                }
            }.start();
        }
    }

    public boolean ready() {
        return cooldownReady;
    }


    public boolean isRanged() {
        return type == RANGED;
    }

    public boolean isMelee() {
        return type == MELEE;
    }

    public Entity getOwner() {
        return owner;
    }

    public void setOwner(Entity owner) {
        this.owner = owner;
    }

    public double getRange() {
        return range;
    }

    public int getMinDamage() {
        return minDamage;
    }

    public int getMaxDamage() {
        return maxDamage;
    }

    public void attack(Entity entity) {
        playNextSound();
        launchCooldown();

        if (entity instanceof Attackable) {
            Random random = new Random();
            Attackable a = (Attackable)entity;

            int damage =
                    random.nextInt(
                            (getMaxDamage() - getMinDamage()) + 1
                    ) + getMinDamage();
            a.takeDamage(damage);
        }
    }


}

@@ -2872,32 +2872,367 @@
 (name));%0A    %7D%0A%0A
+    public static void defaults() %7B%0A%0A        /* Punch */%0A        HashMap%3CString, String%3E punchSounds = new HashMap%3C%3E();%0A        punchSounds.put(%22left-punch%22, %22sounds/left-punch.mp3%22);%0A        punchSounds.put(%22right-punch%22, %22sounds/right-punch.mp3%22);%0A        Weapon.register(%22punch%22, Weapon.MELEE, punchSounds, 10, 1, 2, 1000);%0A%0A    %7D%0A%0A
     private void

add static function to load defaults weapon.