/*
 * Copyright 2010-2016 OpenXcom Developers.
 *
 * This file is part of OpenXcom.
 *
 * OpenXcom 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 3 of the License, or
 * (at your option) any later version.
 *
 * OpenXcom 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 OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "ManageAlienContainmentState.h"
#include <climits>
#include <sstream>
#include <algorithm>
#include "../Engine/Action.h"
#include "../Engine/Game.h"
#include "../Mod/Mod.h"
#include "../Engine/LocalizedText.h"
#include "../Interface/TextButton.h"
#include "../Interface/Window.h"
#include "../Interface/Text.h"
#include "../Interface/TextList.h"
#include "../Savegame/ResearchProject.h"
#include "../Savegame/SavedGame.h"
#include "../Savegame/Base.h"
#include "../Savegame/ItemContainer.h"
#include "../Mod/RuleItem.h"
#include "../Mod/RuleResearch.h"
#include "../Mod/Armor.h"
#include "../Engine/Timer.h"
#include "../Engine/Options.h"
#include "../Menu/ErrorMessageState.h"
#include "SellState.h"
#include "../Mod/RuleInterface.h"
 
namespace OpenXcom
{
 
/**
 * Initializes all the elements in the Manage Alien Containment screen.
 * @param game Pointer to the core game.
 * @param base Pointer to the base to get info from.
 * @param origin Game section that originated this state.
 */
