/*
 * sprite_sel.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_sel.c,v 1.1 2003/04/22 16:29:52 chikama Exp $ */

#include "config.h"

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

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

// 選択された要素(1~) キャンセルの場合は0、初期状態は -1
static int selected_item;
static int selected_item_cur;

// 前のカーソルの状態
static bool oldstate; // spriteの中か外か
static int oldindex; // 何番目の要素か(0~)


static bool sp_is_insprite2(sprite_t *sp, int x, int y, int margin);
static void cb_select_move(agsevent_t *e);
static void cb_select_release(agsevent_t *e);
static void setup_selwindow();
static void remove_selwindow();
static int sel_main();




/**
 *  内側にマージンを含む sprite内領域チェック
 *  マージン内はsprite内部とは判断しない
 */
static bool sp_is_insprite2(sprite_t *sp, int x, int y, int margin) {
	SDL_Point p = {x, y};
	SDL_Rect r = {
		sp->cur.x + margin,
		sp->cur.y + margin,
		sp->curcg->sf->w - 2 * margin,
		sp->curcg->sf->h - 2 * margin
	};
	return SDL_PointInRect(&p, &r);
}

// マウスが移動したときの callback
static void cb_select_move(agsevent_t *e) {
	int x = e->mousex, y = e->mousey;
	sprite_t *sp = sact.sp[sact.sel.spno];
	bool newstate;
	int newindex;
	
	// sprite内か？
	newstate = sp_is_insprite2(sp, x, y, sact.sel.frame_dot);
	newindex = (y - (sp->cur.y + sact.sel.frame_dot)) / (sact.sel.font_size + sact.sel.linespace);
	
	if (newstate == oldstate) {
		if (!newstate || newindex == oldindex) {
			// 前と状態が同じでかつ、新しい状態がspriteの外か、
			// indexが変わらない場合はなにもしない。
			return;
		}
	}
	
	if (newstate) {
		// spriteの内部
		// fprintf(stderr, "in region %d\n", newindex);
		//update_selwindow(newindex + 1);
		selected_item_cur = newindex + 1;
	} else {
		// spriteの外部
		//update_selwindow(0);
		selected_item_cur = 0;
	}
	
	oldstate = newstate;
	oldindex = newindex;
	
	// 再描画
	sp_updateme(sp);
	sp_update_clipped();
}

// ボタンがリリースされたときの callback
static void cb_select_release(agsevent_t *e) {
	int x = e->mousex, y = e->mousey;
	sprite_t *sp = sact.sp[sact.sel.spno];
	bool st;
	int iy;
	
	switch (e->code) {
	case AGSEVENT_BUTTON_LEFT:
		st = sp_is_insprite2(sp, x, y, sact.sel.frame_dot);
		iy = (y - (sp->cur.y + sact.sel.frame_dot)) / (sact.sel.font_size + sact.sel.linespace);

		// カーソルが sprite の外の場合は無視
		if (!st) {
			return;
		}
		
		// 選択要素が空の場合も無視
		if (sact.sel.elem[iy + 1] == NULL) return;
		
		selected_item = iy + 1;
		break;
		
	case AGSEVENT_BUTTON_RIGHT:
		// キャンセル
		selected_item = 0;
		break;
	}
}

static void draw_box(SDL_Surface *dst, int x, int y, int w, int h) {
	SDL_FillRect(dst, &(SDL_Rect){x, y, w, h}, SDL_MapRGB(dst->format, 0, 0, 0));
	SDL_Rect border_rects[4] = {
		{x, y, w, 1},
		{x, y + h - 1, w, 1},
		{x, y, 1, h},
		{x + w - 1, y, 1, h}
	};
	SDL_FillRects(dst, border_rects, 4, SDL_MapRGB(dst->format, 255, 255, 255));
}

// 選択ウィンドを更新するときの callback
static void update_selwindow(sprite_t *sp) {
	int selno = selected_item_cur;
	int x0, y0;

	x0 = sp->cur.x;
	y0 = sp->cur.y;
	// 背景 CG
	sp_draw(sp);
	
	// 選択されている要素
	if (selno && sact.sel.elem[selno] != NULL) {
		int w = sact.sel.charcanvas->w - 2 * sact.sel.frame_dot;
		int h = sact.sel.font_size + sact.sel.linespace;
		int x = x0 + sact.sel.frame_dot;
		int y = y0 + sact.sel.frame_dot + (selno -1) * h;
		draw_box(main_surface, x, y, w, h);
	}
	
	// 選択肢文字列
	SDL_BlitSurface(sact.sel.charcanvas, NULL, main_surface,
		&(SDL_Rect){x0, y0, sact.sel.charcanvas->w, sact.sel.charcanvas->h});
}

