package loon.component;
import loon.LRelease;
import loon.LSystem;
import loon.LTexture;
import loon.canvas.LColor;
import loon.event.Updateable;
import loon.font.FontSet;
import loon.font.IFont;
import loon.font.LFont;
import loon.geom.Vector2f;
import loon.opengl.GLEx;
import loon.opengl.LSTRDictionary;
import loon.opengl.LSTRFont;
import loon.utils.StringUtils;
import loon.utils.TArray;
public class Print implements FontSet<Print>, LRelease {
public enum Mode {
NONE, LEFT, RIGHT, CENTER
}
// they is other char flags
private final static char[] _wrapchars = { '\u3002', '\u3001', '\uff0c',
'\uff0e', '\u300d', '\uff3d', '\u3011', '\u300f', '\u30fc',
'\uff5e', '\uff09', '\u3041', '\u3043', '\u3045', '\u3047',
'\u3049', '\u30a1', '\u30a3', '\u30a5', '\u30a7', '\u30a9',
'\u30c3', '\u30e3', '\u30e5', '\u30e7', '\u30ee', '\u308e',
'\u3083', '\u3085', '\u3087', '\u3063', '\u2026', '\uff0d',
'\uff01', '\uff1f' };
private final static int _otherFlagsSize = _wrapchars.length;
/**
* 返回指定字符串,匹配指定字体后,在指定宽度内的每行应显示字符串.
*
* PS:此项不处理'\n'外的特殊操作符
*
* @param text
* @param font
* @param width
* @return
*/
public static TArray<String> formatMessage(String text, IFont font,
int width) {
TArray<String> list = new TArray<String>();
if (text == null) {
return list;
}
char c1 = '〜';
char c2 = 65374;
String str = text.replace(c1, c2);
String line = "";
int i = 0;
while (i <= str.length()) {
if (i == str.length()) {
list.add(line);
break;
}
char c = str.charAt(i);
if ((c == '\n') || (font.stringWidth(line + c) > width)) {
line = str.substring(0, i);
for (int j = 0; j < _otherFlagsSize; j++) {
if (c == _wrapchars[j]) {
int delta = font.stringWidth(line + c) - width;
if (delta < 15) {
line = str.substring(0, ++i);
break;
}
}
}
i += (c == '\n' ? 1 : 0);
list.add(line);
line = "";
str = str.substring(i);
i = 0;
} else {
line = line + c;
i++;
}
}
return list;
}
private int index, offset, font, tmp_font;
private char text;
private char[] showMessages;
private LColor fontColor = new LColor(LColor.white);
private int interceptMaxString;
private int interceptCount;
private int messageLength = 10;
private String messages;
private boolean onComplete, newLine, visible;
private StringBuffer messageBuffer = new StringBuffer(messageLength);
private int width, height, leftOffset, topOffset, next, messageCount;
private float alpha;
private int size, wait, tmp_dir, left, fontSize, fontHeight;
private Vector2f vector;
private LTexture creeseIcon;
private LSTRFont strings;
private IFont ifont;
private boolean isEnglish, isWait;
private float iconX, iconY;
private int lazyHashCade = 1;
// 默认0,左1,右2
private Mode dirmode = Mode.NONE;
public Print(Vector2f vector, IFont font, int width, int height) {
this("", font, vector, width, height);
}
public Print(String context, IFont font, Vector2f vector, int width,
int height) {
this.setMessage(context, font);
this.vector = vector;
this.width = width;
this.height = height;
this.wait = 0;
this.isWait = false;
}
private boolean nativeFont = false;
public void setMessage(String context, IFont font) {
setMessage(context, font, false);
}
private class PrintUpdate implements Updateable {
Print _print;
boolean _isComplete = false, _drawDrawingFont = false;
private IFont _font = null;
private String _context = null;
private PrintUpdate(Print print, String context, IFont font,
boolean isComplete, boolean drawFont) {
_print = print;
_context = context;
_font = font;
_isComplete = isComplete;
_drawDrawingFont = drawFont;
}
@Override
public void action(Object a) {
if (_context == null) {
return;
}
if (_print.strings != null && !_print.strings.isClose()
&& !_drawDrawingFont) {
_print.strings.close();
}
// 如果是默认的loon系统字体
if (_font instanceof LFont) {
if (_drawDrawingFont) {
LSTRDictionary.Dict dict = LSTRDictionary.get().bind(
(LFont) _font, _context);
_print.strings = dict.getSTR();
_print.ifont = _font;
} else {
_print.strings = new LSTRFont((LFont) _font, _context,
LSystem.isHTML5());
}
// 其他字体(一般是Bitmap Font)
} else {
_print.ifont = _font;
}
_print.lazyHashCade = 1;
_print.wait = 0;
_print.visible = false;
_print.showMessages = new char[] { '\0' };
_print.interceptMaxString = 0;
_print.next = 0;
_print.messageCount = 0;
_print.interceptCount = 0;
_print.size = 0;
_print.tmp_dir = 0;
_print.left = 0;
_print.fontSize = 0;
_print.fontHeight = 0;
_print.messages = _context;
_print.next = _context.length();
_print.onComplete = false;
_print.newLine = false;
_print.messageCount = 0;
_print.messageBuffer.delete(0, messageBuffer.length());
if (_isComplete) {
_print.complete();
}
_print.visible = true;
}
}
public void setMessage(String context, IFont font, boolean isComplete) {
setMessage(context, font, isComplete, false);
}
public void setMessage(String context, IFont font, boolean isComplete,
boolean drawFont) {
LSystem.load(new PrintUpdate(this, context, font, isComplete,
this.nativeFont = drawFont));
}
public String getMessage() {
return messages;
}
private LColor getColor(char flagName) {
if ('r' == flagName || 'R' == flagName) {
return LColor.red;
} else if ('b' == flagName || 'B' == flagName) {
return LColor.black;
} else if ('l' == flagName || 'L' == flagName) {
return LColor.blue;
} else if ('g' == flagName || 'G' == flagName) {
return LColor.green;
} else if ('o' == flagName || 'O' == flagName) {
return LColor.orange;
} else if ('y' == flagName || 'Y' == flagName) {
return LColor.yellow;
} else if ('m' == flagName || 'M' == flagName) {
return LColor.magenta;
} else if ('d' == flagName || 'D' == flagName) {
return LColor.darkGray;
} else if ('e' == flagName || 'E' == flagName) {
return LColor.green;
} else if ('p' == flagName || 'P' == flagName) {
return LColor.pink;
}
return null;
}
public void draw(GLEx g) {
draw(g, LColor.white);
}
private void drawMessage(GLEx gl, LColor old) {
if (!visible) {
return;
}
if ((strings == null && ifont != null) || nativeFont) {
drawBMFont(gl, old);
} else if (strings != null) {
drawDefFont(gl, old);
}
}
public void drawDefFont(GLEx g, LColor old) {
synchronized (showMessages) {
this.size = showMessages.length;
this.fontSize = (int) (isEnglish ? strings.getSize() / 2 : strings
.getSize());
this.fontHeight = strings.getHeight();
switch (dirmode) {
default:
case NONE:
this.tmp_dir = 2;
break;
case LEFT:
this.tmp_dir = (width - (fontSize * messageLength)) / 2
- (int) (fontSize * 1.5);
break;
case RIGHT:
this.tmp_dir = (fontSize * messageLength) / 2;
break;
case CENTER:
this.tmp_dir = width / 2 - (fontSize * messageLength) / 2
+ (int) (fontSize * 4);
break;
}
this.left = tmp_dir;
this.index = offset = font = tmp_font = 0;
int hashCode = 1;
hashCode = LSystem.unite(hashCode, size);
hashCode = LSystem.unite(hashCode, left);
hashCode = LSystem.unite(hashCode, fontSize);
hashCode = LSystem.unite(hashCode, fontHeight);
if (strings == null) {
return;
}
if (hashCode == lazyHashCade) {
strings.postCharCache();
if (iconX != 0 && iconY != 0) {
g.draw(creeseIcon, iconX, iconY);
}
return;
}
strings.startChar();
fontColor = old;
for (int i = 0; i < size; i++) {
text = showMessages[i];
if (text == '\0') {
continue;
}
if (interceptCount < interceptMaxString) {
interceptCount++;
continue;
} else {
interceptMaxString = 0;
interceptCount = 0;
}
if (showMessages[i] == 'n'
&& showMessages[i > 0 ? i - 1 : 0] == '\\') {
index = 0;
left = tmp_dir;
offset++;
continue;
} else if (text == '\n') {
index = 0;
left = tmp_dir;
offset++;
continue;
} else if (text == '<') {
LColor color = getColor(showMessages[i < size - 1 ? i + 1
: i]);
if (color != null) {
interceptMaxString = 1;
fontColor = color;
}
continue;
} else if (showMessages[i > 0 ? i - 1 : i] == '<'
&& getColor(text) != null) {
continue;
} else if (text == '/') {
if (showMessages[i < size - 1 ? i + 1 : i] == '>') {
interceptMaxString = 1;
fontColor = old;
}
continue;
} else if (index > messageLength) {
index = 0;
left = tmp_dir;
offset++;
newLine = false;
} else if (text == '\\') {
continue;
}
tmp_font = strings.charWidth(text);
if (Character.isLetter(text)) {
if (tmp_font < fontSize) {
font = fontSize;
} else {
font = tmp_font;
}
} else {
font = fontSize;
}
left += font;
if (font <= 10 && StringUtils.isSingle(text)) {
left += 12;
}
if (i != size - 1) {
strings.addChar(text, vector.x + left + leftOffset,
(offset * fontHeight) + vector.y + fontSize
+ topOffset, fontColor);
} else if (!newLine && !onComplete) {
iconX = vector.x + left + leftOffset;
iconY = (offset * fontHeight) + vector.y + fontSize
+ topOffset + strings.getAscent();
if (iconX != 0 && iconY != 0) {
g.draw(creeseIcon, iconX, iconY);
}
}
index++;
}
strings.stopChar();
strings.saveCharCache();
lazyHashCade = hashCode;
if (messageCount == next) {
onComplete = true;
}
}
}
public void drawBMFont(GLEx g, LColor old) {
synchronized (showMessages) {
this.size = showMessages.length;
if (nativeFont) {
this.fontSize = (int) (isEnglish ? strings.getSize() / 2
: ifont.getSize());
this.fontHeight = strings.getHeight();
} else {
this.fontSize = (int) (isEnglish ? ifont.getSize() / 2 : ifont
.getSize());
this.fontHeight = ifont.getHeight();
}
switch (dirmode) {
default:
case NONE:
this.tmp_dir = 0;
break;
case LEFT:
this.tmp_dir = (width - (fontSize * messageLength)) / 2
- (int) (fontSize * 1.5);
break;
case RIGHT:
this.tmp_dir = (fontSize * messageLength) / 2;
break;
case CENTER:
this.tmp_dir = width / 2 - (fontSize * messageLength) / 2
+ (int) (fontSize * 4);
break;
}
this.left = tmp_dir;
this.index = offset = font = tmp_font = 0;
fontColor = old;
for (int i = 0; i < size; i++) {
text = showMessages[i];
if (text == '\0') {
continue;
}
if (interceptCount < interceptMaxString) {
interceptCount++;
continue;
} else {
interceptMaxString = 0;
interceptCount = 0;
}
if (showMessages[i] == 'n'
&& showMessages[i > 0 ? i - 1 : 0] == '\\') {
index = 0;
left = tmp_dir;
offset++;
continue;
} else if (text == '\n') {
index = 0;
left = tmp_dir;
offset++;
continue;
} else if (text == '<') {
LColor color = getColor(showMessages[i < size - 1 ? i + 1
: i]);
if (color != null) {
interceptMaxString = 1;
fontColor = color;
}
continue;
} else if (showMessages[i > 0 ? i - 1 : i] == '<'
&& getColor(text) != null) {
continue;
} else if (text == '/') {
if (showMessages[i < size - 1 ? i + 1 : i] == '>') {
interceptMaxString = 1;
fontColor = old;
}
continue;
} else if (index > messageLength) {
index = 0;
left = tmp_dir;
offset++;
newLine = false;
} else if (text == '\\') {
continue;
}
String tmpText = String.valueOf(text);
tmp_font = ifont.charWidth(text);
if (Character.isLetter(text)) {
if (tmp_font < fontSize) {
font = fontSize;
} else {
font = tmp_font;
}
} else {
font = fontSize;
}
left += font;
if (font <= 10 && StringUtils.isSingle(text)) {
left += 12;
}
if (i != size - 1) {
ifont.drawString(g, tmpText, vector.x + left + leftOffset,
(offset * fontHeight) + vector.y + fontSize
+ topOffset, fontColor);
} else if (!newLine && !onComplete) {
iconX = vector.x + left + leftOffset;
iconY = (offset * fontHeight) + vector.y + fontSize
+ topOffset + ifont.getAscent();
if (iconX != 0 && iconY != 0) {
g.draw(creeseIcon, iconX, iconY);
}
}
index++;
}
if (onComplete) {
if (iconX != 0 && iconY != 0) {
g.draw(creeseIcon, iconX, iconY);
}
}
if (messageCount == next) {
onComplete = true;
}
}
}
public synchronized void draw(GLEx g, LColor old) {
if (!visible) {
return;
}
alpha = g.alpha();
if (alpha != 1f) {
g.setAlpha(1f);
}
drawMessage(g, old);
if (alpha != 1f) {
g.setAlpha(alpha);
}
}
public void setX(int x) {
vector.setX(x);
}
public void setY(int y) {
vector.setY(y);
}
public int getX() {
return vector.x();
}
public int getY() {
return vector.y();
}
public void complete() {
synchronized (showMessages) {
this.onComplete = true;
this.messageCount = messages.length();
this.next = messageCount;
this.showMessages = (messages + "_").toCharArray();
this.size = showMessages.length;
}
}
public boolean isComplete() {
if (isWait) {
if (onComplete) {
wait++;
}
return onComplete && wait > 100;
}
return onComplete;
}
public boolean next() {
synchronized (messageBuffer) {
if (!onComplete) {
if (messageCount == next) {
onComplete = true;
return false;
}
if (messageBuffer.length() > 0) {
messageBuffer.delete(messageBuffer.length() - 1,
messageBuffer.length());
}
this.messageBuffer.append(messages.charAt(messageCount));
this.messageBuffer.append('_');
this.showMessages = messageBuffer.toString().toCharArray();
this.size = showMessages.length;
this.messageCount++;
} else {
return false;
}
return true;
}
}
public LTexture getCreeseIcon() {
return creeseIcon;
}
public void setCreeseIcon(LTexture icon) {
if (this.creeseIcon != null) {
creeseIcon.close();
creeseIcon = null;
}
this.creeseIcon = icon;
if (icon == null) {
return;
}
}
public int getMessageLength() {
return messageLength;
}
public void setMessageLength(int messageLength) {
this.messageLength = messageLength;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getLeftOffset() {
return leftOffset;
}
public void setLeftOffset(int leftOffset) {
this.leftOffset = leftOffset;
}
public int getTopOffset() {
return topOffset;
}
public void setTopOffset(int topOffset) {
this.topOffset = topOffset;
}
public boolean isEnglish() {
return isEnglish;
}
public void setEnglish(boolean isEnglish) {
this.isEnglish = isEnglish;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public Mode getTextMode() {
return dirmode;
}
public void setTextMode(Mode mode) {
this.dirmode = mode;
}
public void left() {
setTextMode(Mode.LEFT);
}
public void right() {
setTextMode(Mode.RIGHT);
}
public void center() {
setTextMode(Mode.CENTER);
}
@Override
public Print setFont(IFont font) {
this.ifont = font;
return this;
}
@Override
public IFont getFont() {
return ifont;
}
public boolean isWait() {
return isWait;
}
public void setWait(boolean isWait) {
this.isWait = isWait;
}
@Override
public void close() {
if (!nativeFont) {
if (strings != null) {
strings.close();
strings = null;
}
}
}
}