/*
 * sprite_msg.c: メッセージスプライトの処理
 *
 * Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
 *               1998-                           <masaki-c@is.aist-nara.ac.jp>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
*/
/* $Id: sprite_msg.c,v 1.8 2004/10/31 04:18:02 chikama Exp $ */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "portab.h"
#include "system.h"
#include "ags.h"
#include "nact.h"
#include "input.h"
#include "sact.h"
#include "sprite.h"
#include "drawtext.h"
#include "sactlog.h"

// メッセージキー待ちの時、表示するアニメーションに関する情報
struct markinfo {
	sprite_t *sp;
	cginfo_t *cg;
	int interval;
};

static bool is_messagesprite(int wNum);
static void replacestr_cb(void* data, void* userdata);
static char *replacestr(char *msg);
static void update_mark(sprite_t *sp, cginfo_t *cg);
static int  setupmark(int wNum1, int wNum2, struct markinfo *minfo);
static int  get_linelen(uint8_t *msg);
static uint8_t *get_char(uint8_t *msg, char *mbuf, char *rbuf, int bufmax);
static void append_to_log(char *msg);
static void sactlog_newline();
static void set_align(char *msg, sprite_t *sp, int wSize, int wAlign);


// 文字列置き換え用 (表示時にon-the-flyで変換して表示)
#define REPLACEBUFSIZE MSGBUFMAX+100
static char repbuf[2][REPLACEBUFSIZE];
static char *replacesrc;
static char *replacedst;

// 指定の番号のスプライトがメッセージスプライトかどうかをチェック
static bool is_messagesprite(int wNum) {
	// check sprite number is sane
	if (wNum >= (SPRITEMAX -1) || wNum <= 0) return false;
	
	// check sprite is set
	if (sact.sp[wNum] == NULL) return false;
	
	// check sprite is message sprite
	if (sact.sp[wNum]->type != SPRITE_MSG) return false;
	
	return true;
}

// 文字列の置き換え処理
static void replacestr_cb(void* data, void* userdata) {
	strexchange_t *ex = (strexchange_t *)data;
	char *start, *next, *out;
	
	if (ex == NULL) return;
	
	start = replacesrc;
	out   = replacedst;
	
	while (true) {
		next = strstr(start, ex->src);
		if (next == NULL) break;
		strncat(out, start, (size_t)(next - start));
		strncat(out, ex->dst, max(0, (REPLACEBUFSIZE - (int)strlen(out))));
		start = next + strlen(ex->src);
	}
	strncat(out, start, max(0, REPLACEBUFSIZE - (int)strlen(out)));
	
	replacedst = replacesrc;
	replacesrc = out;
	replacedst[0] = '\0';
}

// 文字列の置き換え
static char *replacestr(char *msg) {
	if (sact.strreplace == NULL) return msg;
	
	repbuf[0][0] = '\0';
	repbuf[1][0] = '\0';
	strncpy(repbuf[0], msg, REPLACEBUFSIZE);
	replacesrc = repbuf[0];
	replacedst = repbuf[1];
	slist_foreach(sact.strreplace, replacestr_cb, NULL);

	return (repbuf[0][0] == '\0') ? repbuf[1] : repbuf[0];
}

// アニメパターンの描画
static void update_mark(sprite_t *sp, cginfo_t *cg) {
	bool show = sp->show;
	cginfo_t *curcg = sp->curcg;

	sp->show = true;
	sp->curcg = cg;
	
	sp_updateme(sp);
	sp_update_clipped();

	sp->show = show;
	sp->curcg = curcg;
}

