/*
 * ******************************************************************************
 *  * Copyright 2015 See AUTHORS file.
 *  *
 *  * 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 games.rednblack.editor.utils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.util.ArrayList;

/**
 * Created by sargis on 8/29/14.
 */
public class NinePatchUtils {

    public static Integer[] findPatches(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int[] row = new int[width];
        int[] column = new int[height];
        row = getPixels(image, 0, 0, width, 1, row);
        column = getPixels(image, 0, 0, 1, height, column);

        boolean[] result = new boolean[1];
        //row = getPixels(image, 0, height - 1, width, 1, row);
        //column = getPixels(image, width - 1, 0, 1, height, column);

        Pair<java.util.List<Pair<Integer>>> top = getPatches(row, result);
        Pair<java.util.List<Pair<Integer>>> left = getPatches(column, result);

        Pair<Integer> topPadding = getPadding(top.first);
        Pair<Integer> leftPadding = getPadding(left.first);

        return new Integer[]{topPadding.first, topPadding.second, leftPadding.first, leftPadding.second};
    }

    public static BufferedImage removePatches(BufferedImage image) {
        BufferedImage buffer = createTranslucentCompatibleImage(
                image.getWidth() - 2, image.getHeight() - 2);

        Graphics2D g2 = buffer.createGraphics();
        g2.drawImage(image, -1, -1, null);
        g2.dispose();
        return buffer;
    }

    public static BufferedImage convertTo9Patch(BufferedImage image, Integer[] patches, float ratio) {
        BufferedImage buffer = createTranslucentCompatibleImage(image.getWidth() + 2, image.getHeight() + 2);
        Graphics2D g2 = buffer.createGraphics();
        g2.drawImage(image, 1, 1, null);
        g2.dispose();
        draw9Patch(buffer, patches, ratio);
        return buffer;
    }

    private static GraphicsConfiguration getGraphicsConfiguration() {
        GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
        return environment.getDefaultScreenDevice().getDefaultConfiguration();
    }

    private static BufferedImage createTranslucentCompatibleImage(int width, int height) {
        return getGraphicsConfiguration().createCompatibleImage(width, height,
                Transparency.TRANSLUCENT);
    }

    private static Pair<Integer> getPadding(java.util.List<Pair<Integer>> pairs) {
        if (pairs.size() == 0) {
            return new Pair<>(0, 0);
        } else if (pairs.size() == 1) {
            if (pairs.get(0).first == 1) {
                return new Pair<>(pairs.get(0).second - pairs.get(0).first, 0);
            } else {
                return new Pair<>(0, pairs.get(0).second - pairs.get(0).first);
            }
        } else {
            int index = pairs.size() - 1;
            return new Pair<>(pairs.get(0).second - pairs.get(0).first,
                    pairs.get(index).second - pairs.get(index).first);
        }
    }

    private static int[] getPixels(BufferedImage img, int x, int y, int w, int h, int[] pixels) {
        if (w == 0 || h == 0) {
            return new int[0];
        }

        if (pixels == null) {
            pixels = new int[w * h];
        } else if (pixels.length < w * h) {
            throw new IllegalArgumentException("Pixels array must have a length >= w * h");
        }

        int imageType = img.getType();
        if (imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) {
            Raster raster = img.getRaster();
            return (int[]) raster.getDataElements(x, y, w, h, pixels);
        }

        // Unmanages the image
        return img.getRGB(x, y, w, h, pixels, 0, w);
    }

    private static Pair<java.util.List<Pair<Integer>>> getPatches(int[] pixels, boolean[] startWithPatch) {
        int lastIndex = 1;
        int lastPixel = pixels[1];
        boolean first = true;

        java.util.List<Pair<Integer>> fixed = new ArrayList<>();
        java.util.List<Pair<Integer>> patches = new ArrayList<>();

        for (int i = 1; i < pixels.length - 1; i++) {
            int pixel = pixels[i];
            if (pixel != lastPixel) {
                if (lastPixel == 0xFF000000) {
                    if (first) startWithPatch[0] = true;
                    patches.add(new Pair<Integer>(lastIndex, i));
                } else {
                    fixed.add(new Pair<Integer>(lastIndex, i));
                }
                first = false;

                lastIndex = i;
                lastPixel = pixel;
            }
        }
        if (lastPixel == 0xFF000000) {
            if (first) startWithPatch[0] = true;
            patches.add(new Pair<>(lastIndex, pixels.length - 1));
        } else {
            fixed.add(new Pair<>(lastIndex, pixels.length - 1));
        }

        if (patches.size() == 0) {
            patches.add(new Pair<>(1, pixels.length - 1));
            startWithPatch[0] = true;
            fixed.clear();
        }

        return new Pair<>(fixed, patches);
    }

    private static void draw9Patch(BufferedImage image, Integer[] patches, float ratio) {
        int width = image.getWidth();
        int height = image.getHeight();
        int wStart = (int) (patches[0] * ratio) + 1; // this number should be rounded UP
        int wEnd = (int) (width - patches[1] * ratio) - 1;
        int hStart = (int) (patches[2] * ratio) + 1; // this number should be rounded UP
        int hEnd = (int) (height - patches[3] * ratio) - 1;
        for (int i = wStart; i < wEnd; i++) {
            image.setRGB(i, 0, 0xFF000000);
            image.setRGB(i, height - 1, 0xFF000000);
        }
        for (int i = hStart; i < hEnd; i++) {
            image.setRGB(0, i, 0xFF000000);
            image.setRGB(width - 1, i, 0xFF000000);
        }
    }

    static class Pair<E> {
        E first;
        E second;

        Pair(E first, E second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public String toString() {
            return "Pair[" + first + ", " + second + "]";
        }
    }
}
