package com.tiedup.remake.client.texture;

import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.tiedup.remake.core.TiedUpMod;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;

/**
 * Handles a dynamically loaded online texture for clothes.
 * Supports standard skins (64x32/64x64) and scales to proper format.
 *
 * Creates multiple texture variants:
 * - Base texture: Original image as uploaded
 * - Clothes texture: Resized for 64x64 format if needed (old 64x32 format)
 * - Full-skin texture: For full-skin mode (covers entire player)
 *
 * Thread-safe: Texture uploads are scheduled on render thread.
 */
@OnlyIn(Dist.CLIENT)
public class DynamicOnlineTexture {

    private static final int MAX_DIMENSION = 1280;

    // Texture objects managed by Minecraft's texture manager
    private DynamicTexture baseTexture;
    private DynamicTexture clothesTexture;
    private DynamicTexture fullSkinTexture;

    private boolean needsResize = false;
    private boolean valid = false;

    private final String sourceUrl;

    /**
     * Create a new dynamic texture from a downloaded image.
     *
     * @param image The downloaded image
     * @param sourceUrl The source URL (for logging)
     */
    public DynamicOnlineTexture(NativeImage image, String sourceUrl) {
        this.sourceUrl = sourceUrl;
        if (image != null) {
            this.valid = processImage(image);
        }
    }

    /**
     * Process the downloaded image and create texture variants.
     *
     * @param image The source image
     * @return true if processing succeeded
     */
    private boolean processImage(NativeImage image) {
        int width = image.getWidth();
        int height = image.getHeight();

        // Validate dimensions
        if (width == 0 || height == 0) {
            TiedUpMod.LOGGER.warn(
                "[DynamicOnlineTexture] Invalid image dimensions: {}x{} from {}",
                width,
                height,
                sourceUrl
            );
            return false;
        }

        // Check for valid skin format (must be divisible by 64 width, 32 height)
        if (height % 32 != 0 || width % 64 != 0) {
            TiedUpMod.LOGGER.warn(
                "[DynamicOnlineTexture] Invalid skin dimensions: {}x{} (must be 64x32 or 64x64 multiples) from {}",
                width,
                height,
                sourceUrl
            );
            return false;
        }

        // Check maximum size
        if (width > MAX_DIMENSION || height > MAX_DIMENSION) {
            TiedUpMod.LOGGER.warn(
                "[DynamicOnlineTexture] Image too large: {}x{} (max {}) from {}",
                width,
                height,
                MAX_DIMENSION,
                sourceUrl
            );
            return false;
        }

        try {
            // Create base texture (original image)
            NativeImage baseImage = copyImage(image);

            // Create clothes texture (resized if needed for old 64x32 format)
            NativeImage clothesImage;
            if (height * 2 == width) {
                // New format (64x64), use as-is
                clothesImage = copyImage(image);
            } else {
                // Old format (64x32), needs conversion to 64x64
                needsResize = true;
                clothesImage = convertOldFormat(image);
            }

            // Create full-skin texture (square format for full coverage)
            NativeImage fullSkinImage = createFullSkinImage(
                clothesImage,
                width
            );

            // Schedule texture upload on render thread
            RenderSystem.recordRenderCall(() -> {
                this.baseTexture = new DynamicTexture(baseImage);
                this.clothesTexture = needsResize
                    ? new DynamicTexture(clothesImage)
                    : this.baseTexture;
                this.fullSkinTexture = new DynamicTexture(fullSkinImage);
            });

            return true;
        } catch (Exception e) {
            TiedUpMod.LOGGER.error(
                "[DynamicOnlineTexture] Failed to process image from {}: {}",
                sourceUrl,
                e.getMessage()
            );
            return false;
        }
    }

    /**
     * Create a copy of a NativeImage.
     */
    private NativeImage copyImage(NativeImage source) {
        NativeImage copy = new NativeImage(
            source.format(),
            source.getWidth(),
            source.getHeight(),
            false
        );
        copy.copyFrom(source);
        return copy;
    }

