/*
 * Copyright 2009 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.codename1.ext.codescan;

import com.google.zxing.LuminanceSource;
import com.codename1.ui.Image;

/**
 * A LuminanceSource based on Java ME's LWUIT Library Image class. It does not
 * support cropping or rotation.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 * @author Sean Owen
 */
public final class CN1ImageLuminanceSource extends LuminanceSource {

    private final Image image;
    private int[] rgbData;

    public CN1ImageLuminanceSource(Image image) {
        super(image.getWidth(), image.getHeight());
        this.image = image;
    }

    // Instead of multiplying by 306, 601, 117, we multiply by 256, 512, 256, so that
    // the multiplies can be implemented as shifts.
    //
    // Really, it's:
    //
    // return ((((pixel >> 16) & 0xFF) << 8) +
    //         (((pixel >>  8) & 0xFF) << 9) +
    //         (( pixel        & 0xFF) << 8)) >> 10;
    //
    // That is, we're replacing the coefficients in the original with powers of two,
    // which can be implemented as shifts, even though changing the coefficients slightly
    // alters the conversion. The difference is not significant for our purposes.
    public byte[] getRow(int y, byte[] row) {
        if (y < 0 || y >= getHeight()) {
            throw new IllegalArgumentException("Requested row is outside the image: " + y);
        }
        int width = getWidth();
        if (row == null || row.length < width) {
            row = new byte[width];
        }

        /*
         * if (rgbData == null || rgbData.length < width) { rgbData = new
         * int[width]; }
         */
        //image.getRGB(rgbData, 0, width, 0, y, width, 1);
        // For LWUIT Image get the row as sub image without alpha processing,
        // as we don't need alpha channel anyway
        Image rowImg = image.subImage(0, y, width, 1, false);
        rgbData = rowImg.getRGB();
        for (int x = 0; x < width; x++) {
            row[x] = toLuminance(rgbData[x]);
        }
        return row;
    }

    public byte[] getMatrix() {
        int width = getWidth();
        int height = getHeight();
        int area = width * height;
        byte[] matrix = new byte[area];

        //int[] rgb = new int[area];
        //image.getRGB(rgb, 0, width, 0, 0, width, height);
        int[] rgb = image.getRGBCached();
        for (int y = 0; y < height; y++) {
            int offset = y * width;
            for (int x = 0; x < width; x++) {
                matrix[offset + x] = toLuminance(rgb[offset + x]);
            }
        }
        return matrix;
    }

    public boolean isRotateSupported() {
        return true;
    }

    public LuminanceSource rotateCounterClockwise() {
        return new CCRotatedLWUITImageLuminanceSource(image);
    }

    static byte toLuminance(int pixel) {
        return (byte) ((((pixel & 0x00FF0000) >> 16)
                + ((pixel & 0x0000FF00) >> 7)
                + (pixel & 0x000000FF)) >> 2);
    }

    /**
     * A variant on {@link LWUITImageLuminanceSource} that acts as if the input
     * is rotated 90 degrees counter-clockwise.
     */
    private static final class CCRotatedLWUITImageLuminanceSource extends LuminanceSource {

        private final Image image;
        private int[] rgbData;

        private CCRotatedLWUITImageLuminanceSource(Image image) {
            super(image.getHeight(), image.getWidth());
            this.image = image;
        }

        public byte[] getRow(int y, byte[] row) {
            int height = getHeight();
            if (y < 0 || y >= height) {
                throw new IllegalArgumentException("Requested row is outside the image: " + y);
            }
            int width = getWidth();
            if (row == null || row.length < width) {
                row = new byte[width];
            }

            /*
             * if (rgbData == null || rgbData.length < width) { rgbData = new
             * int[width]; }
             */
            //image.getRGB(rgbData, 0, height, height - 1 - y, 0, 1, width);
            // For LWUIT Image get the row as sub image without alpha processing,
            // as we don't need alpha channel anyway
            Image rowImg = image.subImage(height - 1 - y, 0, 1, width, false);
            rgbData = rowImg.getRGB();
            for (int x = 0; x < width; x++) {
                row[x] = toLuminance(rgbData[x]);
            }
            return row;
        }

        public byte[] getMatrix() {
            int width = getWidth();
            int height = getHeight();
            int area = width * height;
            byte[] matrix = new byte[area];

            //int[] rgb = new int[area];
            //image.getRGB(rgb, 0, width, 0, 0, width, height);
            int[] rgb = image.getRGBCached();
            // This flips x/y in the target to result in a rotated image
            int offset = height * (width - 1);
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    matrix[offset - height * x + y] = toLuminance(rgb[y * width + x]);
                }
            }
            return matrix;
        }
    }
}
