/*
 * 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.bausystem.region;

import lombok.AllArgsConstructor;
import lombok.Getter;
import yapion.hierarchy.types.YAPIONObject;
import yapion.hierarchy.types.YAPIONType;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Getter
public class Prototype {

    static final Map<String, Prototype> PROTOTYPE_MAP = new HashMap<>();

    public static Prototype getByName(String name) {
        return PROTOTYPE_MAP.get(name);
    }

    public static Prototype getByDisplayName(String name) {
        return PROTOTYPE_MAP.values().stream().filter(prototype -> prototype.getDisplayName().equals(name)).findFirst().orElse(null);
    }

    public static List<Prototype> getPrototypes() {
        return new ArrayList<>(PROTOTYPE_MAP.values());
    }

    @AllArgsConstructor
    @Getter
    public static class Skin {
        private final String name;
        private final String creator; // Nullable
        private final File schematicFile;
        private final File testblockSchematicFile; // Nullable
        private final File buildSchematicFile; // Nullable
    }

    private final String name;
    private final String displayName;
    private final String defaultSkin;
    private final Map<String, Skin> skinMap = new HashMap<>();

    private final int sizeX;
    private final int sizeY;
    private final int sizeZ;

    private final int floorOffset;
    private final int waterOffset;

    private final SubPrototype testblock; // Nullable
    private final SubPrototype build; // Nullable

    private final int copyPointOffsetX;
    private final int copyPointOffsetY;
    private final int copyPointOffsetZ;

    public Prototype(String name, YAPIONObject yapionObject) {
        this.name = name;
        displayName = yapionObject.getPlainValueOrDefault("displayName", name);

        sizeX = yapionObject.getPlainValue("sizeX");
        sizeY = yapionObject.getPlainValue("sizeY");
        sizeZ = yapionObject.getPlainValue("sizeZ");

        copyPointOffsetX = yapionObject.getPlainValueOrDefault("copyOffsetX", 0);
        copyPointOffsetY = yapionObject.getPlainValueOrDefault("copyOffsetY", 0);
        copyPointOffsetZ = yapionObject.getPlainValueOrDefault("copyOffsetZ", 0);

        floorOffset = yapionObject.getPlainValueOrDefault("floorOffset", 0);
        waterOffset = yapionObject.getPlainValueOrDefault("waterOffset", 0);

        if (yapionObject.containsKey("testblock", YAPIONType.OBJECT)) {
            testblock = new SubPrototype(yapionObject.getObject("testblock"));
        } else {
            testblock = null;
        }
        if (yapionObject.containsKey("build", YAPIONType.OBJECT)) {
            build = new SubPrototype(yapionObject.getObject("build"));
        } else {
            build = null;
        }

        this.defaultSkin = yapionObject.getPlainValueOrDefault("defaultSkin", displayName);
        if (yapionObject.containsKey("skins", YAPIONType.ARRAY)) {
            yapionObject.getArray("skins").forEach(yapionAnyType -> {
                YAPIONObject skinObject = (YAPIONObject) yapionAnyType;
                String skinName = skinObject.getPlainValue("name");
                String skinCreator = skinObject.getPlainValueOrDefault("creator", null);
                String schematicFileName = skinObject.getPlainValue("schematic");
                String testblockSchematicFileName = skinObject.getPlainValueOrDefault("testblockSchematic", null);
                String buildSchematicFileName = skinObject.getPlainValueOrDefault("buildSchematic", null);
                boolean disabled = skinObject.getPlainValueOrDefault("disabled", false);
                if (disabled) {
                    return;
                }
                skinMap.put(skinName, new Skin(skinName, skinCreator, new File(schematicFileName), testblockSchematicFileName == null ? null : new File(testblockSchematicFileName), buildSchematicFileName == null ? null : new File(buildSchematicFileName)));
            });
        } else {
            String s = yapionObject.getPlainValueOrDefault("schematic", null);
            File schematicFile = s == null ? null : new File(s);
            File testblockSchematicFile = testblock != null ? testblock.getSchematicFile() : null;
            File buildSchematicFile = build != null ? build.getSchematicFile() : null;
            skinMap.put(displayName, new Skin(defaultSkin, null, schematicFile, testblockSchematicFile, buildSchematicFile));
        }

        if (PROTOTYPE_MAP.containsKey(name)) {
            Region.getRegion(PROTOTYPE_MAP.remove(name)).forEach(region -> {
                region._setPrototype(this);
            });
        }
        PROTOTYPE_MAP.put(name, this);
    }

    @Getter
    public static class SubPrototype {

        private final int offsetX;
        private final int offsetY;
        private final int offsetZ;

        private final int sizeX;
        private final int sizeY;
        private final int sizeZ;

        private final File schematicFile; // Nullable

        private final int extensionNegativeX;
        private final int extensionPositiveX;
        private final int extensionNegativeY;
        private final int extensionPositiveY;
        private final int extensionNegativeZ;
        private final int extensionPositiveZ;

        private boolean extensionRegistered;

        private final boolean hasCopyPoint;
        private final int copyOffsetX;
        private final int copyOffsetY;
        private final int copyOffsetZ;

        private SubPrototype(YAPIONObject yapionObject) {
            offsetX = yapionObject.getPlainValueOrDefault("offsetX", 0);
            offsetY = yapionObject.getPlainValueOrDefault("offsetY", 0);
            offsetZ = yapionObject.getPlainValueOrDefault("offsetZ", 0);

            sizeX = yapionObject.getPlainValue("sizeX");
            sizeY = yapionObject.getPlainValue("sizeY");
            sizeZ = yapionObject.getPlainValue("sizeZ");

            if (yapionObject.containsKey("schematic", String.class)) {
                schematicFile = new File(yapionObject.getValue("schematic", String.class).get());
            } else {
                schematicFile = null;
            }

            if (yapionObject.containsKey("extensionX", Integer.class)) {
                extensionNegativeX = yapionObject.getPlainValue("extensionX");
                extensionPositiveX = yapionObject.getPlainValue("extensionX");
            } else {
                extensionNegativeX = yapionObject.getPlainValueOrDefault("extensionNegativeX", 0);
                extensionPositiveX = yapionObject.getPlainValueOrDefault("extensionPositiveX", 0);
            }

            if (yapionObject.containsKey("extensionY", Integer.class)) {
                extensionNegativeY = yapionObject.getPlainValue("extensionY");
                extensionPositiveY = yapionObject.getPlainValue("extensionY");
            } else {
                extensionNegativeY = yapionObject.getPlainValueOrDefault("extensionNegativeY", 0);
                extensionPositiveY = yapionObject.getPlainValueOrDefault("extensionPositiveY", 0);
            }

            if (yapionObject.containsKey("extensionZ", Integer.class)) {
                extensionNegativeZ = yapionObject.getPlainValue("extensionZ");
                extensionPositiveZ = yapionObject.getPlainValue("extensionZ");
            } else {
                extensionNegativeZ = yapionObject.getPlainValueOrDefault("extensionNegativeZ", 0);
                extensionPositiveZ = yapionObject.getPlainValueOrDefault("extensionPositiveZ", 0);
            }

            extensionRegistered = extensionNegativeX != 0 || extensionPositiveX != 0 || extensionNegativeY != 0 || extensionPositiveY != 0 || extensionNegativeZ != 0 || extensionPositiveZ != 0;

            copyOffsetX = yapionObject.getPlainValueOrDefault("copyOffsetX", 0);
            copyOffsetY = yapionObject.getPlainValueOrDefault("copyOffsetY", 0);
            copyOffsetZ = yapionObject.getPlainValueOrDefault("copyOffsetZ", 0);
            hasCopyPoint = yapionObject.containsKey("copyOffsetX") || yapionObject.containsKey("copyOffsetY") || yapionObject.containsKey("copyOffsetZ");
        }
    }

    public static Region generateRegion(String name, YAPIONObject regionConfig, YAPIONObject regionData) {
        Prototype prototype;
        if (regionData.containsKey("prototype", String.class)) {
            prototype = PROTOTYPE_MAP.get(regionData.getPlainValue("prototype"));
        } else {
            prototype = PROTOTYPE_MAP.get(regionConfig.getPlainValue("prototype"));
        }
        FlagStorage flagStorage;
        if (regionData.containsKey("flagStorage", YAPIONType.OBJECT)) {
            flagStorage = FlagStorage.createStorage(regionData.getObject("flagStorage"));
        } else {
            flagStorage = new FlagStorage();
        }
        return new Region(name, prototype, regionConfig, flagStorage, regionData);
    }
}
