/*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2023 DBeaver Corp and others
 *
 * 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 org.jkiss.dbeaver.ui;

import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.DPIUtil;
import org.eclipse.swt.widgets.Control;

/**
 * Image-related utils
 *
 * <p/>
 * Web image transformation originally get from org.eclipse.gmf.runtime.diagram.ui.render.util.CopyToImageUtil
 *
 * Web image transformation by Anthony Hunter, cmahoney
 */
public class ImageUtils {

    public static Image captureWidget(Control widget, Color borderColor, Image defaultImage)
    {
        Point size = widget.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        Image image = new Image(widget.getDisplay(), size.x, size.y);
        //image.
        GC gc = new GC(image);
        try {
            //gc.copyArea(image, 0, 0);
            widget.print(gc);
        } finally {
            UIUtils.dispose(gc);
        }
        Image result = removeImageBorder(image, borderColor);
        if (result == null) {
            return defaultImage;
        } else {
            return result;
        }
/*
        final ImageData imageData = image.getImageData();
        imageData.transparentPixel = imageData.getPixel(0, 0);
        Image fixedImage = new Image(widget.getDisplay(), imageData);
        image.dispose();
        return fixedImage;
*/
    }

    public static Image removeImageBorder(Image srcImage, Color borderColor)
    {
        final ImageData imageData = srcImage.getImageData();
        if (imageData.height == 0 || imageData.width == 0) {
            return srcImage;
        }
        int borderPixel = imageData.getPixel(0, 0);
        if (!imageData.palette.getRGB(borderPixel).equals(borderColor.getRGB())) {
            // First pixel isn't a border
            return srcImage;
        }

        int emptyTopRows = 0, emptyBottomRows = 0, emptyLeftColumns = 0, emptyRightColumns = 0;
        // Check top rows
        int row;
        for (row = 0; row < imageData.height; row++) {
            boolean emptyRow = true;
            for (int col = 0; col < imageData.width; col++) {
                if (borderPixel != imageData.getPixel(col, row)) {
                    emptyRow = false;
                    break;
                }
            }
            if (!emptyRow) {
                emptyTopRows = row;
                break;
            }
        }
        if (row == imageData.height) {
            // All rows are empty
            return null;
        }
        // Check bottom rows
        for (row = imageData.height - 1; row >= 0; row--) {
            boolean emptyRow = true;
            for (int col = 0; col < imageData.width; col++) {
                if (borderPixel != imageData.getPixel(col, row)) {
                    emptyRow = false;
                    break;
                }
            }
            if (!emptyRow) {
                emptyBottomRows = imageData.height - row - 1;
                break;
            }
        }
        if (emptyTopRows > 0 || emptyBottomRows > 0 || emptyLeftColumns > 0 || emptyRightColumns > 0) {
            return cropImage(
                srcImage,
                emptyLeftColumns,
                emptyTopRows,
                imageData.width - emptyLeftColumns - emptyRightColumns,
                imageData.height - emptyTopRows - emptyBottomRows);
        }
        return srcImage;
    }

	public static Image cropImage(Image srcImage, int x, int y, int w, int h)
	{
		Image cropImage = new Image(srcImage.getDevice(), w, h);

        // Redefine w and h to void them to be too big
		if (x+w > srcImage.getBounds().width) {
			w = srcImage.getBounds().width - x;
		}
		if (y+h > srcImage.getBounds().height) {
			h = srcImage.getBounds().height - y;
		}

		GC cropGC = new GC(cropImage);
		cropGC.drawImage(srcImage,
				x, y,
				w, h,
				0, 0,
				w, h);
		UIUtils.dispose(cropGC);
        UIUtils.dispose(srcImage);

		return cropImage;
	}

    /**
     * Retrieve the image data for the image, using a palette of at most 256
     * colours.
     *
     * @param image the SWT image.
     * @return new image data.
     */
    public static ImageData makeWebImageData(Image image)
    {

        ImageData imageData = image.getImageData();

        /**
         * If the image depth is 8 bits or less, then we can use the existing
         * image data.
         */
        if (imageData.depth <= 8) {
            return imageData;
        }

        /**
         * get an 8 bit imageData for the image
         */
        ImageData newImageData = get8BitPaletteImageData(imageData);

        /**
         * if newImageData is null, it has more than 256 colours. Use the web
         * safe palette to get an 8 bit image data for the image.
         */
        if (newImageData == null) {
            newImageData = getWebSafePaletteImageData(imageData);
        }

        return newImageData;
    }

