package KondoKit

import rt4.GlIndexedSprite
import rt4.GlSprite
import rt4.SoftwareIndexedSprite
import rt4.SoftwareSprite
import java.awt.Color
import java.awt.image.BufferedImage

// Define interfaces for common sprite types
interface BaseSprite {
    val width: Int
    val height: Int
}

interface IndexedSprite : BaseSprite {
    val pixels: ByteArray
    val palette: IntArray
}

interface NonIndexedSprite : BaseSprite {
    val pixels: IntArray?
}

// Adapter functions for existing sprite types
fun adaptSoftwareSprite(sprite: SoftwareSprite): NonIndexedSprite {
    return object : NonIndexedSprite {
        override val width: Int = sprite.width
        override val height: Int = sprite.height
        override val pixels: IntArray? = sprite.pixels
    }
}

fun adaptSoftwareIndexedSprite(sprite: SoftwareIndexedSprite): IndexedSprite {
    return object : IndexedSprite {
        override val width: Int = sprite.width
        override val height: Int = sprite.height
        override val pixels: ByteArray = sprite.pixels
        override val palette: IntArray = sprite.pallet
    }
}

fun adaptGlSprite(sprite: GlSprite): NonIndexedSprite {
    return object : NonIndexedSprite {
        override val width: Int = sprite.width
        override val height: Int = sprite.height
        override val pixels: IntArray? = sprite.pixels
    }
}

fun adaptGlIndexedSprite(sprite: GlIndexedSprite): IndexedSprite {
    return object : IndexedSprite {
        override val width: Int = sprite.width
        override val height: Int = sprite.height
        override val pixels: ByteArray = sprite.pixels
        override val palette: IntArray = sprite.pallet
    }
}

object SpriteToBufferedImage {
    /**
     * Converts a BaseSprite into a BufferedImage.
     *
     * Handles both indexed and non-indexed sprites, with optional tinting and grayscale.
     *
     * @param sprite The sprite to be converted.
     * @param tint An optional Color to tint the image.
     * @param grayscale If true, converts the image to grayscale.
     * @param brightnessBoost A multiplier to boost the brightness of the image.
     * @return The BufferedImage created from the sprite.
     */
    private fun convertToBufferedImage(
        sprite: BaseSprite,
        tint: Color? = null,
        grayscale: Boolean = false,
        brightnessBoost: Float = 1.0f
    ): BufferedImage {
        val width = sprite.width
        val height = sprite.height
        val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)

        when (sprite) {
            is IndexedSprite -> {
                val pixels = sprite.pixels
                val palette = sprite.palette

                // Manually set pixels using the palette
                for (y in 0 until height) {
                    for (x in 0 until width) {
                        val index = pixels[y * width + x].toInt() and 0xFF
                        val color = palette[index]

                        // Apply grayscale or tint if provided
                        val finalColor = if (grayscale) {
                            applyGrayscale(Color(color, true), brightnessBoost)
                        } else if (tint != null) {
                            applyTint(Color(color, true), tint, brightnessBoost)
                        } else {
                            applyBrightness(Color(color, true), brightnessBoost)
                        }

                        image.setRGB(x, y, finalColor.rgb)
                    }
                }
            }
            is NonIndexedSprite -> {
                val pixels = sprite.pixels ?: return image // Handle null case for GlSprite

                // Manually set pixels directly
                for (y in 0 until height) {
                    for (x in 0 until width) {
                        val color = pixels[y * width + x]

                        // Apply grayscale or tint if provided
                        val finalColor = if (grayscale) {
                            applyGrayscale(Color(color, true), brightnessBoost)
                        } else if (tint != null) {
                            applyTint(Color(color, true), tint, brightnessBoost)
                        } else {
                            applyBrightness(Color(color, true), brightnessBoost)
                        }

                        image.setRGB(x, y, finalColor.rgb)
                    }
                }
            }
        }

        return image
    }

    /**
     * Applies a tint to a given color using the tint's alpha value to control the intensity.
     *
     * @param original The original color.
     * @param tint The tint color to be applied.
     * @param brightnessBoost A multiplier to boost the brightness of the image.
     * @return The tinted color.
     */
    private fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color {
        val boostedColor = applyBrightness(original, brightnessBoost)
        val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255)
        val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255)
        val b = (boostedColor.blue * tint.blue / 255).coerceIn(0, 255)
        return Color(r, g, b, boostedColor.alpha)
    }

    /**
     * Boosts the brightness of a given color.
     *
     * @param original The original color.
     * @param factor The multiplier to boost the brightness.
     * @return The color with boosted brightness.
     */
    private fun applyBrightness(original: Color, factor: Float): Color {
        val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt()
        val g = (original.green * factor).coerceIn(0.0f, 255.0f).toInt()
        val b = (original.blue * factor).coerceIn(0.0f, 255.0f).toInt()
        return Color(r, g, b, original.alpha)
    }

    /**
     * Converts a color to grayscale and applies a brightness boost.
     *
     * @param original The original color.
     * @param brightnessBoost A multiplier to boost the brightness.
     * @return The grayscale version of the color with boosted brightness.
     */
    private fun applyGrayscale(original: Color, brightnessBoost: Float): Color {
        // Calculate the grayscale value using the luminosity method
        val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt()
        val boostedGray = (grayValue * brightnessBoost).coerceIn(0.0f, 255.0f).toInt()
        return Color(boostedGray, boostedGray, boostedGray, original.alpha)
    }

    /**
     * Converts an unknown sprite object into a BufferedImage if it matches a known sprite type.
     *
     * @param sprite The sprite object to be converted.
     * @param tint An optional Color to tint the image.
     * @param grayscale If true, converts the image to grayscale.
     * @param brightnessBoost A multiplier to boost the brightness of the image.
     * @return The BufferedImage created from the sprite or a default image if unsupported.
     */
    fun getBufferedImageFromSprite(
        sprite: Any?,
        tint: Color? = null,
        grayscale: Boolean = false,
        brightnessBoost: Float = 1.0f
    ): BufferedImage {
        return when (sprite) {
            is SoftwareSprite -> convertToBufferedImage(adaptSoftwareSprite(sprite), tint, grayscale, brightnessBoost)
            is SoftwareIndexedSprite -> convertToBufferedImage(adaptSoftwareIndexedSprite(sprite), tint, grayscale, brightnessBoost)
            is GlSprite -> convertToBufferedImage(adaptGlSprite(sprite), tint, grayscale, brightnessBoost)
            is GlIndexedSprite -> convertToBufferedImage(adaptGlIndexedSprite(sprite), tint, grayscale, brightnessBoost)
            else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) // Default empty image for unsupported types
        }
    }
}