package tv.dyndns.kishibe.qmaclone.client.chat;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import tv.dyndns.kishibe.qmaclone.client.Controller;
import tv.dyndns.kishibe.qmaclone.client.Service;
import tv.dyndns.kishibe.qmaclone.client.StatusUpdater;
import tv.dyndns.kishibe.qmaclone.client.UserData;
import tv.dyndns.kishibe.qmaclone.client.constant.Constant;
import tv.dyndns.kishibe.qmaclone.client.packet.PacketChatMessage;
import tv.dyndns.kishibe.qmaclone.client.packet.PacketChatMessages;
import tv.dyndns.kishibe.qmaclone.client.packet.RestrictionType;
public class PanelRealtime extends Composite implements KeyDownHandler {
private static final Logger logger = Logger.getLogger(PanelRealtime.class.getName());
public static final int TIMER_INTERVAL = 5000;
private static PanelRealtimeUiBinder uiBinder = GWT.create(PanelRealtimeUiBinder.class);
private static final List<String> NG_WORDS = ImmutableList.of("エロ動画", "おっぱい", "オッパイ", "調教", "御奉仕",
"なまぽ", "ナマポ", "ざーめん", "ザーメン", "おなにー", "オナニー", "ふぇら", "フェラ", "変態", "エロアニメ", "きんたま", "キンタマ",
"金玉", "ちんちん", "チンチン", "れいぷ", "レイプ", "くぱぁ", "クパァ", "せんずり", "精液", "ちんぽ", "チンポ", "ケツ", "けつ",
"Fuck", "ファック", "セックス", "オナホ", "シコシコ", "亀頭", "エロブログ", "えっち", "エッチ", "セクロス", "せくろす", "女体",
"ゲイ", "死ね", "殺す", "ウンコ", "うんこ", "ㄘんㄘん", "淫夢");
private static final int MIN_PLAY_COUNT_TO_SEND_MESSAGE = 10;
interface PanelRealtimeUiBinder extends UiBinder<Widget, PanelRealtime> {
}
@UiField
TextBox textBoxName;
@UiField
TextBox textBoxBody;
@UiField
Button buttonSend;
@UiField(provided = true)
CellListChatLog cellListChatLog;
private int nextArrayIndex = 1;
private final StatusUpdater<PacketChatMessages> updater = new StatusUpdater<PacketChatMessages>(
PacketChatMessages.class.getName(), TIMER_INTERVAL) {
@Override
protected void request(AsyncCallback<PacketChatMessages> callback) {
Service.Util.getInstance().receiveMessageFromChat(nextArrayIndex, callback);
}
@Override
protected PacketChatMessages parse(String json) {
return PacketChatMessages.Json.READER.read(json);
}
@Override
protected void onReceived(PacketChatMessages status) {
callbackRecieveMessage.onSuccess(status);
}
};
private boolean isRecieving = false;
private final LinkedList<PacketChatMessage> messages = new LinkedList<PacketChatMessage>();
private final ListDataProvider<PacketChatMessage> dataProvider = new ListDataProvider<PacketChatMessage>(
messages);
private final Set<Integer> restrictedUserCodes = Sets.newHashSet();
public PanelRealtime() {
cellListChatLog = new CellListChatLog(dataProvider);
cellListChatLog.setPageSize(Constant.CHAT_MAX_RESPONSES);
initWidget(uiBinder.createAndBindUi(this));
textBoxName.setText(UserData.get().getPlayerName());
textBoxBody.addKeyDownHandler(this);
Service.Util.getInstance().getRestrictedUserCodes(RestrictionType.CHAT,
callbackGetRestrictedUserCodes);
}
private final AsyncCallback<Set<Integer>> callbackGetRestrictedUserCodes = new AsyncCallback<Set<Integer>>() {
@Override
public void onSuccess(Set<Integer> result) {
restrictedUserCodes.addAll(result);
}
@Override
public void onFailure(Throwable caught) {
logger.log(Level.WARNING, "制限ユーザーの取得に失敗しました", caught);
}
};
private void sendMessage() {
if (!checkContents()) {
return;
}
if (textBoxName.getText().equals("未初期化です")) {
Controller.getInstance().log("「未初期化です」名義で発言することはできません。名前を変更して下さい。");
return;
}
setEnabled(false);
UserData record = UserData.get();
PacketChatMessage message = new PacketChatMessage();
message.name = textBoxName.getText();
message.body = textBoxBody.getText();
message.imageFileName = record.getImageFileName();
message.classLevel = record.getClassLevel();
message.userCode = record.getUserCode();
// 制限ユーザーからの発言をサーバーに送信しない
if (restrictedUserCodes.contains(message.userCode)) {
if (!messages.isEmpty()) {
message.resId = messages.getFirst().resId + 1;
}
addMessageToPanel(PacketChatMessages.fromMessage(message));
setEnabled(true);
return;
}
Service.Util.getInstance().sendMessageToChat(message, callbackSendMessage);
}
private final AsyncCallback<Void> callbackSendMessage = new AsyncCallback<Void>() {
public void onSuccess(Void result) {
textBoxBody.setText("");
setEnabled(true);
textBoxBody.setFocus(false);
textBoxBody.setFocus(true);
recieveMessage();
}
public void onFailure(Throwable caught) {
logger.log(Level.WARNING, "チャットメッセージの送信中にエラーが発生しました", caught);
}
};
private boolean checkContents() {
if (textBoxName.getText().trim().isEmpty()) {
return false;
}
if (textBoxBody.getText().trim().isEmpty()) {
return false;
}
return true;
}
private void recieveMessage() {
if (isRecieving) {
return;
}
isRecieving = true;
Service.Util.getInstance().receiveMessageFromChat(nextArrayIndex, callbackRecieveMessage);
}
private final AsyncCallback<PacketChatMessages> callbackRecieveMessage = new AsyncCallback<PacketChatMessages>() {
public void onSuccess(PacketChatMessages result) {
if (result != null && result.list != null && !result.list.isEmpty()) {
List<PacketChatMessage> incomingMessages = result.list;
nextArrayIndex = incomingMessages.get(incomingMessages.size() - 1).resId + 1;
addMessageToPanel(result);
}
isRecieving = false;
}
public void onFailure(Throwable caught) {
logger.log(Level.WARNING, "チャットメッセージの取得中にエラーが発生しました", caught);
}
};
private void addMessageToPanel(PacketChatMessages incomingMessages) {
boolean updated = false;
for (PacketChatMessage message : incomingMessages.list) {
if (message == null) {
logger.log(Level.WARNING, "nullのチャットデータが渡されました"
+ Arrays.deepToString(incomingMessages.list.toArray(new PacketChatMessage[0])));
continue;
}
PacketChatMessage first = messages.isEmpty() ? null : messages.getFirst();
if (!shouldShow(message, first)) {
continue;
}
messages.addFirst(message);
updated = true;
}
while (messages.size() > Constant.CHAT_MAX_RESPONSES) {
messages.removeLast();
}
if (updated) {
dataProvider.refresh();
}
}
@VisibleForTesting
boolean shouldShow(PacketChatMessage message, PacketChatMessage lastMessage) {
// チャット履歴が空なら表示する
if (lastMessage == null) {
return true;
}
// 最後の投稿と同じメッセージなら表示しない
// 自分の投稿が2回表示されるバグへの対処
if (Objects.equal(message.body, lastMessage.body) && message.userCode == lastMessage.userCode
&& Objects.equal(message.remoteAddress, lastMessage.remoteAddress)) {
return false;
}
// このユーザーから投稿されたものなら表示する
if (message.userCode == UserData.get().getUserCode()) {
return true;
}
// 制限ユーザーからの投稿は表示しない
if (message.restricted) {
return false;
}
// idが最後に表示されたものと同じか古ければ表示しない
if (message.resId <= lastMessage.resId) {
return false;
}
// NGワードが含まれているものは表示しない
for (String word : NG_WORDS) {
if (message.body.contains(word)) {
return false;
}
}
return true;
}
public void setEnabled(boolean enabled) {
textBoxName.setEnabled(enabled);
textBoxBody.setEnabled(enabled);
buttonSend.setEnabled(enabled);
}
protected void onLoad() {
super.onLoad();
recieveMessage();
updater.start();
}
@Override
protected void onUnload() {
updater.stop();
super.onUnload();
}
@Override
public void onKeyDown(KeyDownEvent event) {
if (event.getSource() == textBoxBody) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
sendMessage();
}
}
}
@UiHandler("buttonSend")
void onButtonSend(ClickEvent e) {
sendMessage();
}
}