    /**
     * Retrieve an image data with an 8 bit palette for an image. We assume that
     * the image has less than 256 colours.
     *
     * @param imageData the imageData for the image.
     * @return the new 8 bit imageData or null if the image has more than 256
     *         colours.
     */
    private static ImageData get8BitPaletteImageData(ImageData imageData)
    {
        PaletteData palette = imageData.palette;
        RGB colours[] = new RGB[256];
        PaletteData newPaletteData = new PaletteData(colours);
        ImageData newImageData = new ImageData(imageData.width, imageData.height, 8, newPaletteData);

        int lastPixel = -1;
        int newPixel = -1;
        for (int i = 0; i < imageData.width; ++i) {
            for (int j = 0; j < imageData.height; ++j) {
                int pixel = imageData.getPixel(i, j);

                if (pixel != lastPixel) {
                    lastPixel = pixel;

                    RGB colour = palette.getRGB(pixel);
                    for (newPixel = 0; newPixel < 256; ++newPixel) {
                        if (colours[newPixel] == null) {
                            colours[newPixel] = colour;
                            break;
                        }
                        if (colours[newPixel].equals(colour)) {
                            break;
                        }
                    }

                    if (newPixel >= 256) {
                        /**
                         * Diagram has more than 256 colors, return null
                         */
                        return null;
                    }
                }

                newImageData.setPixel(i, j, newPixel);
            }
        }

        RGB colour = new RGB(0, 0, 0);
        for (int k = 0; k < 256; ++k) {
            if (colours[k] == null) {
                colours[k] = colour;
            }
        }

        return newImageData;
    }

    /**
     * If the image has less than 256 colours, simply create a new 8 bit palette
     * and map the colours to the new palette.
     */
    private static ImageData getWebSafePaletteImageData(ImageData imageData)
    {
        PaletteData palette = imageData.palette;
        RGB[] webSafePalette = getWebSafePalette();
        PaletteData newPaletteData = new PaletteData(webSafePalette);
        ImageData newImageData = new ImageData(imageData.width,
            imageData.height, 8, newPaletteData);

        int lastPixel = -1;
        int newPixel = -1;
        for (int i = 0; i < imageData.width; ++i) {
            for (int j = 0; j < imageData.height; ++j) {
                int pixel = imageData.getPixel(i, j);

                if (pixel != lastPixel) {
                    lastPixel = pixel;

                    RGB colour = palette.getRGB(pixel);
                    RGB webSafeColour = getWebSafeColour(colour);
                    for (newPixel = 0; newPixel < 256; ++newPixel) {
                        if (webSafePalette[newPixel].equals(webSafeColour)) {
                            break;
                        }
                    }

                    Assert.isTrue(newPixel < 216);
                }
                newImageData.setPixel(i, j, newPixel);
            }
        }

        return newImageData;
    }

    /**
     * Retrieves a web safe colour that closely matches the provided colour.
     *
     * @param colour a colour.
     * @return the web safe colour.
     */
    private static RGB getWebSafeColour(RGB colour)
    {
        int red = Math.round((colour.red + 25) / 51) * 51;
        int green = Math.round((colour.green + 25) / 51) * 51;
        int blue = Math.round((colour.blue + 25) / 51) * 51;
        return new RGB(red, green, blue);
    }

    /**
     * Retrieves a web safe palette. Our palette will be 216 web safe colours
     * and the remaining filled with white.
     *
     * @return array of 256 colours.
     */
    private static RGB[] getWebSafePalette()
    {
        RGB[] colours = new RGB[256];
        int i = 0;
        for (int red = 0; red <= 255; red = red + 51) {
            for (int green = 0; green <= 255; green = green + 51) {
                for (int blue = 0; blue <= 255; blue = blue + 51) {
                    RGB colour = new RGB(red, green, blue);
                    colours[i++] = colour;
                }
            }
        }

        RGB colour = new RGB(0, 0, 0);
        for (int k = 0; k < 256; ++k) {
            if (colours[k] == null) {
                colours[k] = colour;
            }
        }

        return colours;
    }

    public static ImageData getImageDataAtCurrentZoom(Image image) {
        try {
            return image.getImageData(DPIUtil.getDeviceZoom());
        } catch (Exception e) {
            return image.getImageData();
        }
    }

}