/*
 * This software is licensed under the terms of the MIT License.
 * See COPYING for further information.
 * ---
 * Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
 * Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
 */

#include "replay.h"
#include "struct.h"
#include "state.h"

#include "stageinfo.h"
#include "../stage.h"
#include "global.h"

typedef struct ReplayContext {
	CallChain cc;
	Replay *rpy;
	ResourceGroup rg;
	int stage_idx;
	bool demo_mode;
} ReplayContext;

static void replay_do_cleanup(CallChainResult ccr);
static void replay_do_play(CallChainResult ccr);
static void replay_do_post_play(CallChainResult ccr);

void replay_play(Replay *rpy, int firstidx, bool demo_mode, CallChain next) {
	if(firstidx >= rpy->stages.num_elements || firstidx < 0) {
		log_error("No stage #%i in the replay", firstidx);
		run_call_chain(&next, NULL);
		return;
	}

	auto ctx = ALLOC(ReplayContext, {
		.cc = next,
		.rpy = rpy,
		.stage_idx = firstidx,
		.demo_mode = demo_mode,
	});

	res_group_init(&ctx->rg);
	replay_do_play(CALLCHAIN_RESULT(ctx, NULL));
}

static void replay_do_play(CallChainResult ccr) {
	ReplayContext *ctx = ccr.ctx;
	ReplayStage *rstg = NULL;
	StageInfo *stginfo = NULL;
	Replay *rpy = ctx->rpy;

	while(ctx->stage_idx < rpy->stages.num_elements) {
		rstg = dynarray_get_ptr(&rpy->stages, ctx->stage_idx++);
		stginfo = stageinfo_get_by_id(rstg->stage);

		if(!stginfo) {
			log_warn("Invalid stage %X in replay at %i skipped.", rstg->stage, ctx->stage_idx);
			continue;
		}

		break;
	};

	if(stginfo == NULL) {
		replay_do_cleanup(ccr);
	} else {
		assume(rstg != NULL);
		replay_state_init_play(&global.replay.input, rpy, rstg);
		global.replay.input.play.demo_mode = ctx->demo_mode;
		global.plr.mode = plrmode_find(rstg->plr_char, rstg->plr_shot);
		res_group_release(&ctx->rg);
		stage_enter(stginfo, &ctx->rg, CALLCHAIN(replay_do_post_play, ctx));
	}
}

static void replay_do_post_play(CallChainResult ccr) {
	ReplayContext *ctx = ccr.ctx;

	if(global.gameover == GAMEOVER_ABORT) {
		replay_do_cleanup(ccr);
		return;
	}

	if(global.gameover == GAMEOVER_RESTART) {
		--ctx->stage_idx;
	}

	global.gameover = 0;
	replay_do_play(ccr);
}

static void replay_do_cleanup(CallChainResult ccr) {
	ReplayContext *ctx = ccr.ctx;

	global.gameover = 0;
	replay_state_deinit(&global.replay.input);
	res_group_release(&ctx->rg);

	CallChain cc = ctx->cc;
	mem_free(ctx);
	run_call_chain(&cc, NULL);
}
