
package lejos.nxt;

/**
 * Text and graphics output to the LCD display.
 * 
 * @author Andre Nijholt and BB bitBlt and other mods Andy Shaw
 */
public class LCD {
	public static final int SCREEN_WIDTH 	= 100;
	public static final int SCREEN_HEIGHT 	= 64;
	
	public static final int DISPLAY_WIDTH 	= 100;
	public static final int DISPLAY_DEPTH 	= 8;	// 8 * 8 bits = 64 pixels
	
	public static final int NOOF_CHARS 		= 128;
	public static final int FONT_WIDTH 		= 5;
	public static final int FONT_HEIGHT		= 8;
	public static final int CELL_WIDTH 		= FONT_WIDTH + 1;
	public static final int CELL_HEIGHT		= FONT_HEIGHT;
	
	public static final int DISPLAY_CHAR_WIDTH = DISPLAY_WIDTH / CELL_WIDTH;
	public static final int DISPLAY_CHAR_DEPTH = DISPLAY_DEPTH;

    private static byte[] font = getSystemFont();
	private static byte [] displayBuf = getDisplay();
	
	/**
	 * Common raster operations for use with bitBlt
	 */
	public static final int ROP_CLEAR		=	0x00000000;
	public static final int ROP_AND			=	0xff000000;
	public static final int ROP_ANDREVERSE	=	0xff00ff00;
	public static final int ROP_COPY		=	0x0000ff00;
	public static final int ROP_ANDINVERTED	=	0xffff0000;
	public static final int ROP_NOOP		=	0x00ff0000;
	public static final int ROP_XOR			=	0x00ffff00;
	public static final int ROP_OR			=	0xffffff00;
	public static final int ROP_NOR			=	0xffffffff;
	public static final int ROP_EQUIV		=	0x00ffffff;
	public static final int ROP_INVERT		=	0x00ff00ff;
	public static final int ROP_ORREVERSE	=	0xffff00ff;
	public static final int ROP_COPYINVERTED=	0x0000ffff;
	public static final int ROP_ORINVERTED	=	0xff00ffff;
	public static final int ROP_NAND		=	0xff0000ff;
	public static final int ROP_SET			=	0x000000ff;