ManageAlienContainmentState::ManageAlienContainmentState(Base *base, OptionsOrigin origin) : _base(base), _origin(origin), _sel(0), _aliensSold(0)
{
	bool overCrowded = Options::storageLimitsEnforced && _base->getFreeContainment() < 0;
	std::vector<std::string> researchList;
	for (std::vector<ResearchProject*>::const_iterator iter = _base->getResearch().begin(); iter != _base->getResearch().end(); ++iter)
	{
		const RuleResearch *research = (*iter)->getRules();
		RuleItem *item = _game->getMod()->getItem(research->getName());
		if (item && item->isAlien())
		{
			researchList.push_back(research->getName());
		}
	}
 
	// Create objects
	_window = new Window(this, 320, 200, 0, 0);
	_btnOk = new TextButton(overCrowded ? 288:148, 16, overCrowded ? 16:8, 176);
	_btnCancel = new TextButton(148, 16, 164, 176);
	_txtTitle = new Text(310, 17, 5, 8);
	_txtAvailable =  new Text(190, 9, 10, 24);
	_txtUsed = new Text(110, 9, 136, 24);
	_txtItem = new Text(120, 9, 10, 41);
	_txtLiveAliens = new Text(54, 18, 153, 32);
	_txtDeadAliens = new Text(54, 18, 207, 32);
	_txtInterrogatedAliens = new Text(54, 18, 261, 32);
	_lstAliens = new TextList(286, 112, 8, 53);
 
	// Set palette
	setInterface("manageContainment");
 
	add(_window, "window", "manageContainment");
	add(_btnOk, "button", "manageContainment");
	add(_btnCancel, "button", "manageContainment");
	add(_txtTitle, "text", "manageContainment");
	add(_txtAvailable, "text", "manageContainment");
	add(_txtUsed, "text", "manageContainment");
	add(_txtItem, "text", "manageContainment");
	add(_txtLiveAliens, "text", "manageContainment");
	add(_txtDeadAliens, "text", "manageContainment");
	add(_txtInterrogatedAliens, "text", "manageContainment");
	add(_lstAliens, "list", "manageContainment");
 
	centerAllSurfaces();
 
	// Set up objects
	_window->setBackground(_game->getMod()->getSurface((origin == OPT_BATTLESCAPE)? "BACK01.SCR" : "BACK05.SCR"));
 
	_btnOk->setText(tr("STR_REMOVE_SELECTED"));
	_btnOk->onMouseClick((ActionHandler)&ManageAlienContainmentState::btnOkClick);
	_btnOk->onKeyboardPress((ActionHandler)&ManageAlienContainmentState::btnOkClick, Options::keyOk);
 
	_btnCancel->setText(tr("STR_CANCEL"));
	_btnCancel->onMouseClick((ActionHandler)&ManageAlienContainmentState::btnCancelClick);
	_btnCancel->onKeyboardPress((ActionHandler)&ManageAlienContainmentState::btnCancelClick, Options::keyCancel);
 
	if (overCrowded)
	{
		_btnCancel->setVisible(false);
		_btnOk->setVisible(false);
	}
 
	_txtTitle->setBig();
	_txtTitle->setAlign(ALIGN_CENTER);
	_txtTitle->setText(tr("STR_MANAGE_CONTAINMENT"));
 
	_txtItem->setText(tr("STR_ALIEN"));
 
	_txtLiveAliens->setText(tr("STR_LIVE_ALIENS"));
	_txtLiveAliens->setWordWrap(true);
	_txtLiveAliens->setVerticalAlign(ALIGN_BOTTOM);
 
	_txtDeadAliens->setText(tr("STR_DEAD_ALIENS"));
	_txtDeadAliens->setWordWrap(true);
	_txtDeadAliens->setVerticalAlign(ALIGN_BOTTOM);
 
	_txtInterrogatedAliens->setText(tr("STR_UNDER_INTERROGATION"));
	_txtInterrogatedAliens->setWordWrap(true);
	_txtInterrogatedAliens->setVerticalAlign(ALIGN_BOTTOM);
 
	_txtAvailable->setText(tr("STR_SPACE_AVAILABLE").arg(_base->getFreeContainment()));
 
	_txtUsed->setText(tr("STR_SPACE_USED").arg( _base->getUsedContainment()));
 
	_lstAliens->setArrowColumn(184, ARROW_HORIZONTAL);
	_lstAliens->setColumns(4, 160, 64, 46, 46);
	_lstAliens->setSelectable(true);
	_lstAliens->setBackground(_window);
	_lstAliens->setMargin(2);
	_lstAliens->onLeftArrowPress((ActionHandler)&ManageAlienContainmentState::lstItemsLeftArrowPress);
	_lstAliens->onLeftArrowRelease((ActionHandler)&ManageAlienContainmentState::lstItemsLeftArrowRelease);
	_lstAliens->onLeftArrowClick((ActionHandler)&ManageAlienContainmentState::lstItemsLeftArrowClick);
	_lstAliens->onRightArrowPress((ActionHandler)&ManageAlienContainmentState::lstItemsRightArrowPress);
	_lstAliens->onRightArrowRelease((ActionHandler)&ManageAlienContainmentState::lstItemsRightArrowRelease);
	_lstAliens->onRightArrowClick((ActionHandler)&ManageAlienContainmentState::lstItemsRightArrowClick);
	_lstAliens->onMousePress((ActionHandler)&ManageAlienContainmentState::lstItemsMousePress);
 
	const std::vector<std::string> &items = _game->getMod()->getItemsList();
	for (std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i)
	{
		int qty = _base->getStorageItems()->getItem(*i);
		if (qty > 0 && _game->getMod()->getItem(*i, true)->isAlien())
		{
			_qtys.push_back(0);
			_aliens.push_back(*i);
			std::ostringstream ss;
			ss << qty;
			std::string rqty;
			std::vector<std::string>::iterator research = std::find(researchList.begin(), researchList.end(), *i);
			if (research != researchList.end())
			{
				rqty = "1";
				researchList.erase(research);
			}
			else
			{
				rqty = "0";
			}
			_lstAliens->addRow(4, tr(*i).c_str(), ss.str().c_str(), "0", rqty.c_str());
		}
	}
 
	for (std::vector<std::string>::const_iterator i = researchList.begin(); i != researchList.end(); ++i)
	{
		_aliens.push_back(*i);
		_qtys.push_back(0);
		_lstAliens->addRow(4, tr(*i).c_str(), "0", "0", "1");
		_lstAliens->setRowColor(_qtys.size() -1, _lstAliens->getSecondaryColor());
	}
	_timerInc = new Timer(250);
	_timerInc->onTimer((StateHandler)&ManageAlienContainmentState::increase);
	_timerDec = new Timer(250);
	_timerDec->onTimer((StateHandler)&ManageAlienContainmentState::decrease);
}
 
