/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "bagel/baglib/button_object.h"
#include "bagel/baglib/master_win.h"
#include "bagel/baglib/time_object.h"
#include "bagel/baglib/rp_object.h"
#include "bagel/baglib/wield.h"
#include "bagel/baglib/zoom_pda.h"

namespace Bagel {

#define ZOOM_MOO_WLD      "MOOZ_WLD"
#define ZOOM_INV_WLD      "INVZ_WLD"
#define ZOOM_MAP_WLD      "MAPZ_WLD"
#define ZOOM_LOG_WLD      "LOGZ_WLD"

// Keep track of updates...
static uint32 g_lZoomPDALastUpdate;

void SBZoomPda::initialize() {
	g_lZoomPDALastUpdate = 0;
}

SBZoomPda::SBZoomPda(CBofWindow *pParent, const CBofRect &xRect, bool bActivated)
	: CBagStorageDevWnd(),
	  SBBasePda(pParent, xRect, bActivated) {
	_xSDevType = SDEV_ZOOMPDA;
}

ErrorCode SBZoomPda::onRender(CBofBitmap *pBmp, CBofRect *pRect) {
	assert(isValidObject(this));
	assert(pBmp != nullptr);

	// Paint the storage device
	if (pBmp != nullptr) {
		bool bUpdate = false;
		// Only clear the background and paint the backdrop if we've
		// been instructed to.

		if (preFilterPan()) {
			preFilter(pBmp, pRect, (_curDisplay == nullptr ? nullptr : _curDisplay->getObjectList()));

			// Paint our storage device the first time through and the next time
			// through, this takes care of multiple text drawing problems (trust me!).
			if (_bFirstPaint == false) {
				setPreFilterPan(false);
			}

			if (getWorkBmp() != nullptr) {
				getWorkBmp()->paint(pBmp, pRect, pRect);
			}

			bUpdate = true;
		}

		paintStorageDevice(nullptr, pBmp, pRect);

		// Paint the inventory or Map to backdrop
		if (bUpdate && (_curDisplay != nullptr))
			_curDisplay->update(pBmp, _curDisplay->getPosition(), pRect);
	}
	
	return _errCode;
}

ErrorCode SBZoomPda::loadFile(const CBofString &sFile) {
	ErrorCode errorCode = CBagStorageDev::loadFile(sFile);
	removeObject(_mooWnd);
	removeObject(_invWnd);
	removeObject(_mapWnd);
	removeObject(_logWnd);

	return errorCode;
}

ErrorCode SBZoomPda::detach() {
	bool bLogZoomed = (_logWnd == _curDisplay);

	// Other classes need to know if we're zoomed
	setZoomed(false);
	if (_invWnd) {
		_invWnd->detach();
		_invWnd = nullptr;
	}

	if (_mooWnd) {
		_mooWnd->detach();
		_mooWnd = nullptr;
	}

	if (_mapWnd) {
		_mapWnd->detach();
		_mapWnd = nullptr;
	}

	if (_logWnd) {
		_logWnd->detach();
		_logWnd = nullptr;
	}

	// Since the regular PDA does not have a detach method (it doesn't get
	// flushed out until you go to a flashback or quit the game), go through
	// the entire list of RPO's (residue print objects) and restore the saved
	// variable values.  We do this so that any values that were changed in
	// the zoomed version are propagated down to the un-zoomed pda.

	CBagRPObject::synchronizeResiduePrintedObjects(bLogZoomed);

	CBagStorageDevWnd::detach();

	return ERR_NONE;
}

ErrorCode SBZoomPda::attach() {
	// Other classes need to know if we're zoomed
	setZoomed(true);

	ErrorCode errorCode = CBagStorageDevWnd::attach();
	if (errorCode == ERR_NONE) {
		CBagStorageDev *pSDev;
		if (!_mooWnd) {
			pSDev = g_SDevManager->getStorageDevice(ZOOM_MOO_WLD);

			if (pSDev != nullptr) {
				_mooWnd = (CBagStorageDevBmp *)pSDev;
				_mooWnd->setAssociateWnd(getAssociateWnd());
				_mooWnd->setTransparent(false);
				_mooWnd->setVisible(false);
				errorCode = _mooWnd->attach();
			}
		} else {
			// Already attached just update
			_mooWnd->attachActiveObjects();
		}

		if (!_invWnd) {
			pSDev = g_SDevManager->getStorageDevice(ZOOM_INV_WLD);
			if (pSDev != nullptr) {
				_invWnd = (CBagStorageDevBmp *)pSDev;
				_invWnd->setAssociateWnd(getAssociateWnd());

				_invWnd->setTransparent(false);
				_invWnd->setVisible(false);
				errorCode = _invWnd->attach();
			} else {
				bofMessageBox("No PDA INVENTORY found", __FILE__);
				errorCode = ERR_UNKNOWN;
			}
		} else {
			// Already attached just update
			_invWnd->attachActiveObjects();
		}

		if (!_mapWnd) {
			pSDev = g_SDevManager->getStorageDevice(ZOOM_MAP_WLD);
			if (pSDev != nullptr) {
				_mapWnd = (CBagStorageDevBmp *)pSDev;
				_mapWnd->setAssociateWnd(getAssociateWnd());

				_mapWnd->setTransparent(false);
				_mapWnd->setVisible(false);
				errorCode = _mapWnd->attach();
			} else {
				bofMessageBox("No PDA MAP found", __FILE__);
				errorCode = ERR_UNKNOWN;
			}
		} else {
			// Already attached just update
			_mapWnd->attachActiveObjects();
		}

		if (!_logWnd) {
			pSDev = g_SDevManager->getStorageDevice(ZOOM_LOG_WLD);
			if (pSDev != nullptr) {
				_logWnd = (CBagStorageDevBmp *)pSDev;
				_logWnd->setAssociateWnd(getAssociateWnd());

				_logWnd->setTransparent(false);
				_logWnd->setVisible(false);
				errorCode = _logWnd->attach();
			}
		} else {
			// Already attached just update
			_logWnd->attachActiveObjects();
		}

		if (_pdaMode == PDA_INV_MODE) {
			showInventory();
		} else if (_pdaMode == PDA_MAP_MODE) {
			showMap();
		} else if (_pdaMode == PDA_LOG_MODE) {
			showLog();
		}

		show();
		invalidateRect(nullptr);
		updateWindow();
	}

	// Keep track of updates...
	g_lZoomPDALastUpdate = 0;

	return errorCode;
}

CBagObject *SBZoomPda::onNewButtonObject(const CBofString &) {
	CBagButtonObject *PdaButtObj = new CBagButtonObject();
	PdaButtObj->setCallBack(pdaButtonHandler, (SBBasePda *)this);

	return PdaButtObj;
}

void SBZoomPda::onLButtonUp(uint32 nFlags, CBofPoint *xPoint, void *) {
	// Need to override the CBagStorageDevWnd::onLButtonUp(nFlags, xPoint)
	// to do our own thing.

	*_xCursorLocation = *xPoint;
	CBofPoint xCursorLocation = devPtToViewPort(*xPoint);

	CBagObject *pObj = getObject(xCursorLocation, true);
	if (pObj != nullptr) {
		if (pObj->isActive()) {
			pObj->onLButtonUp(nFlags, xPoint);
			setLActiveObject(pObj);
		}
	} else {
		setLActivity(kMouseNONE);

		if (_curDisplay) {
			CBofRect offset = CBagStorageDev::getRect();
			xPoint->x -= offset.topLeft().x;
			xPoint->y -= offset.topLeft().y;

			// Make sure this stuff is nice and dirty before calling off
			// to the button handling routine, this assures that if we go from one screen
			// to the next, then we'll get redrawn.
			setPreFilterPan(true);
			makeListDirty(_curDisplay->getObjectList());

			_curDisplay->onLButtonUp(nFlags, xPoint, nullptr);
		} else {
			// We have no mode yet, then pass it to the default method
			if (_pdaMode == PDA_NO_MODE) {
				CBagStorageDevWnd::onLButtonUp(nFlags, xPoint);
			}
		}
	}

	// This thing could get trashed by the underlying code...
	if (isCreated()) {
		invalidateRect(nullptr);
		updateWindow();
	}
}

void SBZoomPda::onMouseMove(uint32 nFlags, CBofPoint *pPoint, void *) {
	assert(isValidObject(this));

	CBagStorageDev::onMouseMove(nFlags, pPoint, getAssociateWnd());


	// This should be on update cursor virtual func
	if (getExitOnEdge() && (pPoint->x < getExitOnEdge()) && (pPoint->y < 300) && !(getPrevSDev().isEmpty())) {
		CBagMasterWin::setActiveCursor(10);
	} else {
		CBofRect cRect = getBackdrop()->getRect();
		CBagMasterWin::setActiveCursor(getProperCursor(*pPoint, cRect));
	}
}

void SBZoomPda::onMainLoop() {
	uint32 nCurTime = getTimer();

	// Force an update every 1/4 second
	if (_pdaMode == PDA_INV_MODE || _pdaMode == PDA_MAP_MODE) {
		if (nCurTime > (g_lZoomPDALastUpdate + 250)) {
			g_lZoomPDALastUpdate = nCurTime;

			setPreFilterPan(true);
		}
	}

	CBagStorageDevWnd::onMainLoop();
}

ErrorCode SBZoomPda::attachActiveObjects() {
	SBBasePda::attachActiveObjects();
	return CBagStorageDevWnd::attachActiveObjects();
}

ErrorCode SBZoomPda::detachActiveObjects() {
	SBBasePda::detachActiveObjects();

	return CBagStorageDevWnd::detachActiveObjects();
}

} // namespace Bagel
