content: /****************************************************************************
  Copyright (c) 2010-2011 cocos2d-x.org
  
  http://www.cocos2d-x.org
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ****************************************************************************/
  package org.cocos2dx.lib;
  
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  import java.util.LinkedList;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Paint;
  import android.graphics.Typeface;
  import android.graphics.Paint.Align;
  import android.graphics.Paint.FontMetricsInt;
  import android.util.Log;
  
  public class Cocos2dxBitmap{
  	/*
  	 * The values are the same as cocos2dx/platform/CCImage.h.
  	 */
  	private static final int HALIGNCENTER = 3;
  	private static final int HALIGNLEFT	  = 1;
  	private static final int HALIGNRIGHT  = 2;
  	// vertical alignment
  	private static final int VALIGNTOP    = 1;
  	private static final int VALIGNBOTTOM = 2;
  	private static final int VALIGNCENTER = 3;
  	
  	private static Context context;
  	
  	public static void setContext(Context context){
  		Cocos2dxBitmap.context = context;
  	}
  	
  	/*
  	 * @width: the width to draw, it can be 0
  	 * @height: the height to draw, it can be 0
  	 */
      public static void createTextBitmap(String content, String fontName, 
      		int fontSize, int alignment, int width, int height){
      	
      	content = refactorString(content);   	
      	Paint paint = newPaint(fontName, fontSize, alignment);
      	
      	TextProperty textProperty = computeTextProperty(content, paint, width, height);      	
  
          int bitmapTotalHeight = (height == 0 ? textProperty.totalHeight:height);
  
          // Draw text to bitmap
          Bitmap bitmap = Bitmap.createBitmap(textProperty.maxWidth, 
          		bitmapTotalHeight, Bitmap.Config.ARGB_8888);
          Canvas canvas = new Canvas(bitmap);
          
          // Draw string
          FontMetricsInt fm = paint.getFontMetricsInt();
          int x = 0;
          int y = computeY(fm, height, textProperty.totalHeight, alignment);
          String[] lines = textProperty.lines;
          for (String line : lines){
          	x = computeX(paint, line, textProperty.maxWidth, alignment);
          	canvas.drawText(line, x, y, paint);
          	y += textProperty.heightPerLine;
          }
          
          initNativeObject(bitmap);
      }
      
      private static int computeX(Paint paint, String content, int w, int alignment){
      	int ret = 0;
      	int hAlignment = alignment & 0x0F;
      	
      	switch (hAlignment){
      	case HALIGNCENTER:
      		ret = w / 2;
      		break;
      	
          // ret = 0
      	case HALIGNLEFT:   		
      		break;
      		
      	case HALIGNRIGHT:
      		ret = w;
      		break;
      	
      	/*
      	 * Default is align left.
      	 * Should be same as newPaint().
      	 */
      	default:
      		break;
      	}
      	
      	return ret;
      }
      
      private static int computeY(FontMetricsInt fm, int constrainHeight, int totalHeight, int alignment) {
      	int y = -fm.top;
  
      	if (constrainHeight > totalHeight) {
      		int vAlignment = (alignment >> 4) & 0x0F;
      		
          	switch (vAlignment) {
          	case VALIGNTOP:
          		y = -fm.top;
          		break;
          	case VALIGNCENTER:
          		y = -fm.top + (constrainHeight - totalHeight)/2;
          		break;
          	case VALIGNBOTTOM:
          		y = -fm.top + (constrainHeight - totalHeight);
          		break;
          	default:
          		break;
          	}
      	}
      	
      	return y;
      }
      
      private static class TextProperty{
      	// The max width of lines
      	int maxWidth;
      	// The height of all lines
      	int totalHeight;
      	int heightPerLine;
      	String[] lines;
  
      	TextProperty(int w, int h, String[] lines){
      		this.maxWidth = w;
      		this.heightPerLine = h;
      		this.totalHeight = h * lines.length;   		
      		this.lines = lines;
      	}
      }
      
      private static TextProperty computeTextProperty(String content, Paint paint,
      		int maxWidth, int maxHeight){              
          FontMetricsInt fm = paint.getFontMetricsInt();
          int h = (int)Math.ceil(fm.bottom - fm.top);
          int maxContentWidth = 0;
          
          String[] lines = splitString(content, maxHeight, maxWidth, paint);
          
          if (maxWidth != 0){
          	maxContentWidth = maxWidth;
          }
          else {
          	/*
               * Compute the max width
               */
              int temp = 0;
              for (String line : lines){
              	temp = (int)Math.ceil(paint.measureText(line, 0, line.length()));
              	if (temp > maxContentWidth){
              		maxContentWidth = temp;
              	}
              }
          }        
          
          return new TextProperty(maxContentWidth, h, lines);
      }
      
      /*
       * If maxWidth or maxHeight is not 0,
       * split the string to fix the maxWidth and maxHeight.
       */
      private static String[] splitString(String content, int maxHeight, int maxWidth, 
      		Paint paint){
      	String[] lines = content.split("\\n");
      	String[] ret = null;
      	FontMetricsInt fm = paint.getFontMetricsInt();
          int heightPerLine = (int)Math.ceil(fm.bottom - fm.top);
          int maxLines = maxHeight / heightPerLine;
      	
      	if (maxWidth != 0){
      		LinkedList<String> strList = new LinkedList<String>();
      		for (String line : lines){
      			/*
      			 * The width of line is exceed maxWidth, should divide it into
      			 * two or more lines.
      			 */
      			int lineWidth = (int)Math.ceil(paint.measureText(line));
      			if (lineWidth > maxWidth){    				
      				strList.addAll(divideStringWithMaxWidth(paint, line, maxWidth));
      			}
      			else{
      				strList.add(line);
      			}
      			
      			/*
      			 * Should not exceed the max height;
      			 */
      			if (maxLines > 0 && strList.size() >= maxLines){
      				break;
      			}
      		}
      		
      		/*
      		 * Remove exceeding lines
      		 */
      		if (maxLines > 0 && strList.size() > maxLines){
      			while (strList.size() > maxLines){
      				strList.removeLast();
      			}
      		}
      		
      		ret = new String[strList.size()];
      		strList.toArray(ret);
      	} else 
      	if (maxHeight != 0 && lines.length > maxLines) {
      		/*
      		 * Remove exceeding lines
      		 */
      		LinkedList<String> strList = new LinkedList<String>();
      		for (int i = 0; i < maxLines; i++){
      			strList.add(lines[i]);
      		}
      		ret = new String[strList.size()];
      		strList.toArray(ret);
      	}
      	else {
      		ret = lines;
      	}
      	
      	return ret;
      }
      
      private static LinkedList<String> divideStringWithMaxWidth(Paint paint, String content, 
      		int width){
      	int charLength = content.length();
      	int start = 0;
      	int tempWidth = 0;
      	LinkedList<String> strList = new LinkedList<String>();
      	
      	/*
      	 * Break a String into String[] by the width & should wrap the word
      	 */
      	for (int i = 1; i <= charLength; ++i){  		
      		tempWidth = (int)Math.ceil(paint.measureText(content, start, i));
      		if (tempWidth >= width){
      			int lastIndexOfSpace = content.substring(0, i).lastIndexOf(" ");
      			
      			if (lastIndexOfSpace != -1 && lastIndexOfSpace > start){
      				/**
      				 * Should wrap the word
      				 */
      				strList.add(content.substring(start, lastIndexOfSpace));
      				i = lastIndexOfSpace;
      			}
      			else {
      				/*
          			 * Should not exceed the width
          			 */
          			if (tempWidth > width){
          				strList.add(content.substring(start, i - 1));
          				/*
          				 * compute from previous char
          				 */
          				--i;
          			}
          			else {
          				strList.add(content.substring(start, i));   				
          			}
      			}
      			
      			// remove spaces at the beginning of a new line
      			while(content.indexOf(i++) == ' ') {
      			}
      			   			
      			start = i;
      		}
      	}
      	
      	/*
      	 * Add the last chars
      	 */
      	if (start < charLength){
      		strList.add(content.substring(start));
      	}
      	
      	return strList;
      }
      
      private static Paint newPaint(String fontName, int fontSize, int alignment){
      	Paint paint = new Paint();
      	paint.setColor(Color.WHITE);
          paint.setTextSize(fontSize);      
          paint.setAntiAlias(true);    
          
          /*
           * Set type face for paint, now it support .ttf file.
           */
          if (fontName.endsWith(".ttf")){
          	 try {
          		//Typeface typeFace = Typeface.createFromAsset(context.getAssets(), fontName);
          		 Typeface typeFace = Cocos2dxTypefaces.get(context, fontName);
                	paint.setTypeface(typeFace);
               } catch (Exception e){
               	Log.e("Cocos2dxBitmap", 
               		"error to create ttf type face: " + fontName);
               	
               	/*
               	 * The file may not find, use system font
               	 */
               	paint.setTypeface(Typeface.create(fontName, Typeface.NORMAL));
               }
          }
          else {
          	paint.setTypeface(Typeface.create(fontName, Typeface.NORMAL));
          }
          
          int hAlignment = alignment & 0x0F;
          switch (hAlignment){
      	case HALIGNCENTER:
      		paint.setTextAlign(Align.CENTER);
      		break;
      	
      	case HALIGNLEFT:  
      		paint.setTextAlign(Align.LEFT);
      		break;
      		
      	case HALIGNRIGHT:
      		paint.setTextAlign(Align.RIGHT);
      		break;
      	
      	default:
      		paint.setTextAlign(Align.LEFT);
      		break;
      	}
          
          return paint;
      }
      
      private static String refactorString(String str){
      	// Avoid error when content is ""
  		if (str.compareTo("") == 0){
  			return " ";
  		}
  		
  		/*
  		 * If the font of "\n" is "" or "\n", insert " " in front of it.
  		 * 
  		 * For example:
  		 * "\nabc"     -> " \nabc"
  		 * "\nabc\n\n" -> " \nabc\n \n"
  		 */
  		StringBuilder strBuilder = new StringBuilder(str);
  		int start = 0;
  		int index = strBuilder.indexOf("\n");
  		while (index != -1){
  			if (index == 0 || strBuilder.charAt(index -1) == '\n'){
  				strBuilder.insert(start, " ");
  				start = index + 2;
  			} else {
  				start = index + 1;
  			}
  			
  			if (start > strBuilder.length() || index == strBuilder.length()){
  				break;
  			}
  			
  			index = strBuilder.indexOf("\n", start);			
  		}
  		
  		return strBuilder.toString();
  	}
      
      private static void initNativeObject(Bitmap bitmap){
      	byte[] pixels = getPixels(bitmap);
      	if (pixels == null){
      		return;
      	}
      	
      	nativeInitBitmapDC(bitmap.getWidth(), bitmap.getHeight(), pixels);
      }
      
      private static byte[] getPixels(Bitmap bitmap){
      	if (bitmap != null){
      		byte[] pixels = new byte[bitmap.getWidth() * bitmap.getHeight() * 4];
      		ByteBuffer buf = ByteBuffer.wrap(pixels);
      		buf.order(ByteOrder.nativeOrder());
      		bitmap.copyPixelsToBuffer(buf);
      		return pixels;
      	}
      	
      	return null;
      }
      
      private static native void nativeInitBitmapDC(int width, int height, byte[] pixels);
  }
  