package ac.ooechs.oil.util;

import ac.essex.ooechs.imaging.commons.Pixel;
import ac.essex.ooechs.imaging.commons.edge.hough.HoughLine;

import java.awt.image.BufferedImage;
import java.awt.*;
import java.util.Vector;

/**
 * Represents a linear line as detected by the hough transform.
 * This line is represented by an angle theta and a radius from the centre.
 *
 * @author Olly Oechsle, University of Essex, Date: 13-Mar-2008
 * @version 1.0
 */
public class HoughLine3 {

    protected double theta;
    protected double r;
    boolean valid = true;

    public HoughLine3(HoughLine line) {
        this.theta = line.getTheta();
        this.r = line.getR();
    }

    /**
     * Initialises the hough line
     */
    public HoughLine3(double theta, double r) {
        this.theta = theta;
        this.r = r;
    }


    /**
     * Draws the line on the image of your choice with the RGB colour of your choice.
     */
    public void draw(BufferedImage image, BufferedImage newImage, int color) {

        int height = image.getHeight();
        int width = image.getWidth();

        // During processing h_h is doubled so that -ve r values
        int houghHeight = (int) (Math.sqrt(2) * Math.max(height, width)) / 2;

        // Find edge points and vote in array
        float centerX = width / 2;
        float centerY = height / 2;

        // Draw edges in output array
        double tsin = Math.sin(theta);
        double tcos = Math.cos(theta);

        final int GREEN = Color.GREEN.getRGB();
        final int RED = Color.RED.getRGB();

        Vector<Pixel> line = new Vector<Pixel>(200);

        int total = 0;

        if (theta < Math.PI * 0.25 || theta > Math.PI * 0.75) {
            // Draw vertical-ish lines
            for (int y = 1; y < height - 1; y++) {
                int x = (int) ((((r - houghHeight) - ((y - centerY) * tsin)) / tcos) + centerX);
                if (x < width - 1 && x > 0) {
                    boolean edgeNearby = edgeNearTo(image, x, y);
                    if (edgeNearby) total++;
                    line.add(new Pixel(x, y, edgeNearby ? 1 : 0));
                }
            }
        } else {
            // Draw horizontal-sh lines
            for (int x = 1; x < width - 1; x++) {
                int y = (int) ((((r - houghHeight) - ((x - centerX) * tcos)) / tsin) + centerY);
                if (y < height - 1 && y > 0) {
                    boolean edgeNearby = edgeNearTo(image, x, y);
                    if (edgeNearby) total++;
                    line.add(new Pixel(x, y, edgeNearby ? 1 : 0));
                }
            }
        }


        int startIndex;

        for (startIndex = 0; startIndex < line.size(); startIndex++) {
            Pixel p = line.elementAt(startIndex);
            if (p.value == 1) {
                start = p;
                break;
            }
        }

        int endIndex;

        for (endIndex = line.size() - 1; endIndex > 0; endIndex--) {
            Pixel p = line.elementAt(endIndex);
            if (p.value == 1) {
                end = p;
                break;
            }
        }

        double length = endIndex - startIndex;

        double density = total / length;

        if (density > 0.66) {

            for (int i = startIndex; i < endIndex; i++) {
                Pixel p = line.elementAt(i);
                color = p.value == 1 ? GREEN : RED;
                newImage.setRGB(p.x, p.y, color);
            }

        } else {
            valid = false;
        }


    }

    protected Pixel start, end;

    public Pixel getStart() {
        return start;
    }

    public Pixel getEnd() {
        return end;
    }


    public boolean isValid() {
        return valid;
    }

    public boolean edgeNearTo(BufferedImage image, int x, int y) {
        int neighbourhoodSize = 2;
        try {

        for (int dx = -neighbourhoodSize; dx <= neighbourhoodSize; dx++) {
            for (int dy = -neighbourhoodSize; dy <= neighbourhoodSize; dy++) {
                int X = x + dx;
                int Y = y + dy;
                if ((image.getRGB(X, Y) & 0x000000ff) != 0) {
                    return true;
                }
            }
        }
        } catch (ArrayIndexOutOfBoundsException e)  {}
        return false;
    }

}