// アニメパターンの初期化
static int setupmark(int wNum1, int wNum2, struct markinfo *minfo) {
	sprite_t *sp1, *sp2;
	int i = 0;
	
	if (wNum1 == 0 || wNum2 == 0) return 0;
	
	sp1 = sact.sp[wNum1];
	sp2 = sact.sp[wNum2];
	if (sp1 == NULL || sp2 == NULL) return 0;
	
	if (sp1->cg1) {
		minfo[i].sp = sp1;
		minfo[i].cg = sp1->cg1;
		minfo[i].interval = sp1->u.anime.interval;
		i++;
	}
	if (sp1->cg2) {
		minfo[i].sp = sp1;
		minfo[i].cg = sp1->cg2;
		minfo[i].interval = sp1->u.anime.interval;
		i++;
	}
	if (sp1->cg3) {
		minfo[i].sp = sp1;
		minfo[i].cg = sp1->cg3;
		minfo[i].interval = sp1->u.anime.interval;
		i++;
	}
	if (sp2->cg1) {
		minfo[i].sp = sp2;
		minfo[i].cg = sp2->cg1;
		minfo[i].interval = sp2->u.anime.interval;
		i++;
	}
	if (sp2->cg2) {
		minfo[i].sp = sp2;
		minfo[i].cg = sp2->cg2;
		minfo[i].interval = sp2->u.anime.interval;
		i++;
	}
	if (sp2->cg3) {
		minfo[i].sp = sp2;
		minfo[i].cg = sp2->cg3;
		minfo[i].interval = sp2->u.anime.interval;
		i++;
	}
	return i;
}

/*
  メッセージをバッファに追加
    nact.cのメッセージ・コマンド解析ルーチンから呼ばれる
    
  @param msg: 追加する文字列
*/
void smsg_add(const char *msg) {
	int len;
	
	if (msg[0] == '\0') return;
	
	len = MSGBUFMAX - (int)strlen(sact.msgbuf);
	if (len < 0) {
		WARNING("buf shortage (%d)", len);
		return;
	}
	
	strncat(sact.msgbuf, msg, len);
	sact.msgbuf[MSGBUFMAX -1] = '\0';
}

/*
  改行
    改行はメッセージ情報内につっこみ、出力時に改行幅を取り出して改行

  @param wNum: 改行するスプライト番号
  @param size: 改行幅
*/
void smsg_newline(int wNum, int size) {
	uint8_t buf[3];
	
	if (!is_messagesprite(wNum)) return;

	buf[0] = '\n';
	buf[1] = size;
	buf[2] = '\0';
	smsg_add(buf);
}

/*
  (ルビつき)メッセージの出力

  @param wSpriteNumber: メッセージを表示するメッセージスプライト番号
  @param wSize: フォントの大きさ
  @param wColorR: メッセージの色(Red)
  @param wColorG: メッセージの色(Green)
  @param wColorB: メッセージの色(Blue)
  @param wFont: メッセージのフォント(0:ゴシック, 1:明朝)
  @param wSpeed: メッセージの表示速度 (msec)
  @param wLineSpace: 行間スペース
  @param wAlign: 行そろえ
  @param wRSize: ルビフォントサイズ
  @param wRFont: ルビフォント
  @param wRLineSpace: ルビと本文の文字間隔
  @param vLength: ???
*/
void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace, int wAlign, int wRSize, int wRFont, int wRLineSpace, int *wLength) {
	char *msg;
	sprite_t *sp;
	int len = 0; // 処理した文字数?
	bool needupdate = false;
	SDL_Rect uparea = {0,0,0,0};
	
	// wRSize == 0 -> ルビ無し(SACT.MessageOutputからの呼出)

	if (sact.msgbuf[0] == '\0') return;
	
	if (!is_messagesprite(wNum)) return;
	
	// MessageSkip中は文字送り速度を最大に
	if (sact.waitskiplv > 1) wSpeed = 0;
	
	// shortcut
	sp = sact.sp[wNum];
	
	// update開始Y座標 (X座標は0固定)
	uparea.y = sp->u.msg.dspcur.y;

	// 文字列置換
	msg = replacestr(sact.msgbuf);
	
	// 文字アラインメントの調整
	set_align(msg, sp, wSize, wAlign);
	
	while (*msg) {
		char mbuf[20], rbuf[20];
		int cw, delta, wcnt;
		
		wcnt = sys_get_ticks();
		
		mbuf[0] = rbuf[0] = '\0';
		msg = get_char(msg, mbuf, rbuf, sizeof(mbuf) -1); 
		
		if (mbuf[0] == '\n') {
			sp->u.msg.dspcur.x = 0;
			sp->u.msg.dspcur.y += (mbuf[1] + wLineSpace + wRSize + wRLineSpace);
			set_align(msg, sp, wSize, wAlign);
			
			sactlog_newline();
			
			continue;
		}
		
		if (rbuf[0] != '\0') {
			int mlen = strlen(mbuf) * wSize  /2;
			int rlen = strlen(rbuf) * wRSize /2;
			int adjx = max(0, (mlen - rlen) /2);
			dt_setfont(wRFont, wRSize);
			dt_drawtext_col(sp->u.msg.canvas,
					sp->u.msg.dspcur.x + adjx,
					sp->u.msg.dspcur.y,
					rbuf,
					wColorR, wColorG, wColorB);
		}
		dt_setfont(wFont, wSize);

		cw = dt_drawtext_col(sp->u.msg.canvas,
				     sp->u.msg.dspcur.x,
				     sp->u.msg.dspcur.y + wRSize + wRLineSpace,
				     mbuf,
				     wColorR, wColorG, wColorB);
		
		needupdate = true;
		
		append_to_log(mbuf);
		
		if (wSpeed > 0) {
			sp_updateme_part(sp,
					 sp->u.msg.dspcur.x,
					 sp->u.msg.dspcur.y,
					 cw,
					 wSize + wRSize + wRLineSpace);
			sp_update_clipped();
			needupdate = false;
			
			// keywait
			delta = sys_get_ticks() - wcnt;
			if (delta < wSpeed) {
				if (sys_keywait(wSpeed - delta, KEYWAIT_NONCANCELABLE)) {
					
					wSpeed = 0;
				}
			}
		}
		
		sp->u.msg.dspcur.x += cw;
	}
	
	sactlog_newline();
	
	// バッファリング中の文字のクリア
	sact.msgbuf[0] = '\0';
	
	// Waitなしの出力は最後にupdate
	if (needupdate) {
		uparea.w = sp->width;
		uparea.h = min(sp->height, uparea.y - sp->u.msg.dspcur.y + wLineSpace + wLineSpace + wRSize);
		sp_updateme_part(sp, uparea.x, uparea.y, uparea.w, uparea.h);
	}
	
	// ????
	if (wLength) {
		*wLength = len;
	}
}

