#include <history.hpp>
#include <context.hpp>
#include <app/Scene.hpp>
#include <engine/Cable.hpp>
#include <engine/Engine.hpp>


namespace rack {
namespace history {


ComplexAction::~ComplexAction() {
	for (Action* action : actions) {
		delete action;
	}
}

void ComplexAction::undo() {
	for (auto it = actions.rbegin(); it != actions.rend(); it++) {
		Action* action = *it;
		action->undo();
	}
}

void ComplexAction::redo() {
	for (Action* action : actions) {
		action->redo();
	}
}

void ComplexAction::push(Action* action) {
	actions.push_back(action);
}

bool ComplexAction::isEmpty() {
	return actions.empty();
}


ModuleAdd::~ModuleAdd() {
	json_decref(moduleJ);
}

void ModuleAdd::setModule(app::ModuleWidget* mw) {
	assert(mw);
	model = mw->getModel();
	assert(mw->getModule());
	moduleId = mw->getModule()->id;
	pos = mw->box.pos;
	// ModuleAdd doesn't *really* need the state to be serialized, although ModuleRemove certainly does.
	// However, creating a module may give it a nondeterministic initial state for whatever reason, so serialize anyway.
	moduleJ = APP->engine->moduleToJson(mw->getModule());
}

void ModuleAdd::undo() {
	app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId);
	if (!mw)
		return;
	APP->scene->rack->removeModule(mw);
	delete mw;
}

void ModuleAdd::redo() {
	INFO("Creating module %s", model->getFullName().c_str());
	engine::Module* module = model->createModule();
	module->id = moduleId;
	try {
		module->fromJson(moduleJ);
	}
	catch (Exception& e) {
		WARN("%s", e.what());
	}
	APP->engine->addModule(module);

	INFO("Creating module widget %s", model->getFullName().c_str());
	app::ModuleWidget* mw = model->createModuleWidget(module);
	mw->box.pos = pos;
	APP->scene->rack->addModule(mw);
}


void ModuleMove::undo() {
	app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId);
	if (!mw)
		return;
	mw->box.pos = oldPos;
	APP->scene->rack->updateExpanders();
}

void ModuleMove::redo() {
	app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId);
	if (!mw)
		return;
	mw->box.pos = newPos;
	APP->scene->rack->updateExpanders();
}


void ModuleBypass::undo() {
	engine::Module* module = APP->engine->getModule(moduleId);
	if (!module)
		return;
	APP->engine->bypassModule(module, !bypassed);
}

void ModuleBypass::redo() {
	engine::Module* module = APP->engine->getModule(moduleId);
	if (!module)
		return;
	APP->engine->bypassModule(module, bypassed);
}


ModuleChange::~ModuleChange() {
	json_decref(oldModuleJ);
	json_decref(newModuleJ);
}

void ModuleChange::undo() {
	engine::Module* module = APP->engine->getModule(moduleId);
	if (!module)
		return;
	APP->engine->moduleFromJson(module, oldModuleJ);
}

void ModuleChange::redo() {
	engine::Module* module = APP->engine->getModule(moduleId);
	if (!module)
		return;
	APP->engine->moduleFromJson(module, newModuleJ);
}


void ParamChange::undo() {
	engine::Module* module = APP->engine->getModule(moduleId);
	if (!module)
		return;
	APP->engine->setParamValue(module, paramId, oldValue);
}

void ParamChange::redo() {
	engine::Module* module = APP->engine->getModule(moduleId);
	if (!module)
		return;
	APP->engine->setParamValue(module, paramId, newValue);
}


void CableAdd::setCable(app::CableWidget* cw) {
	assert(cw);
	assert(cw->cable);
	assert(cw->cable->id >= 0);
	cableId = cw->cable->id;
	assert(cw->cable->outputModule);
	outputModuleId = cw->cable->outputModule->id;
	outputId = cw->cable->outputId;
	assert(cw->cable->inputModule);
	inputModuleId = cw->cable->inputModule->id;
	inputId = cw->cable->inputId;
	color = cw->color;
}

bool CableAdd::isCable(app::CableWidget* cw) const {
	if (!(cw && cw->cable && cw->cable->id >= 0 && cw->cable->outputModule && cw->cable->inputModule))
		return false;
	engine::Cable* c = cw->cable;
	return c->inputModule->id == inputModuleId && c->inputId == inputId && c->outputModule->id == outputModuleId && c->outputId == outputId;
}

void CableAdd::undo() {
	app::CableWidget* cw = APP->scene->rack->getCable(cableId);
	if (!cw)
		return;
	APP->scene->rack->removeCable(cw);
	delete cw;
}

void CableAdd::redo() {
	engine::Cable* cable = new engine::Cable;
	cable->id = cableId;
	cable->inputModule = APP->engine->getModule(inputModuleId);
	if (!cable->inputModule) {
		delete cable;
		return;
	}
	cable->inputId = inputId;
	cable->outputModule = APP->engine->getModule(outputModuleId);
	if (!cable->outputModule) {
		delete cable;
		return;
	}
	cable->outputId = outputId;
	APP->engine->addCable(cable);

	app::CableWidget* cw = new app::CableWidget;
	cw->setCable(cable);
	cw->color = color;
	APP->scene->rack->addCable(cw);
}


void CableColorChange::setCable(app::CableWidget* cw) {
	assert(cw);
	assert(cw->cable);
	assert(cw->cable->id >= 0);
	cableId = cw->cable->id;
}

void CableColorChange::undo() {
	app::CableWidget* cw = APP->scene->rack->getCable(cableId);
	if (!cw)
		return;
	cw->color = oldColor;
}

void CableColorChange::redo() {
	app::CableWidget* cw = APP->scene->rack->getCable(cableId);
	if (!cw)
		return;
	cw->color = newColor;
}


State::State() {
	clear();
}

State::~State() {
	clear();
}

void State::clear() {
	for (Action* action : actions) {
		delete action;
	}
	actions.clear();
	actionIndex = 0;
	savedIndex = -1;
}

void State::push(Action* action) {
	// Delete all future actions (if we have undone some actions)
	for (int i = actionIndex; i < (int) actions.size(); i++) {
		delete actions[i];
	}
	actions.resize(actionIndex);
	// Delete actions from beginning if limit is reached
	static const int limit = 500;
	int n = (int) actions.size() - limit + 1;
	if (n > 0) {
		for (int i = 0; i < n; i++) {
			delete actions[i];
		}
		actions.erase(actions.begin(), actions.begin() + n);
		actionIndex -= n;
		savedIndex -= n;
	}
	// Push action
	actions.push_back(action);
	actionIndex++;
	// Unset the savedIndex if we just permanently overwrote the saved state
	if (actionIndex == savedIndex) {
		savedIndex = -1;
	}
}

void State::undo() {
	if (canUndo()) {
		actionIndex--;
		actions[actionIndex]->undo();
	}
}

void State::redo() {
	if (canRedo()) {
		actions[actionIndex]->redo();
		actionIndex++;
	}
}

bool State::canUndo() {
	return actionIndex > 0;
}

bool State::canRedo() {
	return actionIndex < (int) actions.size();
}

std::string State::getUndoName() {
	if (!canUndo())
		return "";
	return actions[actionIndex - 1]->name;
}

std::string State::getRedoName() {
	if (!canRedo())
		return "";
	return actions[actionIndex]->name;
}

void State::setSaved() {
	savedIndex = actionIndex;
}

bool State::isSaved() {
	return actionIndex == savedIndex;
}


} // namespace history
} // namespace rack
