/*
 * This file is a part of the SteamWar software.
 *
 * Copyright (C) 2021  SteamWar.de-Serverteam
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.teamserver.command;

import de.steamwar.command.SWCommand;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.inventory.SWListInv;
import de.steamwar.teamserver.Builder;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class MaterialCommand extends SWCommand implements Listener {

    public MaterialCommand() {
        super("material");
    }

    private List<MaterialData> materialData = new ArrayList<>();
    private Map<Player, Search> searchMap = new HashMap<>();

    {
        for (Material value : Material.values()) {
            if (!value.isBlock()) {
                continue;
            }
            if (value.isLegacy()) {
                continue;
            }
            materialData.add(new MaterialData(value));
        }
    }

    private static class MaterialData {
        private Material material;
        private Material originalMaterial;

        private String name;
        private double blastResistance;
        private double hardness;
        private boolean transparent;
        private boolean solid;
        private boolean gravity;
        private boolean occluding;
        private boolean interacteable;
        private boolean flammable;
        private boolean burnable;
        private boolean waterloggable;

        public MaterialData(Material material) {
            this.originalMaterial = material;

            name = material.name();
            blastResistance = material.getBlastResistance();
            hardness = material.getHardness();
            transparent = material.isTransparent();
            solid = material.isSolid();
            gravity = material.hasGravity();
            occluding = material.isOccluding();
            interacteable = material.isInteractable();
            flammable = material.isFlammable();
            burnable = material.isBurnable();
            BlockData blockData = material.createBlockData();
            waterloggable = blockData instanceof Waterlogged;

            if (material.isItem() && material != Material.AIR) {
                this.material = material;
            } else {
                this.material = Material.GHAST_TEAR;
            }
        }

        public SWListInv.SWListEntry<Material> toSWItem(Player p) {
            List<String> lore = new ArrayList<>();
            lore.add(Builder.MESSAGE.parse("MATERIAL_BLAST-RESISTANCE", p, blastResistance));
            lore.add(Builder.MESSAGE.parse(blastResistance > 9 ? "MATERIAL_TNT_UNBREAKABLE" : "MATERIAL_TNT_BREAKABLE", p));
            lore.add(Builder.MESSAGE.parse("MATERIAL_HARDNESS", p, hardness));
            if (transparent) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_TRANSPARENT", p));
            }
            if (solid) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_SOLID", p));
            }
            if (gravity) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_GRAVITY", p));
            }
            if (occluding) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_OCCLUDING", p));
            }
            if (interacteable) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_INTERACT-ABLE", p));
            }
            if (flammable) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_FLAMMABLE", p));
            }
            if (burnable) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_BURNABLE", p));
            }
            if (waterloggable) {
                lore.add(Builder.MESSAGE.parse("MATERIAL_WATERLOGGABLE", p));
            }
            return new SWListInv.SWListEntry<>(new SWItem(material, "§e" + name, lore, false, clickType -> {
            }), originalMaterial);
        }

        public boolean is(Search search) {
            boolean result = true;
            if (search.transparent) {
                result &= transparent;
            }
            if (search.solid) {
                result &= solid;
            }
            if (search.gravity) {
                result &= gravity;
            }
            if (search.occluding) {
                result &= occluding;
            }
            if (search.interacteable) {
                result &= interacteable;
            }
            if (search.flammable) {
                result &= flammable;
            }
            if (search.burnable) {
                result &= burnable;
            }
            if (search.waterloggable) {
                result &= waterloggable;
            }
            if (!result) {
                return false;
            }
            if (!(blastResistance >= search.blastResistanceMin && blastResistance < search.blastResistanceMax)) {
                return false;
            }
            if (search.name.isEmpty()) {
                return true;
            }
            return Pattern.compile(".*" + search.name.toLowerCase().replace(' ', '.') + ".*").asPredicate().test(name.toLowerCase());
        }
    }

    private static class Search {
        private boolean transparent = false;
        private boolean solid = false;
        private boolean gravity = false;
        private boolean occluding = false;
        private boolean interacteable = false;
        private boolean flammable = false;
        private boolean burnable = false;
        private boolean waterloggable = false;

        private double blastResistanceMin = 0;
        private double blastResistanceMax = 5000000;
        private String name = "";
    }

    @Register
    public void materialGUI(Player p) {
        List<SWListInv.SWListEntry<Material>> swListEntries = new ArrayList<>();
        materialData.forEach(data -> {
            if (data.is(searchMap.get(p))) {
                swListEntries.add(data.toSWItem(p));
            }
        });
        SWListInv<Material> materialSWListInv = new SWListInv<>(p, Builder.MESSAGE.parse("MATERIAL_INV_NAME", p, swListEntries.size(), materialData.size()), false, swListEntries, (clickType, material) -> {
        });
        materialSWListInv.setItem(49, new SWItem(Material.NAME_TAG, Builder.MESSAGE.parse("MATERIAL_SEARCH", p), clickType -> {
            searchGUI(p);
        }));
        materialSWListInv.open();
    }

    private void searchGUI(Player p) {
        SWInventory swInventory = new SWInventory(p, 54, Builder.MESSAGE.parse("MATERIAL_SEARCH", p));
        Search search = searchMap.get(p);
        swInventory.setItem(45, new SWItem(Material.ARROW, Builder.MESSAGE.parse("MATERIAL_BACK", p), clickType -> {
            materialGUI(p);
        }));
        swInventory.setItem(10, new SWItem(Material.NAME_TAG, Builder.MESSAGE.parse("MATERIAL_SEARCH_NAME", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.name), clickType -> {
            SWAnvilInv swAnvilInv = new SWAnvilInv(p, Builder.MESSAGE.parse("MATERIAL_SEARCH_NAME", p), search.name);
            swAnvilInv.setCallback(s -> {
                search.name = s;
                searchGUI(p);
            });
            swAnvilInv.open();
        }));
        swInventory.setItem(19, new SWItem(Material.GLASS, Builder.MESSAGE.parse("MATERIAL_SEARCH_TRANSPARENT", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.transparent), clickType -> {
            search.transparent = !search.transparent;
            searchGUI(p);
        }));
        swInventory.setItem(20, new SWItem(Material.BRICK, Builder.MESSAGE.parse("MATERIAL_SEARCH_SOLID", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.solid), clickType -> {
            search.solid = !search.solid;
            searchGUI(p);
        }));
        swInventory.setItem(21, new SWItem(Material.BLACK_CONCRETE_POWDER, Builder.MESSAGE.parse("MATERIAL_SEARCH_GRAVITY", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.gravity), clickType -> {
            search.gravity = !search.gravity;
            searchGUI(p);
        }));
        swInventory.setItem(22, new SWItem(Material.BIRCH_LOG, Builder.MESSAGE.parse("MATERIAL_SEARCH_OCCLUDING", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.occluding), clickType -> {
            search.occluding = !search.occluding;
            searchGUI(p);
        }));
        swInventory.setItem(23, new SWItem(Material.OAK_BUTTON, Builder.MESSAGE.parse("MATERIAL_SEARCH_INTERACTEABLE", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.interacteable), clickType -> {
            search.interacteable = !search.interacteable;
            searchGUI(p);
        }));
        swInventory.setItem(24, new SWItem(Material.FLINT_AND_STEEL, Builder.MESSAGE.parse("MATERIAL_SEARCH_FLAMMABLE", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.flammable), clickType -> {
            search.flammable = !search.flammable;
            searchGUI(p);
        }));
        swInventory.setItem(25, new SWItem(Material.LAVA_BUCKET, Builder.MESSAGE.parse("MATERIAL_SEARCH_BURNABLE", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.burnable), clickType -> {
            search.burnable = !search.burnable;
            searchGUI(p);
        }));
        swInventory.setItem(28, new SWItem(Material.WATER_BUCKET, Builder.MESSAGE.parse("MATERIAL_SEARCH_WATERLOGGABLE", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.waterloggable), clickType -> {
            search.waterloggable = !search.waterloggable;
            searchGUI(p);
        }));
        swInventory.setItem(30, new SWItem(Material.FIRE_CHARGE, Builder.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE_MIN", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.blastResistanceMin), clickType -> {
            SWAnvilInv swAnvilInv = new SWAnvilInv(p, Builder.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p), search.blastResistanceMin + "");
            swAnvilInv.setCallback(s -> {
                try {
                    search.blastResistanceMin = Double.parseDouble(s);
                    if (search.blastResistanceMin < 0) search.blastResistanceMin = 0;
                    if (search.blastResistanceMin > 5000000) search.blastResistanceMin = 5000000;
                    if (search.blastResistanceMin > search.blastResistanceMax)
                        search.blastResistanceMin = search.blastResistanceMax;
                } catch (NumberFormatException e) {
                    // Ignored
                }
                searchGUI(p);
            });
            swAnvilInv.open();
        }));
        swInventory.setItem(31, new SWItem(Material.TNT, Builder.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE_MAX", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.blastResistanceMax), clickType -> {
            SWAnvilInv swAnvilInv = new SWAnvilInv(p, Builder.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p), search.blastResistanceMax + "");
            swAnvilInv.setCallback(s -> {
                try {
                    search.blastResistanceMax = Double.parseDouble(s);
                    if (search.blastResistanceMax > 5000000) search.blastResistanceMax = 5000000;
                    if (search.blastResistanceMax < 0) search.blastResistanceMax = 0;
                    if (search.blastResistanceMax < search.blastResistanceMin)
                        search.blastResistanceMax = search.blastResistanceMin;
                } catch (NumberFormatException e) {
                    // Ignored
                }
                searchGUI(p);
            });
            swAnvilInv.open();
        }));
        swInventory.setItem(32, new SWItem(Material.NETHER_BRICK, Builder.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE_EXACT", p) + Builder.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.blastResistanceMin + "-" + search.blastResistanceMax), clickType -> {
            SWAnvilInv swAnvilInv = new SWAnvilInv(p, Builder.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p), search.blastResistanceMax + "");
            swAnvilInv.setCallback(s -> {
                try {
                    search.blastResistanceMax = Double.parseDouble(s);
                    if (search.blastResistanceMax > 5000000) search.blastResistanceMax = 5000000;
                    if (search.blastResistanceMax < 0) search.blastResistanceMax = 0;
                    search.blastResistanceMin = search.blastResistanceMax;
                } catch (NumberFormatException e) {
                    // Ignored
                }
                searchGUI(p);
            });
            swAnvilInv.open();
        }));
        swInventory.open();
    }

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        searchMap.put(event.getPlayer(), new Search());
    }

    @EventHandler
    public void onPlayerQuit(PlayerQuitEvent event) {
        searchMap.remove(event.getPlayer());
    }
}
