/*
 * This file is a part of the SteamWar software.
 *
 * Copyright (C) 2020  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.dynamic.path;

import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.transform.AffineTransform;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionSystem;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import de.steamwar.bausystem.utils.PasteBuilder;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.World;

import javax.annotation.Nullable;
import java.io.File;
import java.util.function.Function;

public class PathAreaTile implements Region.Area { // TODO: Change to VariantSelector

    private static final File PATH_DIR = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/path");
    private static final File PATH_CENTER = new File(PATH_DIR, "PathCenter.schem");

    @RequiredArgsConstructor
    private enum Path {
        North(0, -18, 5, 0, 0),
        South(0, 19, 5, 0, 180),
        West(-18, 0, 5, 0, 90),
        East(19, 0, 5, 0, 270),
        ;

        private final int checkX;
        private final int checkZ;
        private final int pasteX;
        private final int pasteZ;
        private final int rotate;

        private static final Function<RegionType.ConnectionType, File> function = connectionType -> {
            return new File(PATH_DIR, "PathEdge" + connectionType.name() + ".schem");
        };
    }

    @RequiredArgsConstructor
    private enum PathCorner {
        NorthEast(Path.North, Path.East, 14, 0, 0),
        EastSouth(Path.East, Path.South, 14, 0, 270),
        SouthWest(Path.South, Path.West, 14, 0, 180),
        WestNorth(Path.West, Path.North, 14, 0, 90),
        ;

        private final Path check1;
        private final Path check2;
        private final int pasteX;
        private final int pasteZ;
        private final int rotate;

        private static final TriFunction<RegionType.ConnectionType, RegionType.ConnectionType, RegionType.ConnectionType, File> function = (connectionType1, connectionType2, connectionTypeCorner) -> {
            return new File(PATH_DIR, "PathCorner" + connectionType1.name() + connectionType2.name() + connectionTypeCorner.name() + ".schem");
        };

        private interface TriFunction<T, U, V, W> {
            W apply(T t, U u, V v);
        }
    }

    private final int minX;
    private final int minZ;
    private final Point minPoint;
    private final Point maxPoint;
    private final Point copyPoint;
    private final Region region;

    public PathAreaTile(int minX, int minZ, Region region) {
        this.minX = minX;
        this.minZ = minZ;

        minPoint = new Point(minX, 0, minZ);
        maxPoint = new Point(minX + 18, 255, minZ + 18);
        copyPoint = new Point(minX + 9, 0, minZ + 9);

        this.region = region;
    }

    @Override
    public @NonNull Point getMinPoint(boolean extension) {
        return minPoint;
    }

    @Override
    public @NonNull Point getMaxPoint(boolean extension) {
        return maxPoint;
    }

    @Override
    public @NonNull Point getCopyPoint() {
        return copyPoint;
    }

    @Nullable
    @Override
    public File getResetFile() {
        return null; // I know what I do!
    }

    @Override
    public void reset(PasteBuilder pasteBuilder, boolean extension) {
        Point minPoint = getMinPoint(false);

        paste(PATH_CENTER, minPoint.add(5, 0, 5), 0);

        for (Path path : Path.values()) {
            RegionType.ConnectionType connectionType = RegionSystem.INSTANCE.get(minPoint.add(path.checkX, 0, path.checkZ).toLocation((World) null)).getType().getConnectionType();
            File schem = path.function.apply(connectionType);
            if (schem.exists()) {
                paste(schem, minPoint.add(path.pasteX, 0, path.pasteZ), path.rotate);
            }
        }

        for (PathCorner corner : PathCorner.values()) {
            RegionType.ConnectionType connectionType1 = RegionSystem.INSTANCE.get(minPoint.add(corner.check1.checkX, 0, corner.check1.checkZ).toLocation((World) null)).getType().getConnectionType();
            RegionType.ConnectionType connectionType2 = RegionSystem.INSTANCE.get(minPoint.add(corner.check2.checkX, 0, corner.check2.checkZ).toLocation((World) null)).getType().getConnectionType();
            RegionType.ConnectionType connectionTypeCorner = RegionSystem.INSTANCE.get(minPoint.add(corner.check1.checkX + corner.check2.checkX, 0, corner.check1.checkZ + corner.check2.checkZ).toLocation((World) null)).getType().getConnectionType();
            File schem = corner.function.apply(connectionType1, connectionType2, connectionTypeCorner);
            if (schem.exists()) {
                System.out.println(connectionType1 + " " + connectionType2 + " " + connectionTypeCorner + " " + schem);
                paste(schem, minPoint.add(corner.pasteX, 0, corner.pasteZ), corner.rotate);
            } else {
                System.out.println(connectionType1 + " " + connectionType2 + " " + connectionTypeCorner + " " + PATH_DIR.getAbsolutePath() + "/PathCornerUnknown.schem");
                paste(new File(PATH_DIR, "PathCornerUnknown.schem"), minPoint.add(corner.pasteX, 0, corner.pasteZ), corner.rotate);
            }
        }
    }

    private void paste(File file, Point minPoint, int rotate) {
        Clipboard clipboard = FlatteningWrapper.impl.loadSchematic(file);
        BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
        BlockVector3 to = minPoint.toBlockVector3().subtract(offset);
        clipboard.paste(BukkitAdapter.adapt(Bukkit.getWorlds().get(0)), to, false, true, new AffineTransform().rotateY(rotate));
    }
}