/*
  メッセージ領域のクリア
  @param wNum: クリアするスプライト番号
 */
void smsg_clear(int wNum) {
	if (!is_messagesprite(wNum)) return;
	
	// 表示位置の初期化
	sprite_t *sp = sact.sp[wNum];
	sp->u.msg.dspcur.x = 0;
	sp->u.msg.dspcur.y = 0;
	
	sact.msgbuf[0]  = '\0';
	sact.msgbuf2[0] = '\0';

	// Clear the canvas
	SDL_FillRect(sp->u.msg.canvas, NULL, 0);

	sp_updateme(sp);
	
	if (sact.logging) {
		sact.log = list_append(sact.log, strdup("\n"));
	}
}

/*
  出力中の文字列があるかチェック
  @return: なし(1) , あり(0)
 */
bool smsg_is_empty() {
	return (sact.msgbuf[0] == '\0');
}

int smsg_peek(int nTopStringNum) {
	char *p = sact.msgbuf;
	int i;
	for (i = 0; *p; i++) {
		char *q = strchr(p, '\n');
		if (q)
			*q = '\0';
		svar_set(nTopStringNum + i, p);
		if (q) {
			*q = '\n';
			p = q + 2;
		} else {
			i++;
			break;
		}
	}
	return i;
}

/*
  メッセージキー入力待ち
   @param wNum1: スプライト番号1(アニメーションスプライト)
   @param wNum2: スプライト番号2(アニメーションスプライト)
   @param msglen: ?
 */
int smsg_keywait(int wNum1, int wNum2, int msglen) {
	struct markinfo minfo[6];
	int i = 0, j, maxstep;
	
	if (sact.waitskiplv > 0) {
		sys_getInputInfo();
		return 0;
	}
	
	// アニメパターンの初期化
	maxstep = setupmark(wNum1, wNum2, minfo);
	
	sact.waittype = KEYWAIT_MESSAGE;
	sact.waitkey = -1;
	
	while (sact.waitkey == -1 && !nact->is_quit) {
		int st = sys_get_ticks();
		int interval = 25;
		
		// アニメパターンがある場合、その更新
		// Zキーで隠されているとは表示しない
		// (アニメパターンが隠す対象でない時はどうしよう...)
		if (maxstep &&
		    !sact.zhiding &&
		    sact.waittype != KEYWAIT_BACKLOG) {
			j = i % maxstep;
			interval = minfo[j].interval;
			update_mark(minfo[j].sp, minfo[j].cg);
			i++;
		} 
		sys_keywait(interval - (sys_get_ticks() - st), KEYWAIT_NONCANCELABLE);
	}
	
	sact.waittype = KEYWAIT_NONE;
	
	return sact.waitkey;
}