/**
 *
 */
ManageAlienContainmentState::~ManageAlienContainmentState()
{
	delete _timerInc;
	delete _timerDec;
}
 
/**
 * Runs the arrow timers.
 */
void ManageAlienContainmentState::think()
{
	State::think();
 
	_timerInc->think(this, 0);
	_timerDec->think(this, 0);
}
 
/**
 * Deals with the selected aliens.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::btnOkClick(Action *)
{
	for (size_t i = 0; i < _qtys.size(); ++i)
	{
		if (_qtys[i] > 0)
		{
			// remove the aliens
			_base->getStorageItems()->removeItem(_aliens[i], _qtys[i]);
 
			if (Options::canSellLiveAliens)
			{
				_game->getSavedGame()->setFunds(_game->getSavedGame()->getFunds() + _game->getMod()->getItem(_aliens[i], true)->getSellCost() * _qtys[i]);
			}
			else
			{
				// add the corpses
				_base->getStorageItems()->addItem(
					_game->getMod()->getArmor(
						_game->getMod()->getUnit(
							_aliens[i], true
						)->getArmor(), true
					)->getCorpseGeoscape(), _qtys[i]
				); // ;)
			}
		}
	}
	_game->popState();
 
	if (Options::storageLimitsEnforced && _base->storesOverfull())
	{
		_game->pushState(new SellState(_base, _origin));
		if (_origin == OPT_BATTLESCAPE)
			_game->pushState(new ErrorMessageState(tr("STR_STORAGE_EXCEEDED").arg(_base->getName()), _palette, _game->getMod()->getInterface("manageContainment")->getElement("errorMessage")->color, "BACK01.SCR", _game->getMod()->getInterface("manageContainment")->getElement("errorPalette")->color));
		else
			_game->pushState(new ErrorMessageState(tr("STR_STORAGE_EXCEEDED").arg(_base->getName()), _palette, _game->getMod()->getInterface("manageContainment")->getElement("errorMessage")->color, "BACK13.SCR", _game->getMod()->getInterface("manageContainment")->getElement("errorPalette")->color));
 	}
}
 
/**
 * Returns to the previous screen.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::btnCancelClick(Action *)
{
	_game->popState();
}
 
/**
 * Starts increasing the alien count.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsRightArrowPress(Action *action)
{
	_sel = _lstAliens->getSelectedRow();
	if (action->getDetails()->button.button == SDL_BUTTON_LEFT && !_timerInc->isRunning()) _timerInc->start();
}
 
/**
 * Stops increasing the alien count.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsRightArrowRelease(Action *action)
{
	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
	{
		_timerInc->stop();
	}
}
 
/**
 * Increases the selected alien count;
 * by one on left-click, to max on right-click.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsRightArrowClick(Action *action)
{
	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) increaseByValue(INT_MAX);
	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
	{
		increaseByValue(1);
		_timerInc->setInterval(250);
		_timerDec->setInterval(250);
	}
}
 
/**
 * Starts decreasing the alien count.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsLeftArrowPress(Action *action)
{
	_sel = _lstAliens->getSelectedRow();
	if (action->getDetails()->button.button == SDL_BUTTON_LEFT && !_timerDec->isRunning()) _timerDec->start();
}
 
/**
 * Stops decreasing the alien count.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsLeftArrowRelease(Action *action)
{
	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
	{
		_timerDec->stop();
	}
}
 
/**
 * Decreases the selected alien count;
 * by one on left-click, to 0 on right-click.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsLeftArrowClick(Action *action)
{
	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) decreaseByValue(INT_MAX);
	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
	{
		decreaseByValue(1);
		_timerInc->setInterval(250);
		_timerDec->setInterval(250);
	}
}
 
/**
 * Handles the mouse-wheels on the arrow-buttons.
 * @param action Pointer to an action.
 */
