import java.awt.Color;

import objectdraw.DrawingCanvas;
import objectdraw.FilledRect;
import objectdraw.Location;
import objectdraw.RandomIntGenerator;
import objectdraw.Text;

// A visual representation of an array of integers.
// Each element in the array is represented by a rectangle.
// The height of the rectangle represents the value of an
// element.
//
// The elements can have their color changed to represent
// them being visited in a search algorithm.
//
// The location of an element can change to represent its
// movement during a sorting algorithm.
public class Array {
        
        // The color of the rectangle before applying an algorithm.
        private static Color BASIC_COLOR = Color.black;
        
        // The color after being visited in a search algorithm 
        // or a sort algorithm
        private static Color VISITED_COLOR = Color.red;
        
        // Color used to mark a rectangle whose value matches
        // one we are looking for.
        private static Color FOUND_COLOR = Color.blue;

        // The array of rectangles providing the animation
        private FilledRect[] array;
        
        // The labels on the rectangles
        private Text[] labels;
        
        // Size of font used to label rectangles
        private int fontSize = 24;

        // Constructs a new array.  Draws rectangles in the canvas
        // representing the array elements.  Uses the entire canvas.
        // Parameters:
        //    numElems - the size of the array
        //    missing - ???
        //          canvas - where to draw the rectangles. 
        public Array(int numElems, DrawingCanvas canvas) {
                this (numElems, canvas, false);
        }

        // Constructs a new array.  Draws rectangles in the canvas
        // representing the array elements.  Uses the entire canvas.
        // Parameters:
        //    numElems - the size of the array
        //          canvas - where to draw the rectangles. 
        public Array(int numElems, DrawingCanvas canvas, boolean sorted) {
                if (numElems > canvas.getWidth()) {
                        numElems = canvas.getWidth();
                }
                int elemWidth = canvas.getWidth() / numElems;
                array = new FilledRect[numElems];
                labels = new Text[numElems];

                fillArray(numElems, canvas, elemWidth, sorted);
        }

        // Fill the array with random integers and create the rectangles.
        private void fillArray(int numElems, DrawingCanvas canvas, int elemWidth, boolean sorted) {
                int min = 24;
                int max = canvas.getHeight();
                int gap;
                if (elemWidth < 2) {
                        gap = 0;
                }
                else {
                        gap = 1;
                }
                RandomIntGenerator gen = new RandomIntGenerator(min, max);
                for (int i = 0; i < numElems; i++) {
                        int barheight;
                        if (!sorted) {
                                barheight = gen.nextValue();
                        }
                        else {
                                barheight = min + i * (max-min)/numElems;
                        }
                        array[i] =
                                new FilledRect(
                                        i * elemWidth,
                                        max - barheight,
                                        elemWidth - gap,
                                        barheight,
                                        canvas);
                        if (elemWidth >= 30) {
                                labelRectangle(canvas, elemWidth, max, i);
                        }
                }
        }

        // Label the rectangle with its height
        private void labelRectangle(DrawingCanvas canvas, int elemWidth, int labelBottom, int i) {
                Text t =
                        new Text(i/*getElement(i)*/, i * elemWidth + 2, labelBottom - fontSize, canvas);
                t.setFontSize(fontSize);
                if (t.getWidth() > elemWidth) {
                        // Pick the largest font size that will fit in the bars.
                        fontSize = (int) (fontSize * (elemWidth - 4) / t.getWidth());
                        t.moveTo(i * elemWidth + 2, labelBottom - fontSize - 1);
                        t.setFontSize (fontSize);
                }
                t.setColor(Color.white);
                labels[i] = t;
        }

        // Return the size of the array
        public int length() {
                return array.length;
        }

        // Return the value of the ith element.
        public int getElement(int i) {
                return (int) (array[i].getHeight());
        }

        // Change the value of the ith element to v.  Redraw the
        // corresponding rectangle.
        public void setElement(int i, int v) {
                FilledRect ai = array[i];
                double bottom = ai.getHeight() + ai.getY();
                ai.setHeight(v);
                ai.moveTo(ai.getX(), bottom - v);
                //if (labels[i] != null) {
                //        labels[i].setText(v);
                //}
        }

        // Swap the ith and jth rectangles.
        public void swap(int i, int j) {
                int vi = (int) array[i].getHeight();
                int vj = (int) array[j].getHeight();
                setElement(i, vj);
                setElement(j, vi);
        }

        // Colors the rectangle at the given index
        // to show that it has been found
        public void markFound(int index) {
                array[index].setColor(FOUND_COLOR);
        }

        // Colors the ith rectangle to show it has been visited.
        public void visit(int i) {
                array[i].setColor(VISITED_COLOR);
        }

        // Colors all the rectangles between start and end
        // to show they have been visited.
        public void visit(int start, int end) {
                for (int i = start; i <= end; i++) {
                        array[i].setColor(VISITED_COLOR);
                }
        }

        // Colors all the rectangles to their original color.
        public void clear() {
                for (int i = 0; i < array.length; i++) {
                        array[i].setColor(BASIC_COLOR);
                }
        }

        // Colors all the rectangles between start and end
        // to their original color
        public void clear(int start, int end) {
                for (int i = start; i <= end; i++) {
                        array[i].setColor(BASIC_COLOR);
                }
        }

        // Removes all the rectangles from the canvas.
        public void removeFromCanvas() {
                for (int i = 0; i < array.length; i++) {
                        array[i].removeFromCanvas();
                        if (labels[i] != null) {
                                labels[i].removeFromCanvas();
                        }
                }
        }

        // Returns true if the rectangle has been visited.
        public boolean isVisited(int i) {
                return array[i].getColor() == VISITED_COLOR;
        }

        // Returns the value of the rectangle at a given location.
        // Returns -1 if the location is not inside any rectangle
        public int getValueForPoint(Location p) {
                for (int i = 0; i < array.length; i++) {
                        if (array[i].contains(p)) {
                                return getElement(i);
                        }
                }
                return -1;
        }

        // Returns the values in the array separated by 
        // newlines
        public String toString() {
                String s = "";
                for (int i = 0; i < this.length(); i++) {
                        s += this.getElement(i) + "\n";
                }
                return s;
        }
}
