/*!
 * todo これ単体で750行を超えているので要分割
 * @brief 自動拾いエディタ画面でキーを押した時の挙動一式
 * @date 2020/04/26
 * @author Hourier
 */

#include "autopick/autopick-editor-command.h"
#include "autopick/autopick-commands-table.h"
#include "autopick/autopick-dirty-flags.h"
#include "autopick/autopick-drawer.h"
#include "autopick/autopick-editor-util.h"
#include "autopick/autopick-entry.h"
#include "autopick/autopick-finder.h"
#include "autopick/autopick-flags-table.h"
#include "autopick/autopick-inserter-killer.h"
#include "autopick/autopick-methods-table.h"
#include "autopick/autopick-reader-writer.h"
#include "core/asking-player.h"
#include "core/show-file.h"
#include "game-option/input-options.h"
#include "game-option/keymap-directory-getter.h"
#include "player/player-class.h"
#include "player/player-race.h"
#include "term/term-color-types.h"

/*
 * @brief
 * @param player_ptr プレーヤーへの参照ポインタ
 * @param tb 自動拾いの構文
 * @param com_id エディタ内で打ったコマンド
 * @return 
 * @details Execute a single editor command
 */
ape_quittance do_editor_command(player_type *player_ptr, text_body_type *tb, int com_id)
{
	switch (com_id)
	{
	case EC_QUIT:
	{
		if (tb->changed)
		{
			if (!get_check(_("全ての変更を破棄してから終了します。よろしいですか？ ",
				"Discard all changes and quit. Are you sure? "))) break;
		}

		return APE_QUIT_WITHOUT_SAVE;
	}
	case EC_SAVEQUIT:
		return APE_QUIT_AND_SAVE;
	case EC_REVERT:
	{
		if (!get_check(_("全ての変更を破棄して元の状態に戻します。よろしいですか？ ",
			"Discard all changes and revert to original file. Are you sure? "))) break;

		free_text_lines(tb->lines_list);
		tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
		tb->dirty_flags |= DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
		tb->cx = tb->cy = 0;
		tb->mark = 0;
		tb->changed = FALSE;
		break;
	}
	case EC_HELP:
	{
		(void)show_file(player_ptr, TRUE, _("jeditor.txt", "editor.txt"), NULL, 0, 0);
		tb->dirty_flags |= DIRTY_SCREEN;
		break;
	}
	case EC_RETURN:
	{
		if (tb->mark)
		{
			tb->mark = 0;
			tb->dirty_flags |= DIRTY_ALL;
		}

		insert_return_code(tb);
		tb->cy++;
		tb->cx = 0;
		tb->dirty_flags |= DIRTY_ALL;
		break;
	}
	case EC_LEFT:
	{
		if (0 < tb->cx)
		{
			int len;
#ifdef JP
			int i;
#endif
			tb->cx--;
			len = strlen(tb->lines_list[tb->cy]);
			if (len < tb->cx) tb->cx = len;
#ifdef JP
			for (i = 0; tb->lines_list[tb->cy][i]; i++)
			{
				if (iskanji(tb->lines_list[tb->cy][i]))
				{
					i++;
					if (i == tb->cx)
					{
						tb->cx--;
						break;
					}
				}
			}
#endif
		}
		else if (tb->cy > 0)
		{
			tb->cy--;
			tb->cx = strlen(tb->lines_list[tb->cy]);
		}

		break;
	}
	case EC_DOWN:
	{
		if (!tb->lines_list[tb->cy + 1])
		{
			if (!add_empty_line(tb)) break;
		}

		tb->cy++;
		break;
	}
	case EC_UP:
		if (tb->cy > 0) tb->cy--;
		break;
	case EC_RIGHT:
	{
#ifdef JP
		if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
#endif
		tb->cx++;
		int len = strlen(tb->lines_list[tb->cy]);
		if (len < tb->cx)
		{
			tb->cx = len;
			if (!tb->lines_list[tb->cy + 1])
			{
				if (!add_empty_line(tb)) break;
			}

			tb->cy++;
			tb->cx = 0;
		}

		break;
	}
	case EC_BOL:
		tb->cx = 0;
		break;
	case EC_EOL:
		tb->cx = strlen(tb->lines_list[tb->cy]);
		break;
	case EC_PGUP:
		while (0 < tb->cy && tb->upper <= tb->cy)
			tb->cy--;

		while (0 < tb->upper && tb->cy + 1 < tb->upper + tb->hgt)
			tb->upper--;

		break;
	case EC_PGDOWN:
		while (tb->cy < tb->upper + tb->hgt)
		{
			if (!tb->lines_list[tb->cy + 1])
			{
				if (!add_empty_line(tb)) break;
			}

			tb->cy++;
		}

		tb->upper = tb->cy;
		break;
	case EC_TOP:
		tb->cy = 0;
		break;
	case EC_BOTTOM:
		while (TRUE)
		{
			if (!tb->lines_list[tb->cy + 1])
			{
				if (!add_empty_line(tb)) break;
			}

			tb->cy++;
		}

		tb->cx = 0;
		break;
	case EC_CUT:
	{
		copy_text_to_yank(tb);
		if (tb->my == tb->cy)
		{
			int bx1 = MIN(tb->mx, tb->cx);
			int bx2 = MAX(tb->mx, tb->cx);
			int len = strlen(tb->lines_list[tb->cy]);
			if (bx2 > len) bx2 = len;

			kill_line_segment(tb, tb->cy, bx1, bx2, TRUE);
			tb->cx = bx1;
		}
		else
		{
			int by1 = MIN(tb->my, tb->cy);
			int by2 = MAX(tb->my, tb->cy);

			for (int y = by2; y >= by1; y--)
			{
				int len = strlen(tb->lines_list[y]);

				kill_line_segment(tb, y, 0, len, TRUE);
			}

			tb->cy = by1;
			tb->cx = 0;
		}

		tb->mark = 0;
		tb->dirty_flags |= DIRTY_ALL;
		tb->changed = TRUE;
		break;
	}
	case EC_COPY:
	{
		copy_text_to_yank(tb);

		/*
		 * Move cursor position to the end of the selection
		 *
		 * Pressing ^C ^V correctly duplicates the selection.
		 */
		if (tb->my != tb->cy)
		{
			tb->cy = MAX(tb->cy, tb->my);
			if (!tb->lines_list[tb->cy + 1])
			{
				if (!add_empty_line(tb)) break;
			}

			tb->cy++;
			break;
		}

		tb->cx = MAX(tb->cx, tb->mx);
		if (!tb->lines_list[tb->cy][tb->cx])
		{
			if (!tb->lines_list[tb->cy + 1])
			{
				if (!add_empty_line(tb)) break;
			}

			tb->cy++;
			tb->cx = 0;
		}

		break;
	}
	case EC_PASTE:
	{
		chain_str_type *chain = tb->yank;
		int len = strlen(tb->lines_list[tb->cy]);
		if (!chain) break;
		if (tb->cx > len) tb->cx = len;

		if (tb->mark)
		{
			tb->mark = 0;
			tb->dirty_flags |= DIRTY_ALL;
		}

		while (chain)
		{
			concptr yank_str = chain->s;
			char buf[MAX_LINELEN];
			int i;
			char rest[MAX_LINELEN], *rest_ptr = rest;
			for (i = 0; i < tb->cx; i++)
				buf[i] = tb->lines_list[tb->cy][i];

			strcpy(rest, &(tb->lines_list[tb->cy][i]));
			while (*yank_str && i < MAX_LINELEN - 1)
			{
				buf[i++] = *yank_str++;
			}

			buf[i] = '\0';
			chain = chain->next;
			if (chain || tb->yank_eol)
			{
				insert_return_code(tb);
				string_free(tb->lines_list[tb->cy]);
				tb->lines_list[tb->cy] = string_make(buf);
				tb->cx = 0;
				tb->cy++;

				continue;
			}

			tb->cx = strlen(buf);
			while (*rest_ptr && i < MAX_LINELEN - 1)
			{
				buf[i++] = *rest_ptr++;
			}

			buf[i] = '\0';
			string_free(tb->lines_list[tb->cy]);
			tb->lines_list[tb->cy] = string_make(buf);
			break;
		}

		tb->dirty_flags |= DIRTY_ALL;
		tb->dirty_flags |= DIRTY_EXPRESSION;
		tb->changed = TRUE;
		break;
	}
	case EC_BLOCK:
	{
		if (tb->mark)
		{
			tb->mark = 0;
			tb->dirty_flags |= DIRTY_ALL;
			break;
		}

		tb->mark = MARK_MARK;
		if (com_id == tb->old_com_id)
		{
			int tmp = tb->cy;
			tb->cy = tb->my;
			tb->my = tmp;
			tmp = tb->cx;
			tb->cx = tb->mx;
			tb->mx = tmp;
			tb->dirty_flags |= DIRTY_ALL;
			break;
		}

		int len = strlen(tb->lines_list[tb->cy]);

		tb->my = tb->cy;
		tb->mx = tb->cx;
		if (tb->cx > len) tb->mx = len;
		break;
	}
	case EC_KILL_LINE:
	{
		int len = strlen(tb->lines_list[tb->cy]);
		if (tb->cx > len) tb->cx = len;

		if (tb->mark)
		{
			tb->mark = 0;
			tb->dirty_flags |= DIRTY_ALL;
		}

		if (tb->old_com_id != com_id)
		{
			kill_yank_chain(tb);
			tb->yank = NULL;
		}

		if (tb->cx < len)
		{
			add_str_to_yank(tb, &(tb->lines_list[tb->cy][tb->cx]));
			kill_line_segment(tb, tb->cy, tb->cx, len, FALSE);
			tb->dirty_line = tb->cy;
			break;
		}

		if (tb->yank_eol) add_str_to_yank(tb, "");

		tb->yank_eol = TRUE;
		do_editor_command(player_ptr, tb, EC_DELETE_CHAR);
		break;
	}
	case EC_DELETE_CHAR:
	{
		if (tb->mark)
		{
			tb->mark = 0;
			tb->dirty_flags |= DIRTY_ALL;
		}

#ifdef JP
		if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
#endif
		tb->cx++;
		int len = strlen(tb->lines_list[tb->cy]);
		if (len >= tb->cx)
		{
			do_editor_command(player_ptr, tb, EC_BACKSPACE);
			break;
		}

		if (tb->lines_list[tb->cy + 1])
		{
			tb->cy++;
			tb->cx = 0;
		}
		else
		{
			tb->cx = len;
			break;
		}

		do_editor_command(player_ptr, tb, EC_BACKSPACE);
		break;
	}
	case EC_BACKSPACE:
	{
		int len, i, j, k;
		char buf[MAX_LINELEN];
		if (tb->mark)
		{
			tb->mark = 0;
			tb->dirty_flags |= DIRTY_ALL;
		}

		len = strlen(tb->lines_list[tb->cy]);
		if (len < tb->cx) tb->cx = len;

		if (tb->cx == 0)
		{
			if (tb->cy == 0) break;
			tb->cx = strlen(tb->lines_list[tb->cy - 1]);
			strcpy(buf, tb->lines_list[tb->cy - 1]);
			strcat(buf, tb->lines_list[tb->cy]);
			string_free(tb->lines_list[tb->cy - 1]);
			string_free(tb->lines_list[tb->cy]);
			tb->lines_list[tb->cy - 1] = string_make(buf);

			for (i = tb->cy; tb->lines_list[i + 1]; i++)
				tb->lines_list[i] = tb->lines_list[i + 1];

			tb->lines_list[i] = NULL;
			tb->cy--;
			tb->dirty_flags |= DIRTY_ALL;
			tb->dirty_flags |= DIRTY_EXPRESSION;
			tb->changed = TRUE;
			break;
		}

		for (i = j = k = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
		{
			k = j;
#ifdef JP
			if (iskanji(tb->lines_list[tb->cy][i]))
				buf[j++] = tb->lines_list[tb->cy][i++];
#endif
			buf[j++] = tb->lines_list[tb->cy][i];
		}

		while (j > k)
		{
			tb->cx--;
			j--;
		}

		for (; tb->lines_list[tb->cy][i]; i++)
		{
			buf[j++] = tb->lines_list[tb->cy][i];
		}

		buf[j] = '\0';
		string_free(tb->lines_list[tb->cy]);
		tb->lines_list[tb->cy] = string_make(buf);
		tb->dirty_line = tb->cy;
		check_expression_line(tb, tb->cy);
		tb->changed = TRUE;
		break;
	}
	case EC_SEARCH_STR:
	{
		byte search_dir;
		tb->dirty_flags |= DIRTY_SCREEN;
		search_dir = get_string_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str);

		if (!search_dir) break;

		if (search_dir == 1) do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
		else do_editor_command(player_ptr, tb, EC_SEARCH_BACK);

		break;
	}
	case EC_SEARCH_FORW:
		if (tb->search_o_ptr)
		{
			search_for_object(player_ptr, tb, tb->search_o_ptr, TRUE);
			break;
		}

		if (tb->search_str && tb->search_str[0])
		{
			search_for_string(tb, tb->search_str, TRUE);
			break;
		}

		tb->dirty_flags |= DIRTY_NO_SEARCH;
		break;

	case EC_SEARCH_BACK:
	{
		if (tb->search_o_ptr)
		{
			search_for_object(player_ptr, tb, tb->search_o_ptr, FALSE);
			break;
		}

		if (tb->search_str && tb->search_str[0])
		{
			search_for_string(tb, tb->search_str, FALSE);
			break;
		}

		tb->dirty_flags |= DIRTY_NO_SEARCH;
		break;
	}
	case EC_SEARCH_OBJ:
	{
		tb->dirty_flags |= DIRTY_SCREEN;

		if (!get_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str)) break;

		do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
		break;
	}
	case EC_SEARCH_DESTROYED:
	{
		if (!get_destroyed_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str))
		{
			tb->dirty_flags |= DIRTY_NO_SEARCH;
			break;
		}

		do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
		break;
	}
	case EC_INSERT_OBJECT:
	{
		autopick_type an_entry, *entry = &an_entry;
		if (!entry_from_choosed_object(player_ptr, entry))
		{
			tb->dirty_flags |= DIRTY_SCREEN;
			break;
		}

		tb->cx = 0;
		insert_return_code(tb);
		string_free(tb->lines_list[tb->cy]);
		tb->lines_list[tb->cy] = autopick_line_from_entry_kill(entry);
		tb->dirty_flags |= DIRTY_SCREEN;
		break;
	}
	case EC_INSERT_DESTROYED:
	{
		if (!tb->last_destroyed) break;

		tb->cx = 0;
		insert_return_code(tb);
		string_free(tb->lines_list[tb->cy]);
		tb->lines_list[tb->cy] = string_make(tb->last_destroyed);
		tb->dirty_flags |= DIRTY_ALL;
		tb->changed = TRUE;
		break;
	}
	case EC_INSERT_BLOCK:
	{
		char expression[80];
		sprintf(expression, "?:[AND [EQU $RACE %s] [EQU $CLASS %s] [GEQ $LEVEL %02d]]",
#ifdef JP
			rp_ptr->E_title, cp_ptr->E_title,
#else
			rp_ptr->title, cp_ptr->title,
#endif
			player_ptr->lev);
		tb->cx = 0;
		insert_return_code(tb);
		string_free(tb->lines_list[tb->cy]);
		tb->lines_list[tb->cy] = string_make(expression);
		tb->cy++;
		insert_return_code(tb);
		string_free(tb->lines_list[tb->cy]);
		tb->lines_list[tb->cy] = string_make("?:1");
		tb->dirty_flags |= DIRTY_ALL;
		tb->changed = TRUE;
		break;
	}
	case EC_INSERT_MACRO:
	{
		draw_text_editor(player_ptr, tb);
		Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
		Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW, _("P:<トリガーキー>: ", "P:<Trigger key>: "));
		if (!insert_macro_line(tb)) break;

		tb->cx = 2;
		tb->dirty_flags |= DIRTY_ALL;
		tb->changed = TRUE;
		break;
	}
	case EC_INSERT_KEYMAP:
	{
		draw_text_editor(player_ptr, tb);
		Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
		Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW,
			format(_("C:%d:<コマンドキー>: ", "C:%d:<Keypress>: "), (rogue_like_commands ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG)));

		if (!insert_keymap_line(tb)) break;

		tb->cx = 2;
		tb->dirty_flags |= DIRTY_ALL;
		tb->changed = TRUE;
		break;
	}
	case EC_CL_AUTOPICK:
		toggle_command_letter(tb, DO_AUTOPICK);
		break;
	case EC_CL_DESTROY:
		toggle_command_letter(tb, DO_AUTODESTROY);
		break;
	case EC_CL_LEAVE:
		toggle_command_letter(tb, DONT_AUTOPICK);
		break;
	case EC_CL_QUERY:
		toggle_command_letter(tb, DO_QUERY_AUTOPICK);
		break;
	case EC_CL_NO_DISP:
		toggle_command_letter(tb, DO_DISPLAY);
		break;
	case EC_IK_UNAWARE:
		toggle_keyword(tb, FLG_UNAWARE);
		break;
	case EC_IK_UNIDENTIFIED:
		toggle_keyword(tb, FLG_UNIDENTIFIED);
		break;
	case EC_IK_IDENTIFIED:
		toggle_keyword(tb, FLG_IDENTIFIED);
		break;
	case EC_IK_STAR_IDENTIFIED:
		toggle_keyword(tb, FLG_STAR_IDENTIFIED);
		break;
	case EC_KK_WEAPONS:
		toggle_keyword(tb, FLG_WEAPONS);
		break;
	case EC_KK_FAVORITE_WEAPONS:
		toggle_keyword(tb, FLG_FAVORITE_WEAPONS);
		break;
	case EC_KK_ARMORS:
		toggle_keyword(tb, FLG_ARMORS);
		break;
	case EC_KK_MISSILES:
		toggle_keyword(tb, FLG_MISSILES);
		break;
	case EC_KK_DEVICES:
		toggle_keyword(tb, FLG_DEVICES);
		break;
	case EC_KK_LIGHTS:
		toggle_keyword(tb, FLG_LIGHTS);
		break;
	case EC_KK_JUNKS:
		toggle_keyword(tb, FLG_JUNKS);
		break;
	case EC_KK_CORPSES:
		toggle_keyword(tb, FLG_CORPSES);
		break;
	case EC_KK_SPELLBOOKS:
		toggle_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_KK_SHIELDS:
		toggle_keyword(tb, FLG_SHIELDS);
		break;
	case EC_KK_BOWS:
		toggle_keyword(tb, FLG_BOWS);
		break;
	case EC_KK_RINGS:
		toggle_keyword(tb, FLG_RINGS);
		break;
	case EC_KK_AMULETS:
		toggle_keyword(tb, FLG_AMULETS);
		break;
	case EC_KK_SUITS:
		toggle_keyword(tb, FLG_SUITS);
		break;
	case EC_KK_CLOAKS:
		toggle_keyword(tb, FLG_CLOAKS);
		break;
	case EC_KK_HELMS:
		toggle_keyword(tb, FLG_HELMS);
		break;
	case EC_KK_GLOVES:
		toggle_keyword(tb, FLG_GLOVES);
		break;
	case EC_KK_BOOTS:
		toggle_keyword(tb, FLG_BOOTS);
		break;
	case EC_OK_COLLECTING:
		toggle_keyword(tb, FLG_COLLECTING);
		break;
	case EC_OK_BOOSTED:
		toggle_keyword(tb, FLG_BOOSTED);
		break;
	case EC_OK_MORE_DICE:
		toggle_keyword(tb, FLG_MORE_DICE);
		break;
	case EC_OK_MORE_BONUS:
		toggle_keyword(tb, FLG_MORE_BONUS);
		break;
	case EC_OK_WORTHLESS:
		toggle_keyword(tb, FLG_WORTHLESS);
		break;
	case EC_OK_ARTIFACT:
		toggle_keyword(tb, FLG_ARTIFACT);
		break;
	case EC_OK_EGO:
		toggle_keyword(tb, FLG_EGO);
		break;
	case EC_OK_GOOD:
		toggle_keyword(tb, FLG_GOOD);
		break;
	case EC_OK_NAMELESS:
		toggle_keyword(tb, FLG_NAMELESS);
		break;
	case EC_OK_AVERAGE:
		toggle_keyword(tb, FLG_AVERAGE);
		break;
	case EC_OK_RARE:
		toggle_keyword(tb, FLG_RARE);
		break;
	case EC_OK_COMMON:
		toggle_keyword(tb, FLG_COMMON);
		break;
	case EC_OK_WANTED:
		toggle_keyword(tb, FLG_WANTED);
		break;
	case EC_OK_UNIQUE:
		toggle_keyword(tb, FLG_UNIQUE);
		break;
	case EC_OK_HUMAN:
		toggle_keyword(tb, FLG_HUMAN);
		break;
	case EC_OK_UNREADABLE:
		toggle_keyword(tb, FLG_UNREADABLE);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_OK_REALM1:
		toggle_keyword(tb, FLG_REALM1);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_OK_REALM2:
		toggle_keyword(tb, FLG_REALM2);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_OK_FIRST:
		toggle_keyword(tb, FLG_FIRST);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_OK_SECOND:
		toggle_keyword(tb, FLG_SECOND);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_OK_THIRD:
		toggle_keyword(tb, FLG_THIRD);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	case EC_OK_FOURTH:
		toggle_keyword(tb, FLG_FOURTH);
		add_keyword(tb, FLG_SPELLBOOKS);
		break;
	}

	tb->old_com_id = com_id;
	return APE_QUIT;
}