// 選択ウィンドの準備
static void setup_selwindow() {
	sprite_t *sp = sact.sp[sact.sel.spno];
	int i;
	
	// 選択肢文字用 canvas
	sact.sel.charcanvas = SDL_CreateRGBSurfaceWithFormat(0, sp->cg1->sf->w, sp->cg1->sf->h, 32, SDL_PIXELFORMAT_ARGB8888);
	SDL_SetSurfaceBlendMode(sact.sel.charcanvas, SDL_BLENDMODE_BLEND);
	
	dt_setfont(sact.sel.font_type, sact.sel.font_size);
	
	// 指定のスプライトに文字(選択肢を書く)
	for (i = 1; i < SEL_ELEMENT_MAX; i++) {
		int x, y;
		if (sact.sel.elem[i] == NULL) continue;
		// 文字の場所計算
		x = 0; // 行そろえは無し
		y = (i - 1) * (sact.sel.font_size + sact.sel.linespace);
		dt_drawtext_col(sact.sel.charcanvas,
			    x + sact.sel.frame_dot, y + sact.sel.frame_dot,
			    sact.sel.elem[i], 255, 255, 255);
	}
	
	// デフォルトで選択される選択肢がある場合、そこへカーソルを移動
	if (sact.sel.movecursor) {
		int x = sp->cur.x + sact.sel.frame_dot + 2;
		int y = sp->cur.y + sact.sel.frame_dot + 2 +
			(sact.sel.font_size + sact.sel.linespace) * (sact.sel.movecursor - 1);
		ags_setCursorLocation(x, y, true, true);
		selected_item = (sact.sel.movecursor -1);
		oldstate = true;
		oldindex = selected_item -1;
	}

	// その他初期化
	selected_item_cur = 0;

	// スプライト再描画 callback の登録
	sp->update = update_selwindow;
}

// 選択ウィンドの削除
static void remove_selwindow() {
	sprite_t *sp = sact.sp[sact.sel.spno];

	// スプライト再描画 callback を元にもどす
	sp->update = sp_draw;

	// スプライトを再描画して(おそらく消す)
	sp_updateme(sp);
	sp_update_clipped();
	
	// 作業用 surface の削除
	if (sact.sel.charcanvas)
		SDL_FreeSurface(sact.sel.charcanvas);
	sact.sel.charcanvas = NULL;
}


// 選択メインループ
static int sel_main() {
	sact.waittype = KEYWAIT_SELECT;
	sact.waitkey = -1;

	selected_item = -1;
	
	while(selected_item == -1) {
		sys_keywait(25, KEYWAIT_CANCELABLE);
		if (nact->is_quit)
			selected_item = 0;
	}
	
	sact.waittype = KEYWAIT_NONE;
	
	return selected_item;
}

/*
 選択肢関連の初期化
*/
void ssel_init() {
	// callbackの設定
	sact.sel.cbmove = cb_select_move;
	sact.sel.cbrelease = cb_select_release;

	// デフォルトフォント
	sact.sel.font_type = FONT_GOTHIC;
}

void ssel_reset(void) {
	ssel_clear();
	if (sact.sel.charcanvas) {
		SDL_FreeSurface(sact.sel.charcanvas);
		sact.sel.charcanvas = NULL;
	}
}

/*
  内部の選択肢情報をクリア
*/
void ssel_clear() {
	int i;
	
	for (i = 0; i < SEL_ELEMENT_MAX; i++) {
		free(sact.sel.elem[i]);
		sact.sel.elem[i] = NULL;
	}
}

/*
  登録文字列を内部選択肢情報に追加
  @param nString: 登録文字列変数
  @param wI     : 登録位置
*/
void ssel_add(int nString, int wI) {
	if ((wI >= SEL_ELEMENT_MAX -1) || (wI <= 0)) {
		//error
		return;
	}
	if (sact.sel.elem[wI] != NULL) {
		free(sact.sel.elem[wI]);
	}
	
	sact.sel.elem[wI] = strdup(svar_get(nString));
}

/*
  選択ウィンドを開いて選択

  @param wNum: 枠,背景とするスプライト番号
  @param wChoiceSize: 選択肢文字サイズ
  @param wMenuOutSpc: 枠スプライトの外側からのピクセル数
  @param wChoiceLineSpace: 選択肢の行間
  @param wChoiceAutoMoveCursor: オープン時に自動的に移動する選択肢の番号
  @param nAlign: 行そろえ (0:左, 1:中央, 2: 右)
*/
int ssel_select(int wNum, int wChoiceSize, int wMenuOutSpc, int wChoiceLineSpace, int wChoiceAutoMoveCursor, int nAlign) {
	int ret = 0;
	bool saveflag;
	
	// check sprite number is sane
	if (wNum >= (SPRITEMAX-1) || wNum <= 0) return ret;
	
	// check sprite is set
	if (sact.sp[wNum] == NULL) return ret;
	
	// must be normal sprite
	if (sact.sp[wNum]->type != SPRITE_NORMAL) return ret;
	
	sact.sel.spno = wNum;
	sact.sel.font_size = wChoiceSize;
	sact.sel.frame_dot = wMenuOutSpc;
	sact.sel.linespace = wChoiceLineSpace;
	sact.sel.movecursor = wChoiceAutoMoveCursor;
	sact.sel.align      = nAlign;

	// 古い sprite の表示フラグを保存
	saveflag = sact.sp[wNum]->show;
	sact.sp[wNum]->show = true;
	setup_selwindow();
	
	ret = sel_main();

	// 表示フラグを元に戻す
	sact.sp[wNum]->show = saveflag;
	
	remove_selwindow();
	
	return ret;
}
