package com.jme3.scene.plugins.blender.textures.blending;

import java.util.logging.Logger;

import jme3tools.converters.MipMapGenerator;

import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.texture.Image;

/**
 * An abstract class that contains the basic methods used by the classes that
 * will derive from it.
 * 
 * @author Marcin Roguski (Kaelthas)
 */
/* package */abstract class AbstractTextureBlender implements TextureBlender {
    private static final Logger LOGGER = Logger.getLogger(AbstractTextureBlender.class.getName());

    protected int               flag;
    protected boolean           negateTexture;
    protected int               blendType;
    protected float[]           materialColor;
    protected float[]           color;
    protected float             blendFactor;

    public AbstractTextureBlender(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) {
        this.flag = flag;
        this.negateTexture = negateTexture;
        this.blendType = blendType;
        this.materialColor = materialColor;
        this.color = color;
        this.blendFactor = blendFactor;
    }

    /**
     * The method that performs the ramp blending.
     * 
     * @param type
     *            the blend type
     * @param materialRGB
     *            the rgb value of the material, here the result is stored too
     * @param fac
     *            color affection factor
     * @param pixelColor
     *            the texture color
     * @param blenderContext
     *            the blender context
     */
    protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) {
        float oneMinusFactor = 1.0f - fac;
        MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);

        switch (type) {
            case MTEX_BLEND_HUE: {// FIXME: not working well for image textures (works fine for generated textures)
                float[] colorTransformResult = new float[3];
                materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
                if (colorTransformResult[0] != 0.0f) {
                    float colH = colorTransformResult[0];
                    materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
                    materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
                    materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0];
                    materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1];
                    materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2];
                }
                break;
            }
            case MTEX_BLEND_SAT: {
                float[] colorTransformResult = new float[3];
                materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
                float h = colorTransformResult[0];
                float s = colorTransformResult[1];
                float v = colorTransformResult[2];
                if (s != 0.0f) {
                    materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
                    materialHelper.hsvToRgb(h, oneMinusFactor * s + fac * colorTransformResult[1], v, materialRGB);
                }
                break;
            }
            case MTEX_BLEND_VAL: {
                float[] rgbToHsv = new float[3];
                float[] colToHsv = new float[3];
                materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
                materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
                materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2], materialRGB);
                break;
            }
            case MTEX_BLEND_COLOR: {// FIXME: not working well for image textures (works fine for generated textures)
                float[] rgbToHsv = new float[3];
                float[] colToHsv = new float[3];
                materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
                if (colToHsv[2] != 0) {
                    materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
                    materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
                    materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0];
                    materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1];
                    materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2];
                }
                break;
            }
            default:
                throw new IllegalStateException("Unknown ramp type: " + type);
        }
    }

    public void copyBlendingData(TextureBlender textureBlender) {
        if (textureBlender instanceof AbstractTextureBlender) {
            flag = ((AbstractTextureBlender) textureBlender).flag;
            negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
            blendType = ((AbstractTextureBlender) textureBlender).blendType;
            materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
            color = ((AbstractTextureBlender) textureBlender).color.clone();
            blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
        } else {
            LOGGER.warning("Cannot copy blending data from other types than " + this.getClass());
        }
    }

    /**
     * The method prepares images for blending. It generates mipmaps if one of
     * the images has them defined and the other one has not.
     * 
     * @param target
     *            the image where the blending result is stored
     * @param source
     *            the image that is being read only
     */
    protected void prepareImagesForBlending(Image target, Image source) {
        LOGGER.fine("Generating mipmaps if needed!");
        boolean targetHasMipmaps = target == null ? false : target.getMipMapSizes() != null && target.getMipMapSizes().length > 0;
        boolean sourceHasMipmaps = source == null ? false : source.getMipMapSizes() != null && source.getMipMapSizes().length > 0;
        if (target != null && !targetHasMipmaps && sourceHasMipmaps) {
            MipMapGenerator.generateMipMaps(target);
        } else if (source != null && !sourceHasMipmaps && targetHasMipmaps) {
            MipMapGenerator.generateMipMaps(source);
        }
    }
}
