import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.MediaTracker;
 

/**
 * <B>Image2Ascii V1.0b</b><BR>
 * <b>Author:</b> Harel Malka Oct/2001<P>
 * Image2Ascii recieves an GIF/JPG image path, and a set of parameters, 
 * and the image converted to text, ascii or html table.
 * <BR>
 * <P>Usage example:<BR>
 * <pre>Image2Ascii?p=/jpg.jpg&s=r&fs=10px&flh=50% - Draws jpg.jpg using 'r' char in 10px & 50% line height.
 * Image2Ascii?p=/jpg.jpg&s=x&fs=13px&flh=90%&g=1 - Draws grayscaled jpg.jpg using 'x' char, 13px & 90% height.		
 * Image2Ascii?p=/jpg.jpg&a=1 - Draws jpg.jpg as ascii only
 * Image2Ascii?p=/jpg.jpg&tbl=1 -  Draws jpg.jpg with as an html table</pre>
 * </P>
 * Image2Ascii uses a properties file names <i>Image2Ascii.properties</i>, which contains
 * the root local path for all images. This must be set to fit your web server.
 * <BR>The properties file should be place at the root directory of your
 * webserver.<BR>
 * <pre>
 * Input Parameters:
 * p	path to the image
 * s	symbol to draw the image with 	
 * t	text to draw the image with (overide s)
 * fl 	filename to read text from 
 * g	draw in grayscale
 * a	draw as ascii only 
 * tbl  draw as a html table 
 * fs   font size (in px or pt)
 * flh  font line height (in % or pt)
 *
 * for more:  http://www.harelmalka.com
 * 
 * License: None - do whatever you want with this. Obviously - there's no guarantee...
 * 
 * </pre>
 * @author  Harel Malka Oct/2001
 * @version v1.0b
 * -----------------------------------
 *
 */
public class Image2Ascii extends HttpServlet {
	private static final String VERSION			= "1.0";
	private static final String SERVLET_NAME_DEBUG		= "Image2Ascii :: ";
	private static String PROPERTIES_FILE			= "Image2Ascii.properties";
	private static String BACKGROUND_COLOR 			= "black";
	private static String FONT_ERROR_COLOR 			= "white";
	private static String ROOT_IMAGE_PATH 			= "/home/images/"; 
	private static String FONT_FAMILY			= "Verdana, Geneva, Arial, Helvetica, sans-serif";
	private static String FONT_SIZE				= "9px";
	private static String FONT_WEIGHT			= "bold";
	private static String FONT_LINE_HEIGHT			= "70%";
	private static int IMAGE2ASCII2_INSTANCECOUNT 		= 0;
	private static int IMAGE2ASCII2_REQUESTCOUNT 		= 0;
	private String out_content_type 			= null;				
	public String imageSymbol				=":";
	MediaTracker mt;
   	Frame frame						= null;
	Graphics g 						= null;
	Toolkit toolkit;
 	private final String ASCII_LINGO_DEFAULT	= "YourTextCanComeHere";
	private String asciiLingo	= ASCII_LINGO_DEFAULT;
	private int asciiLingo_cursor = 0;
	private char asciiChars[] = asciiLingo.toCharArray();
	