	/**
	 * Standard two input BitBlt function with the LCD display as the
	 * destination. Supports standard raster ops and
	 * overlapping images. Images are held in native leJOS/Lego format.
	 * @param src byte array containing the source image
	 * @param sw Width of the source image
	 * @param sh Height of the source image
	 * @param sx X position to start the copy from
	 * @param sy Y Position to start the copy from
	 * @param dx X destination
	 * @param dy Y destination
	 * @param w width of the area to copy
	 * @param h height of the area to copy
	 * @param rop raster operation.
	 */
	public static void bitBlt(byte [] src, int sw, int sh, int sx, int sy, int dx, int dy, int w, int h, int rop)
	{
		bitBlt(src, sw, sh, sx, sy, displayBuf, SCREEN_WIDTH, SCREEN_HEIGHT, dx, dy, w, h, rop);	
	}
	/**
	 * Special case bitBlt with no input image. Can be used to clear areas
	 * draw rectangles etc.
	 * @param dx X destination
	 * @param dy Y destination
	 * @param w width of the area to copy
	 * @param h height of the area to copy
	 * @param rop raster operation.
	 */	
	public static void bitBlt(int dx, int dy, int w, int h, int rop)
	{
		bitBlt(displayBuf, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, displayBuf, SCREEN_WIDTH, SCREEN_HEIGHT, dx, dy, w, h, rop);	
	}	
	/**
	 * Method to set a pixel to screen.
	 */
	public static void setPixel(int rgbColor, int x, int y) {
		if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT) return; // Test-Modify for speed
		int bit = (y & 0x7);
		int index = (y/8)*DISPLAY_WIDTH + x;
		displayBuf[index] = (byte)((displayBuf[index] & ~(1 << bit)) | (rgbColor << bit));
	}
	/**
	 * Output a string to the display. Allow any location. Allow the string to
	 * be inverted.
	 * @param str String to display	
	 * @param x X location (pixels)
	 * @param y Y location (pixels)
	 * @param invert set to true to displayed the string inverted
	 */
	public static void drawString(String str, int x, int y, boolean invert) {
		char [] strData = str.toCharArray();
		if (invert)
		{
			for (int i = 0; (i < strData.length); i++) {
				drawChar(strData[i], x + i*CELL_WIDTH, y, true);
			}
		}
		else
		{
			for (int i = 0; (i < strData.length); i++) {
				drawChar(strData[i], x + i*CELL_WIDTH, y, ROP_COPY);
			}
		}		
		
	}
	/**
	 * Draw a single char to an arbitary location on the screen.
	 * @param c Character to display
	 * @param x X location (pixels)
	 * @param y Y location (pixels)
	 * @param invert Set to true to invert the display
	 */
	public static void drawChar(char c, int x, int y, boolean invert) {
		bitBlt(font, FONT_WIDTH*128, FONT_HEIGHT, FONT_WIDTH*c, 0, x, y, FONT_WIDTH, FONT_HEIGHT, (invert ? ROP_COPYINVERTED : ROP_COPY));
		if (invert) bitBlt(x+FONT_WIDTH, y, 1, FONT_HEIGHT, ROP_SET);
	}
	
	/**
	 * Output a string to the display. Allow any location. Allow use of raster
	 * operations.
	 * @param str String to display	
	 * @param x X location (pixels)
	 * @param y Y location (pixels)
	 * @param rop Raster operation
	 */
	public static void drawString(String str, int x, int y, int rop) {
		char [] strData = str.toCharArray();
		for (int i = 0; (i < strData.length); i++) {
			drawChar(strData[i], x + i*CELL_WIDTH, y, rop);
		}
	}

	/**
	 * Draw a single char to an arbitary location on the screen.
	 * @param c Character to display
	 * @param x X location (pixels)
	 * @param y Y location (pixels)
	 * @param rop Raster operation for how to combine with existing content
	 */
	public static void drawChar(char c, int x, int y, int rop) {
		bitBlt(font, FONT_WIDTH*128, FONT_HEIGHT, FONT_WIDTH*c, 0, x, y, FONT_WIDTH, FONT_HEIGHT, rop);
	}
	
	/**
	 * Draw pixels a byte at a time. Should probably no longer be used.
	 * @param b
	 * @param x
	 * @param y
	 * @param invert
	 */
	public static void drawPixels(byte b, int x, int y, boolean invert) {
		int index = (y/8)*DISPLAY_WIDTH + x;
		displayBuf[index] |= ((invert ? (b ^ 0xFF) : b));
	}

	public static void clearDisplay() {
		clear();
	}
	
	public static void setDisplay() {
	}

	/**
	 * Display a string on the LCD at specified x,y co-ordinate.
	 */
	public static native void drawString(String str, int x, int y);

	/**
	 * Display an int on the LCD at specified x,y co-ordinate.
	 */
	public static native void drawInt(int i, int x, int y);

	/**
	 * Display an in on the LCD at x,y with leading spaces to occupy at least the number
	 * of characters specified by the places parameter.
	 */
	public static native void drawInt(int i, int places, int x, int y);

	/**
	 * Update the display.
	 */
	public static native void refresh();
	  
	/**
	 * Clear the display.
	 */
	public static native void clear();
	  
	/**
	 * Write graphics from a Java buffer to the display.
	 */
	public static native void setDisplay(int[] buff);
	
	/**
	 * Provide access to the LCD display frame buffer. Allows both the firmware
	 * and Java to make changes.
	 * @return byte array that is the frame buffer.
	 */
	public static native byte [] getDisplay();
    
	/**
	 * Provide access to the LCD system font. Allows both the firmware
	 * and Java to share the same font bitmaps.
	 * @return byte array that is the frame buffer.
	 */
	public static native byte [] getSystemFont();
	
	/**
	 * Turn on/off the automatic refresh of the LCD display. At system startup
	 * auto refresh is on.
	 * @param mode 1 to enable 0 to disable
	 */
	public static native void setAutoRefresh(int mode);
	
	/**
	 * Standard two input BitBlt function. Supports standard raster ops and
	 * overlapping images. Images are held in native leJOS/Lego format.
	 * @param src byte array containing the source image
	 * @param sw Width of the source image
	 * @param sh Height of the source image
	 * @param sx X position to start the copy from
	 * @param sy Y Position to start the copy from
	 * @param dst byte array containing the destination image
	 * @param dw Width of the destination image
	 * @param dh Height of the destination image
	 * @param dx X destination
	 * @param dy Y destination
	 * @param w width of the area to copy
	 * @param h height of the area to copy
	 * @param rop raster operation.
	 */
	public native static void bitBlt(byte [] src, int sw, int sh, int sx, int sy, byte dst[], int dw, int dh, int dx, int dy, int w, int h, int rop);
	
	/**
	 * Scrolls the screen up one text line
	 *
	 */
	public static void scroll() {
		LCD.bitBlt(displayBuf, SCREEN_WIDTH, SCREEN_HEIGHT, 0, CELL_HEIGHT,
				   0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - CELL_HEIGHT, ROP_COPY);
		LCD.bitBlt(0, SCREEN_HEIGHT - CELL_HEIGHT,
				   SCREEN_WIDTH, CELL_HEIGHT, ROP_CLEAR);
	}
}