/*
  スプライト再描画のコールバック
  @param sp: 再描画するスプライト
 */
void smsg_update(sprite_t *sp) {
	SDL_Rect sp_rect = {0, 0, sp->width, sp->height};
	int sx = 0;
	int sy = 0;
	int dx = sp->cur.x;
	int dy = sp->cur.y;
	int w = sp->width;
	int h = sp->height;
	if (!ags_clipCopyRect(&sp_rect, &sact.updaterect, &sx, &sy, &dx, &dy, &w, &h)) {
		return;
	}
	SDL_SetSurfaceBlendMode(sp->u.msg.canvas, SDL_BLENDMODE_BLEND);
	SDL_SetSurfaceAlphaMod(sp->u.msg.canvas, sp->blendrate);
	SDL_BlitSurface(sp->u.msg.canvas, &(SDL_Rect){sx, sy, w, h}, main_surface, &(SDL_Rect){dx, dy, w, h});
	
	SACT_DEBUG("do update no=%d, sx=%d, sy=%d, w=%d, h=%d, dx=%d, dy=%d",
		sp->no, sx, sy, w, h, dx, dy);
}

// 改行を含む文字列バッファ中から、改行までの文字列の長さを取り出す
static int get_linelen(uint8_t *msg) {
	int c = 0;
	
	while (*msg) {
		if (*msg == '\n') break;
		if (0 == strncmp("|RB|", msg, 4)) {
			msg += 4;
			while (*msg != '|') {
				msg++; c++;
			}
			msg++;
			while (*msg != '|') {
				msg++;
			}
			msg++;
		} else {
			msg++; c++;
		}
	}
	return c;
}

// 改行やルビを含む文字列バッファからそれらを取り出す
//   改行の場合        : 改行幅もいっしょに
//   ルビつき文字の場合: メッセージ本体と対応するルビ文字列
//   それ以外          : 全角|半角文字１文字
static uint8_t *get_char(uint8_t *msg, char *mbuf, char *rbuf, int bufmax) {
	if (msg[0] == '\n') {
		mbuf[0] = '\n';
		mbuf[1] = msg[1];
		mbuf[2] = '\0';
		return msg +2;
	}
	
	// ルビつき文字
	if (0 == strncmp("|RB|", msg, 4)) {
		int i;
		msg += 4;
		for (i = 0; *msg != '|' && i < bufmax; i++) {
			mbuf[i] = *msg++;
		}
		msg++; mbuf[i] = '\0';
		for (i = 0; *msg != '|' && i < bufmax; i++) {
			rbuf[i] = *msg++;
		}
		msg++; rbuf[i] = '\0';
	} else {
		uint8_t *p = advance_char(msg, nact->encoding);
		while (msg < p)
			*mbuf++ = *msg++;
		*mbuf = '\0';
	}
	return msg;
}

// バックログ用バッファに追加
static void append_to_log(char *msg) {
	if (sact.logging) {
		int len = MSGBUFMAX - (int)strlen(sact.msgbuf2);
		strncat(sact.msgbuf2, msg, len);
		sact.msgbuf2[MSGBUFMAX -1] = '\0';
	}
}

// バックログを保存するリストに追加
static void sactlog_newline() {
	if (sact.logging) {
		if (sact.msgbuf2[0] == '\0') return;
		sact.log = list_append(sact.log, toUTF8(sact.msgbuf2));
		sact.msgbuf2[0] = '\0';
	}
}

// 書き出し位置がx=0の時のみアラインメントの調整を行う
static void set_align(char *msg, sprite_t *sp, int wSize, int wAlign) {
	if (sp->u.msg.dspcur.x == 0) {
		int mlen = get_linelen(msg) * wSize/2;
		int adjx = 0;
		
		switch (wAlign) {
		case 1: // センタリング
			adjx = (sp->width - mlen) / 2;
			break;
		case 2: // 右寄せ
			adjx = (sp->width - mlen);
			break;
		}
		sp->u.msg.dspcur.x = max(0, adjx);
	}
}