    /**
     * Convert old 64x32 format to modern 64x64 format.
     * This copies the arm and leg textures to the correct locations.
     */
    private NativeImage convertOldFormat(NativeImage source) {
        int width = source.getWidth();
        int height = source.getHeight();
        int scale = width / 64;

        // Create new 64x64 (scaled) image
        NativeImage result = new NativeImage(
            source.format(),
            width,
            width,
            false
        );

        // Copy top half (head, body, arms - same as before)
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                result.setPixelRGBA(x, y, source.getPixelRGBA(x, y));
            }
        }

        // Copy left arm from right arm location (mirrored)
        // Old format has arms in row 2, col 10 (44-47, 16-32)
        // New format left arm goes to row 3-4, col 8 (32-48, 48-64)
        copyRegion(
            source,
            result,
            44 * scale,
            16 * scale,
            32 * scale,
            48 * scale,
            4 * scale,
            16 * scale
        );

        // Copy left leg from right leg location (mirrored)
        // Old format has leg in row 0-1 (0-16, 16-32)
        // New format left leg goes to row 3-4, col 4 (16-32, 48-64)
        copyRegion(
            source,
            result,
            0 * scale,
            16 * scale,
            16 * scale,
            48 * scale,
            16 * scale,
            16 * scale
        );

        return result;
    }

    /**
     * Copy a region from source to destination image.
     */
    private void copyRegion(
        NativeImage src,
        NativeImage dst,
        int srcX,
        int srcY,
        int dstX,
        int dstY,
        int width,
        int height
    ) {
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if (
                    srcX + x < src.getWidth() &&
                    srcY + y < src.getHeight() &&
                    dstX + x < dst.getWidth() &&
                    dstY + y < dst.getHeight()
                ) {
                    dst.setPixelRGBA(
                        dstX + x,
                        dstY + y,
                        src.getPixelRGBA(srcX + x, srcY + y)
                    );
                }
            }
        }
    }

    /**
     * Create full-skin image (square format, opaque in key areas).
     */
    private NativeImage createFullSkinImage(NativeImage source, int width) {
        NativeImage fullSkin = new NativeImage(
            source.format(),
            width,
            width,
            false
        );
        fullSkin.copyFrom(source);

        // Make key areas fully opaque for full-skin mode
        int quarter = width / 4;
        setAreaOpaque(fullSkin, 0, 0, quarter, quarter);
        setAreaOpaque(fullSkin, 0, quarter, width, quarter * 2);
        setAreaOpaque(fullSkin, quarter, quarter * 3, quarter * 3, width);

        return fullSkin;
    }

    /**
     * Make an area of the image fully opaque.
     */
    private void setAreaOpaque(
        NativeImage image,
        int x1,
        int y1,
        int x2,
        int y2
    ) {
        for (int x = x1; x < x2 && x < image.getWidth(); x++) {
            for (int y = y1; y < y2 && y < image.getHeight(); y++) {
                int pixel = image.getPixelRGBA(x, y);
                // Set alpha to 255 (fully opaque)
                pixel |= 0xFF000000;
                image.setPixelRGBA(x, y, pixel);
            }
        }
    }

    /**
     * Get the base texture ID (original image).
     */
    public int getBaseTextureId() {
        return baseTexture != null ? baseTexture.getId() : -1;
    }

    /**
     * Get the clothes texture ID (resized for modern format).
     */
    public int getClothesTextureId() {
        return clothesTexture != null
            ? clothesTexture.getId()
            : getBaseTextureId();
    }

    /**
     * Get the full-skin texture ID.
     */
    public int getFullSkinTextureId() {
        return fullSkinTexture != null ? fullSkinTexture.getId() : -1;
    }

    /**
     * Bind the clothes texture for rendering.
     */
    public void bindClothes() {
        int id = getClothesTextureId();
        if (id != -1) {
            RenderSystem.setShaderTexture(0, id);
        }
    }

    /**
     * Bind the full-skin texture for rendering.
     */
    public void bindFullSkin() {
        int id = getFullSkinTextureId();
        if (id != -1) {
            RenderSystem.setShaderTexture(0, id);
        }
    }

    /**
     * Bind the base texture for rendering.
     */
    public void bindBase() {
        int id = getBaseTextureId();
        if (id != -1) {
            RenderSystem.setShaderTexture(0, id);
        }
    }

    /**
     * Check if the texture was successfully loaded.
     */
    public boolean isValid() {
        return valid;
    }

    /**
     * Check if the image needed resizing (old 64x32 format).
     */
    public boolean wasResized() {
        return needsResize;
    }

    /**
     * Get the source URL for debugging.
     */
    public String getSourceUrl() {
        return sourceUrl;
    }

    /**
     * Release texture resources.
     */
    public void close() {
        if (baseTexture != null) {
            baseTexture.close();
            baseTexture = null;
        }
        if (clothesTexture != null && clothesTexture != baseTexture) {
            clothesTexture.close();
            clothesTexture = null;
        }
        if (fullSkinTexture != null) {
            fullSkinTexture.close();
            fullSkinTexture = null;
        }
        valid = false;
    }

    /**
     * Get the DynamicTexture for clothes (for ResourceLocation registration).
     */
    @Nullable
    public DynamicTexture getClothesTexture() {
        return clothesTexture;
    }

    /**
     * Get the DynamicTexture for full-skin mode.
     */
    @Nullable
    public DynamicTexture getFullSkinTexture() {
        return fullSkinTexture;
    }
}
