package sys.vp8;

import sys.vp8.lib.*;
import sys.vp8.lib.Vp8Library.*;
import org.bridj.*;


public class Converter {
    // Converts RGB24 data (packed as ARGB ints) to a VPX YUV I420 image

    public static void ConvertRGB24ToI420(int[] rgbData, Pointer<vpx_image_t> img) {
        int width = img.get().d_w();
        int height = img.get().d_h();

        Pointer<Byte> imgY = img.get().planes().get(Vp8Library.VPX_PLANE_Y);
        Pointer<Byte> imgU = img.get().planes().get(Vp8Library.VPX_PLANE_U);
        Pointer<Byte> imgV = img.get().planes().get(Vp8Library.VPX_PLANE_V);

        int i1 = 0;
        int uvi = 0;
        for (int ypos = 0; ypos < height; ypos += 2) {
            for (int xpos = 0; xpos < width; xpos += 2) {
                int i2 = i1 + 1;
                int i3 = width + i1;
                int i4 = width + i2;

                int rgb1 = rgbData[i1];
                int rgb2 = rgbData[i2];
                int rgb3 = rgbData[i3];
                int rgb4 = rgbData[i4];

                int r1 = unpackR(rgb1);
                int g1 = unpackG(rgb1);
                int b1 = unpackB(rgb1);
                int r2 = unpackR(rgb2);
                int g2 = unpackG(rgb2);
                int b2 = unpackB(rgb2);
                int r3 = unpackR(rgb3);
                int g3 = unpackG(rgb3);
                int b3 = unpackB(rgb3);
                int r4 = unpackR(rgb4);
                int g4 = unpackG(rgb4);
                int b4 = unpackB(rgb4);

                imgY.set(i1, (byte) getY(r1, g1, b1));
                imgY.set(i2, (byte) getY(r2, g2, b2));
                imgY.set(i3, (byte) getY(r3, g3, b3));
                imgY.set(i4, (byte) getY(r4, g4, b4));

                int u1 = getU(r1, g1, b1);
                int u2 = getU(r2, g2, b2);
                int u3 = getU(r3, g3, b3);
                int u4 = getU(r4, g4, b4);

                int v1 = getV(r1, g1, b1);
                int v2 = getV(r2, g2, b2);
                int v3 = getV(r3, g3, b3);
                int v4 = getV(r4, g4, b4);

                imgU.set(uvi, (byte) ((u1 + u2 + u3 + u4) / 4));
                imgV.set(uvi, (byte) ((v1 + v2 + v3 + v4) / 4));

                i1 += 2;
                uvi++;
            }

            i1 += width;
        }
    }

    // Converts a VPX YUV I420 image to RGB24 data (packed as ARGB ints) 
    public static void ConvertI420ToRGB24(Pointer<vpx_image_t> img, int[] rgbData) {
        int width = img.get().d_w();
        int height = img.get().d_h();

        Pointer<Byte> imgY = img.get().planes().get(Vp8Library.VPX_PLANE_Y);
        Pointer<Byte> imgU = img.get().planes().get(Vp8Library.VPX_PLANE_U);
        Pointer<Byte> imgV = img.get().planes().get(Vp8Library.VPX_PLANE_V);

        int yPadding = img.get().stride().get(Vp8Library.VPX_PLANE_Y) - width;
        int uPadding = img.get().stride().get(Vp8Library.VPX_PLANE_U) - (width / 2);
        int vPadding = img.get().stride().get(Vp8Library.VPX_PLANE_V) - (width / 2);

        int yi = 0;
        int ui = 0;
        int vi = 0;
        int rgbi = 0;
        for (int ypos = 0; ypos < height; ypos++) {
            for (int xpos = 0; xpos < width; xpos++) {
                int y = imgY.get(yi) & 0xFF;
                int u = imgU.get(ui) & 0xFF;
                int v = imgV.get(vi) & 0xFF;

                int r = getR(y, u, v);
                int g = getG(y, u, v);
                int b = getB(y, u, v);

                rgbData[rgbi++] = packRGB(r, g, b);

                yi++;
                if (xpos % 2 == 1) {
                    ui++;
                    vi++;
                }
            }

            yi += yPadding;
            if (ypos % 2 == 0) {
                ui -= (width / 2);
                vi -= (width / 2);
            } else {
                ui += uPadding;
                vi += vPadding;
            }
        }
    }

    private static int unpackR(int rgb) {
        return (rgb >> 16) & 0xFF;
    }

    private static int unpackG(int rgb) {
        return (rgb >> 8) & 0xFF;
    }

    private static int unpackB(int rgb) {
        return (rgb >> 0) & 0xFF;
    }

    private static int packRGB(int r, int g, int b) {
        return 0xFF000000
                | (r & 0xFF) << 16
                | (g & 0xFF) << 8
                | (b & 0xFF) << 0;
    }

    private static int getR(int y, int u, int v) {
        return clamp(y + (int) (1.402f * (v - 128)));
    }

    private static int getG(int y, int u, int v) {
        return clamp(y - (int) (0.344f * (u - 128) + 0.714f * (v - 128)));
    }

    private static int getB(int y, int u, int v) {
        return clamp(y + (int) (1.772f * (u - 128)));
    }

    private static int getY(int r, int g, int b) {
        return (int) (0.299f * r + 0.587f * g + 0.114f * b);
    }

    private static int getU(int r, int g, int b) {
        return (int) (-0.169f * r - 0.331f * g + 0.499f * b + 128);
    }

    private static int getV(int r, int g, int b) {
        return (int) (0.499f * r - 0.418f * g - 0.0813f * b + 128);
    }

    private static int clamp(int value) {
        return Math.min(Math.max(value, 0), 255);
    }
}