/* 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "common/algorithm.h"
#include "common/util.h"
#include "xeen/xsurface.h"
#include "xeen/resources.h"
#include "xeen/screen.h"

namespace Xeen {

XSurface::XSurface() : Graphics::Surface(), _freeFlag(false) {
}

XSurface::XSurface(int w, int h) : Graphics::Surface(), _freeFlag(false) {
	create(w, h);
}

XSurface::~XSurface() {
	if (_freeFlag)
		free();
}

void XSurface::create(uint16 w, uint16 h) {
	Graphics::Surface::create(w, h, Graphics::PixelFormat::createFormatCLUT8());
	_freeFlag = true;
}

void XSurface::create(XSurface *s, const Common::Rect &bounds) {
	pixels = (byte *)s->getBasePtr(bounds.left, bounds.top);
	format = Graphics::PixelFormat::createFormatCLUT8();
	pitch = s->pitch;
	w = bounds.width();
	h = bounds.height();

	_freeFlag = false;
}


void XSurface::transBlitTo(XSurface &dest) const {
	transBlitTo(dest, Common::Point());
}

void XSurface::blitTo(XSurface &dest) const {
	blitTo(dest, Common::Point());
}

void XSurface::transBlitTo(XSurface &dest, const Common::Point &destPos) const {
	if (dest.getPixels() == nullptr)
		dest.create(w, h);

	for (int yp = 0; yp < h; ++yp) {
		const byte *srcP = (const byte *)getBasePtr(0, yp);
		byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y + yp);

		for (int xp = 0; xp < w; ++xp, ++srcP, ++destP) {
			if (*srcP != 0)
				*destP = *srcP;
		}
	}

	dest.addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + w, destPos.y));
}

void XSurface::transBlitTo(XSurface &dest, const Common::Point &destPos, 
		int scale, int transparentColor) {
	int destX = destPos.x, destY = destPos.y;
	int frameWidth = this->w;
	int frameHeight = this->h;
	int direction = 1;

	int highestDim = MAX(frameWidth, frameHeight);
	bool lineDist[SCREEN_WIDTH];
	int distXCount = 0, distYCount = 0;

	if (scale != 0) {
		int distCtr = 0;
		int distIndex = 0;
		do {
			distCtr += scale;
			if (distCtr < 100) {
				lineDist[distIndex] = false;
			}
			else {
				lineDist[distIndex] = true;
				distCtr -= 100;

				if (distIndex < frameWidth)
					++distXCount;

				if (distIndex < frameHeight)
					++distYCount;
			}
		} while (++distIndex < highestDim);

		destX += (this->w - distXCount) / 2;
		destY += (this->h - distYCount) / 2;
	}

	// Start of draw logic for scaled sprites
	const byte *srcPixelsP = (const byte *)getPixels();

	int destRight = dest.w - 1;
	int destBottom = dest.h - 1;

	// Check x bounding area
	int spriteLeft = 0;
	int spriteWidth = distXCount;
	int widthAmount = destX + distXCount - 1;

	if (destX < 0) {
		spriteWidth += destX;
		spriteLeft -= destX;
	}
	widthAmount -= destRight;
	if (widthAmount > 0)
		spriteWidth -= widthAmount;

	if (spriteWidth <= 0)
		return;

	// Check y bounding area
	int spriteTop = 0;
	int spriteHeight = distYCount;
	int heightAmount = destY + distYCount - 1;

	if (destY < 0) {
		spriteHeight += destY;
		spriteTop -= destY;
	}
	heightAmount -= destBottom;
	if (heightAmount > 0)
		spriteHeight -= heightAmount;
	int spriteBottom = spriteTop + spriteHeight;

	if (spriteHeight <= 0)
		return;

	byte *destPixelsP = (byte *)dest.getBasePtr(destX + spriteLeft, destY + spriteTop);
	int destWidth = 0, destHeight = 0;

	spriteLeft = spriteLeft * direction;

	// Loop through the lines of the sprite
	for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += this->pitch) {
		if (!lineDist[yp])
			// Not a display line, so skip it
			continue;
		// Check whether the sprite line is in the display range
		++sprY;
		if ((sprY >= spriteBottom) || (sprY < spriteTop))
			continue;

		// Found a line to display. Loop through the pixels
		const byte *srcP = srcPixelsP;
		byte *destP = destPixelsP;
		++destHeight;

		for (int xp = 0, sprX = 0; xp < frameWidth; ++xp, ++srcP) {
			if (xp < spriteLeft)
				// Not yet reached start of display area
				continue;
			if (!lineDist[sprX++])
				// Not a display pixel
				continue;

			if (*srcP != transparentColor)
				*destP = *srcP;

			destP += direction;
		}

		// Keep track of widest line drawn
		destWidth = MAX(destP - destPixelsP, destWidth);

		// Move to the next destination line
		destPixelsP += dest.pitch;
	}

	// Add a dirty rect for the affected area
	dest.addDirtyRect(Common::Rect(destX + spriteLeft, destY + spriteTop,
		destX + spriteLeft + destWidth, destY + spriteTop + destHeight));
}

void XSurface::blitTo(XSurface &dest, const Common::Point &destPos) const {
	if (dest.getPixels() == nullptr)
		dest.create(w, h);

	for (int yp = 0; yp < h; ++yp) {
		const byte *srcP = (const byte *)getBasePtr(0, yp);
		byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y + yp);

		Common::copy(srcP, srcP + w, destP);
	}

	dest.addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + w, destPos.y + h));
}

} // End of namespace Xeen