void ManageAlienContainmentState::lstItemsMousePress(Action *action)
{
	_sel = _lstAliens->getSelectedRow();
	if (action->getDetails()->button.button == SDL_BUTTON_WHEELUP)
	{
		_timerInc->stop();
		_timerDec->stop();
		if (action->getAbsoluteXMouse() >= _lstAliens->getArrowsLeftEdge() &&
			action->getAbsoluteXMouse() <= _lstAliens->getArrowsRightEdge())
		{
			increaseByValue(Options::changeValueByMouseWheel);
		}
	}
	else if (action->getDetails()->button.button == SDL_BUTTON_WHEELDOWN)
	{
		_timerInc->stop();
		_timerDec->stop();
		if (action->getAbsoluteXMouse() >= _lstAliens->getArrowsLeftEdge() &&
			action->getAbsoluteXMouse() <= _lstAliens->getArrowsRightEdge())
		{
			decreaseByValue(Options::changeValueByMouseWheel);
		}
	}
}
 
/**
 * Gets the quantity of the currently selected alien on the base.
 * @return Quantity of selected alien on the base.
 */
int ManageAlienContainmentState::getQuantity()
{
	return _base->getStorageItems()->getItem(_aliens[_sel]);
}
 
/**
 * Increases the quantity of the selected alien to exterminate by one.
 */
void ManageAlienContainmentState::increase()
{
	_timerDec->setInterval(50);
	_timerInc->setInterval(50);
	increaseByValue(1);
}
 
/**
 * Increases the quantity of the selected alien to exterminate by "change".
 * @param change How much we want to add.
 */
void ManageAlienContainmentState::increaseByValue(int change)
{
	int qty = getQuantity() - _qtys[_sel];
	if (change <= 0 || qty <= 0) return;
 
	change = std::min(qty, change);
	_qtys[_sel] += change;
	_aliensSold += change;
	updateStrings();
}
 
/**
 * Decreases the quantity of the selected alien to exterminate by one.
 */
void ManageAlienContainmentState::decrease()
{
	_timerInc->setInterval(50);
	_timerDec->setInterval(50);
	decreaseByValue(1);
}
 
/**
 * Decreases the quantity of the selected alien to exterminate by "change".
 * @param change How much we want to remove.
 */
void ManageAlienContainmentState::decreaseByValue(int change)
{
	if (change <= 0 || _qtys[_sel] <= 0) return;
	change = std::min(_qtys[_sel], change);
	_qtys[_sel] -= change;
	_aliensSold -= change;
	updateStrings();
}
 
/**
 * Updates the quantity-strings of the selected alien.
 */
void ManageAlienContainmentState::updateStrings()
{
	std::ostringstream ss, ss2;
	int qty = getQuantity() - _qtys[_sel];
	ss << qty;
	ss2 << _qtys[_sel];
 
	_lstAliens->setRowColor(_sel, (qty == 0)? _lstAliens->getSecondaryColor() : _lstAliens->getColor());
	_lstAliens->setCellText(_sel, 1, ss.str());
	_lstAliens->setCellText(_sel, 2, ss2.str());
 
	int aliens = _base->getUsedContainment() - _aliensSold;
	int spaces = _base->getAvailableContainment() - aliens;
	if (Options::storageLimitsEnforced)
	{
		_btnOk->setVisible(spaces >= 0);
	}
	_txtAvailable->setText(tr("STR_SPACE_AVAILABLE").arg(spaces));
	_txtUsed->setText(tr("STR_SPACE_USED").arg(aliens));
}
 
}

V826 Consider replacing the 'researchList' std::vector with std::list. Overall efficiency of operations will increase.

V807 Decreased performance. Consider creating a pointer to avoid using the '_game->getMod()' expression repeatedly.