	/**Initialize global variables*/
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		IMAGE2ASCII2_INSTANCECOUNT++;
		ServletContext sc = config.getServletContext();
		String propFilePath =  sc.getRealPath(PROPERTIES_FILE);
		PROPERTIES_FILE = propFilePath; 
		System.out.println(SERVLET_NAME_DEBUG + "propFilePath = " + propFilePath);
		try {
			readPropertiesFile();
		} catch (Exception e) { 
			System.out.println(SERVLET_NAME_DEBUG + "Error reading properties file." + e.getMessage()); 
		}
		frame = new Frame();
		frame.addNotify();
		toolkit = Toolkit.getDefaultToolkit();
		mt = new MediaTracker(frame);
	}
	
  	/**Process the HTTP Get request*/
	public void doGet(HttpServletRequest request, HttpServletResponse response) 
		throws ServletException, IOException {
  		// initialize request parameters.
		asciiLingo_cursor=0;
		asciiLingo	= ASCII_LINGO_DEFAULT;
		String imagePath = ""; 
		String fontSize = "";
		String fontLineHeight = "";
		boolean tableDisplay = false;
		boolean grayScale   = false;
		boolean asciiOnlyMode = false;
		Date now = new Date();
		StringBuffer sb = new StringBuffer();
		IMAGE2ASCII2_REQUESTCOUNT++;
		System.out.println(SERVLET_NAME_DEBUG+" @ " + now.toString() + ". Request count is " + IMAGE2ASCII2_REQUESTCOUNT);
		if (request.getParameter("p")!=null) 	{ 
			imagePath = request.getParameter("p"); 
		}
		
		if (request.getParameter("g")!=null) 	{ 
			grayScale = true; 
		} else { grayScale = false; }
		
		if (request.getParameter("fs")!=null) 	{ 
			fontSize = request.getParameter("fs"); 
		} else { fontSize = FONT_SIZE; }
		
		if (request.getParameter("flh")!=null) 	{ 
			fontLineHeight = request.getParameter("flh"); 
		} else { fontLineHeight = FONT_LINE_HEIGHT; }
		
		if (request.getParameter("s")!=null)	{ 
			imageSymbol = request.getParameter("s"); 
		} else {
			imageSymbol = null; }
		if (request.getParameter("t")!=null) 	{ 
				imageSymbol = null;
				asciiLingo = request.getParameter("t");  
				asciiChars = asciiLingo.toCharArray();
		}
		
		if (request.getParameter("a")!=null) 	{ 
			asciiOnlyMode = true; 
		} else { asciiOnlyMode = false;  }
		
		if (request.getParameter("fl")!=null) 	{ 
			String textFileName = request.getParameter("fl");
			try {
				asciiChars = readFile(textFileName);
			} catch (Exception e) {
				System.out.println(SERVLET_NAME_DEBUG+" @ " + now.toString() + ". Cannot read file " + textFileName + "\n" + e.getMessage());	
			}
		} else {  
		}
		
		if (request.getParameter("tbl")!=null) 	{ tableDisplay = true; } 
		
		
		
		String local_image_path = ROOT_IMAGE_PATH + imagePath;
		response.setContentType("text/html");
    		PrintWriter out = response.getWriter();
		out.println("<html><head><title>Image2Ascii</title>");
		File imageFile = new File(local_image_path);
	   	if (!imageFile.exists()) { 
			sb.append("</head><body><font color=" + FONT_ERROR_COLOR + ">Image file not found: "  + local_image_path + "</font><BR></body></html>"); 
		} else { 
			Image image = toolkit.getImage(local_image_path);
			mt.addImage(image,0);
			try { 
				mt.waitForID(0); 
			} catch (InterruptedException e) { //nothing
			}
			
			 
			sb.append(buildStyle(fontSize, fontLineHeight));
			sb.append("</head><body>\n");
			sb.append("<!-- Created @ ");
			sb.append(now.toString());
			sb.append(" Using Image2Ascii by Harel Malka 2001 	http://www.harelmalka.com -->\n");
			try {
				String temp = 	convertToAscii(image, tableDisplay, grayScale, asciiOnlyMode);
				sb.append(temp);
			} catch (Exception e) {
				System.out.println(SERVLET_NAME_DEBUG+"Could not convert : " + e.getMessage());	
			}
			sb.append("\n<P><span class=\"footer\">Created @ ");
			sb.append(now.toString());
			sb.append(" Using Image2Ascii by Harel Malka 2001 	http://www.harelmalka.com</span></P>\n");
			sb.append("</body></html>");
			image = null;		
		}
		String asciiOutput = sb.toString();
		out.print(asciiOutput);
		out.close();
		sb = null;
		imageFile = null;
		fontSize = null;
	}
	
	public String buildStyle(String fontSize, String fontLineHeight) {
		StringBuffer styleb = new StringBuffer();
		styleb.append("<style type=\"text/css\">");
		styleb.append("BODY { background:");
		styleb.append(BACKGROUND_COLOR);
		styleb.append("; font-family:");
		styleb.append(FONT_FAMILY);
		styleb.append("; font-size:");
		styleb.append(fontSize);
		styleb.append("; font-weight:");
		styleb.append(FONT_WEIGHT);
		styleb.append("; line-height:");
		styleb.append(fontLineHeight);
 		styleb.append("; } .footer { color:silver; font-size: 9px; font-weight: normal;} ");
		styleb.append(" PRE { font-size:");
		styleb.append(fontSize);
		styleb.append("; color: white; }");
		styleb.append("</style>");
		return styleb.toString();
	}
	
		
	public String convertToAscii(Image img, boolean tblDisplay, boolean grayScale, boolean asciiOnlyMode) 
		throws Exception {
		StringBuffer outBuffer = new StringBuffer();
		if (asciiOnlyMode) {
			outBuffer.append("<PRE>\n");
		} else if (tblDisplay) {
			outBuffer.append("<table cellpadding=0 cellspacing=0 border=0>");
		}
		mt.addImage(img,2);
		try { 
			mt.waitForID(2); 
		} catch (InterruptedException ie) {
			System.out.println(SERVLET_NAME_DEBUG + "INTERRUPTED WHILE LOADING IMAGE"); }
		try {
			int imageDim[] = getImageDim(img);
			BufferedImage bImage = convertToBufferedImage(img);
			
			for (int y=0; y<imageDim[1]; y++) {   // rows
				if (!asciiOnlyMode && tblDisplay) {
					outBuffer.append("<tr>");
				}
				for (int x=0; x<imageDim[0]; x++) { //cols
					int thisPixel = 0;
					if (!grayScale && !asciiOnlyMode) { 
						thisPixel = 16777215 & bImage.getRGB(x,y);
						if (thisPixel<256) thisPixel = thisPixel * 65793;
					} else if (grayScale && !asciiOnlyMode) {
						double pxs = getGrayScalePixelValue(bImage.getRGB(x,y));  
						thisPixel =(int) pxs * 65793; 
					} else if (asciiOnlyMode) {
						double pxs = getGrayScalePixelValue(bImage.getRGB(x,y));  
						thisPixel =(int) pxs;
					}
					if (asciiOnlyMode) {
						String asciiSymbol = "";
						if (thisPixel < 45) {
							asciiSymbol = "X";
						} else if (thisPixel >=45 && thisPixel <79) {
							asciiSymbol = "+";
						} else if (thisPixel >= 79 && thisPixel <= 125) {
							asciiSymbol = ":";
						} else if (thisPixel > 125 && thisPixel <=200) {
							asciiSymbol = ".";
						}
						outBuffer.append(asciiSymbol);
					} else if (tblDisplay) {
						outBuffer.append("<td bgcolor=#");
					} else {
						outBuffer.append("<font color=#");
					}
					if (!asciiOnlyMode) {
						outBuffer.append(RGB2Hex(thisPixel));
						outBuffer.append(">");
						if (tblDisplay) {
							outBuffer.append("&nbsp;");
						} else if (imageSymbol==null) {
							String thisChar = ""+getAsciiChar();
							outBuffer.append(thisChar.toLowerCase());
						} else {
							outBuffer.append(imageSymbol);
						}
						if (tblDisplay) {
							outBuffer.append("</td>");
						} else {
							outBuffer.append("</font>");
						}
					}
				}
				if (asciiOnlyMode) {
					outBuffer.append("\n");
				} else if (tblDisplay) {
					outBuffer.append("</tr>");
				} else {
					outBuffer.append("<BR>");
				}
			}
		} catch (Exception e) {
			 System.out.println(SERVLET_NAME_DEBUG + "Image Convers = (int) ion Failed : " + e.getMessage());	
		}
		if (tblDisplay) { outBuffer.append("</table>"); }
		else if (asciiOnlyMode) { outBuffer.append("</pre>"); }
		return outBuffer.toString();
	  }
  	
	public char getAsciiChar() {
		if (asciiLingo_cursor>=asciiChars.length) asciiLingo_cursor = 0;
		char output = asciiChars[asciiLingo_cursor];

		asciiLingo_cursor++;
		return output;

	}
	
	
	public float getGrayScalePixelValue(int pixelVal) {
            int c = pixelVal;
            int r = (c&0xff0000)>>16;
            int g = (c&0xff00)>>8;
            int b = c&0xff;
            return (float)(r*0.299 + g*0.587 + b*0.114);
    	 }
	
	public String RGB2Hex (int rgb) {
		String output = Integer.toHexString(Math.abs(rgb));
		return output;
	}
	
	public BufferedImage convertToBufferedImage(Image img) {
		int imgDim[] = getImageDim(img);
		BufferedImage bi = new BufferedImage(imgDim[0], imgDim[1], BufferedImage.TYPE_INT_RGB);       
		Graphics2D g2d = bi.createGraphics();
		g2d.drawImage(img, 0,0,imgDim[0], imgDim[1], null);
		return bi;   
	}
	
  	/**
	 * Measures image width and height
	 * @param Image image to measure.
	 * @returns int[] image dimensions array (0=width, 1=height)
	 */
  	public int[] getImageDim(Image img) {
  		mt.addImage(img,1);
		try { 
			mt.waitForID(1);
		} catch (InterruptedException ie) {
			System.out.println("INTERUPTED");
		}
		int output[] = new int[2];
		output[0] = img.getWidth(frame);
		output[1] = img.getHeight(frame);
		return output;
  	} 
  
	 
	/**
	 * Returns Servlet information
	 * @returns String  
	 * @throws WiwReportingException
	 */
	public String getServletInfo() {
		StringBuffer sb = new StringBuffer();	
		sb.append("-----------------------------------------------------------------\n");
		sb.append("Image2Ascii v" + VERSION + " :: Image to Ascii convertion tool.\n");
		sb.append("Written by Harel Malka October-2001.\n");
		sb.append("http://www.harelmalka.com\n\n");
		sb.append("harel@harelmalka.com\n\n");
		sb.append("-----------------------------------------------------------------\n");
		return sb.toString();
	}
	
	/**
	 * Provides help about the servlet usage
	 */
  	public static String help() {
		StringBuffer sb = new StringBuffer();
		sb.append("-----------------------------------------------------------------\n");
		sb.append("Image2Ascii v" + VERSION + " :: Help.\n");
		sb.append("Written by Harel Malka October-2001.\n");
		sb.append("harel@harelmalka.com\n");
		sb.append("http://www.harelmalka.com\n\n");
		sb.append("\n");
		sb.append("Input Parameters\n");
		sb.append("\tp \t path and filename of the image to get\n");
		sb.append("\ts \t character to paint image with\n");
		sb.append("\tt \t string to paint image with (overide s)\n");
		sb.append("\tfl \t filename to read text from\n");
		sb.append("\ttbl \t html table mode \n");
		sb.append("\tg \t Grayscale mode \n");
		sb.append("\ta \t Ascii Only mode \n");
		sb.append("\tfs \t font size to use (style sheet)\n");
		sb.append("\tflh \t font line height to use (style sheet)\n");
		sb.append("\t\n");
		sb.append("Usage Examples:\n");
		sb.append("\t Image2Ascii?p=/jpg.jpg&s=r&fs=10px&flh=50% \n");
		sb.append("\t\t Draws jpg.jpg using 'r' char in 10px & 50% line height.\n");
		sb.append("\t Image2Ascii?p=/jpg.jpg&s=x&fs=13px&flh=90%&g=1 \n");
		sb.append("\t\t Draws grayscaled jpg.jpg using 'x' char, 13px & 90% height.\n");
		sb.append("\t Image2Ascii?p=/jpg.jpg&a=1 \n");
		sb.append("\t\t Draws jpg.jpg as ascii only.\n");
		sb.append("\t Image2Ascii?p=/jpg.jpg&tbl=1 \n");
		sb.append("\t\t Draws jpg.jpg with as an html table.\n\n");
		sb.append("System Default Parameters (properties file): \n");
		sb.append("-----------------------------------------------------------------\n");
		sb.append("ROOT_IMAGE_PATH \n");
		sb.append("BACKGROUND_COLOR \n");
		sb.append("FONT_ERROR_COLOR \n");
		sb.append("FONT_FAMILY \n");
		sb.append("FONT_SIZE \n");
		sb.append("FONT_LINE_HEIGHT \n");
		sb.append("\n");
		return sb.toString();
	}
	
  	public static void main(String args[]) {
		System.out.println(help());  
		System.exit(0);
  	}
	
	/**
	 * Read the properties file and assigns the root image path variable.
	 * @throws Exception 
	 */
	public void readPropertiesFile() throws Exception {
		File propFile = new File(PROPERTIES_FILE);
		if (!propFile.exists() || !propFile.canRead()) {
			throw new Exception (SERVLET_NAME_DEBUG+"Cannot open properties file" + PROPERTIES_FILE + ". Make sure that file is in the same directory as the servlet and has the right permissions set."); }
		InputStream is;
		is = new FileInputStream(propFile);
		Properties props = new Properties();
		props.load(is);
		ROOT_IMAGE_PATH 	= props.getProperty("ROOT_IMAGE_PATH");
		BACKGROUND_COLOR 	= props.getProperty("BACKGROUND_COLOR");
		FONT_ERROR_COLOR 		= props.getProperty("ERROR_COLOR");
		FONT_FAMILY			= props.getProperty("FONT_FAMILY");
		FONT_SIZE			= props.getProperty("FONT_SIZE");
		FONT_LINE_HEIGHT	= props.getProperty("FONT_LINE_HEIGHT");
		System.out.println(SERVLET_NAME_DEBUG+"Properties file read & loaded");
	}
  	
	public char[] readFile(String filename) throws Exception {
		File file = new File(ROOT_IMAGE_PATH + filename);
		if (!file.exists() || !file.canRead()) {
			throw new Exception (SERVLET_NAME_DEBUG+"Cannot open file " + filename + ". Make sure that file is in the same directory as the servlet and has the right permissions set." ); }
		FileReader fr;
		FileInputStream fis;
		fis = new FileInputStream(file);
		fr = new FileReader(file);
		char asciiChar[] = new char[fis.available()];
		//while (fr.ready()) {
			fr.read (asciiChar);
		//}
		fr.close();
		fis.close();
		file=null;
		return asciiChar;
	}
	
	 
	
  	public void destroy() {
	  	IMAGE2ASCII2_INSTANCECOUNT--;	
	  	if (frame!=null) frame.removeNotify();
  	}
}

