/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/costume.cpp,v 1.8 2001/11/06 20:00:47 strigeus Exp $
 *
 */

#include "stdafx.h"
#include "scumm.h"

const byte revBitMask[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};

void CostumeRenderer::ignorePakCols(int a) {
	int n;
	n = _height;
	if (a>1) n *= a;
	do {
		_repcolor = *_srcptr++;
		_replen = _repcolor&_maskval;
		if (_replen==0) {
			_replen = *_srcptr++;
		}
		do {
			if (!--n) {
				_repcolor >>= _shrval;
				return;
			}
		} while (--_replen);
	} while (1);
}

const byte cost_scaleTable[256] = {
255, 253, 125, 189, 61, 221, 93, 157, 29, 237,
109, 173, 45, 205, 77, 141, 13, 245, 117, 181,
53, 213, 85, 149, 21, 229, 101, 165, 37, 197, 69,
133, 5, 249, 121, 185, 57, 217, 89, 153, 25, 233,
105, 169, 41, 201, 73, 137, 9, 241, 113, 177, 49,
209, 81, 145, 17, 225, 97, 161, 33, 193, 65, 129,
1, 251, 123, 187, 59, 219, 91, 155, 27, 235, 107,
171, 43, 203, 75, 139, 11, 243, 115, 179, 51, 211,
83, 147, 19, 227, 99, 163, 35, 195, 67, 131, 3,
247, 119, 183, 55, 215, 87, 151, 23, 231, 103,
167, 39, 199, 71, 135, 7, 239, 111, 175, 47, 207,
79, 143, 15, 223, 95, 159, 31, 191, 63, 127, 0,
128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208,
48, 176, 112, 240, 8, 136, 72, 200, 40, 168, 104,
232, 24, 152, 88, 216, 56, 184, 120, 248, 4, 132,
68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52,
180, 116, 244, 12, 140, 76, 204, 44, 172, 108,
236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130,
66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50,
178, 114, 242, 10, 138, 74, 202, 42, 170, 106,
234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134,
70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54,
182, 118, 246, 14, 142, 78, 206, 46, 174, 110,
238, 30, 158, 94, 222, 62, 190, 126, 254
};

byte CostumeRenderer::mainRoutine(Actor *a, int slot, int frame) {
	int xmove, ymove, i,b,s;
	uint scal;
	byte scaling;
	byte charsetmask, masking;
	byte unk19;
	int ex1,ex2;

	CHECK_HEAP

	_maskval = 0xF;
	_shrval = 4;
	if (_numColors == 32) {
		_maskval = 7;
		_shrval = 3;
	}
	
	_width2 = _srcptr[0];
	_width = _width2;
	_height2 = _srcptr[2];
	_height = _height2;
	xmove = (int16)READ_LE_UINT16(_srcptr+4) + _xmove;
	ymove = (int16)READ_LE_UINT16(_srcptr+6) + _ymove;
	_xmove += (int16)READ_LE_UINT16(_srcptr+8);
	_ymove -= (int16)READ_LE_UINT16(_srcptr+10);
	_srcptr += 12;

	switch(_ptr[7]&0x7F) {
	case 0x60: case 0x61:
		ex1 = _srcptr[0];
		ex2 = _srcptr[1];
		_srcptr += 2;
		if (ex1!=0xFF || ex2!=0xFF) {
			ex1=READ_LE_UINT16(_ptr + _numColors + 10 + ex1*2);
			_srcptr = _ptr + READ_LE_UINT16(_ptr + ex1 + ex2*2) + 14;
		}
	} 

	_xpos = _actorX;
	_ypos = _actorY;
		
	scaling = _scaleX==255 && _scaleY==255 ? 0 : 1;
	s = 0;

	if (scaling) {
		_scaleIndexXStep = -1;
		if (xmove < 0) {
			xmove = -xmove;
			_scaleIndexXStep = 1;
		}

		if(_mirror) {
			unk19 = _scaleIndexX = 128 - xmove;
			for (i=0; i<xmove; i++) {
				scal = cost_scaleTable[_scaleIndexX++];
				if (scal < _scaleX)
					_xpos -= _scaleIndexXStep;
			}
			_right = _left = _xpos;
			_scaleIndexX = unk19;
			for (i=0; i<_width; i++) {
				if (_right < 0) {
					s++;
					unk19 = _scaleIndexX;
				}
				scal = cost_scaleTable[_scaleIndexX++];
				if (scal < _scaleX)
					_right++;
			}
		} else {
			unk19 = _scaleIndexX = xmove + 128;
			for (i=0; i<xmove; i++) {
				scal = cost_scaleTable[_scaleIndexX--];
				if (scal < _scaleX)
					_xpos += _scaleIndexXStep;
			}
			_right = _left = _xpos;
			_scaleIndexX = unk19;
			for (i=0; i<_width; i++) {
				if (_left > 319) {
					s++;
					unk19 = _scaleIndexX;
				}
				scal = cost_scaleTable[_scaleIndexX--];
				if(scal < _scaleX)
					_left--;
			}
		}
		_scaleIndexX = unk19;
		if (s)
			s--;
		_scaleIndexYStep = -1;
		if (ymove < 0) {
			ymove = -ymove;
			_scaleIndexYStep = 1;
		}
		_scaleIndexY = 128 - ymove;
		for (i=0; i<ymove; i++) {
			scal = cost_scaleTable[_scaleIndexY++];
			if (scal < _scaleY)
				_ypos -= _scaleIndexYStep;
		}
		_top = _bottom = _ypos;
		_scaleIndexY = 128 - ymove;
		for (i=0; i<_height; i++) {
			scal = cost_scaleTable[_scaleIndexY++];
			if (scal < _scaleY) 
				_bottom++;
		}
		_scaleIndexY = _scaleIndexYTop = 128 - ymove;
	} else {
		if(_mirror==0)
			xmove = -xmove;
		_xpos += xmove;
		_ypos += ymove;
		if (_mirror) {
			_left = _xpos;
			_right = _xpos + _width;
		} else {
			_left = _xpos - _width;
			_right = _xpos;
		}
		_top = _ypos;
		_bottom = _top + _height;
	}

	_scaleIndexXStep = -1;
	if (_mirror)
		_scaleIndexXStep = 1;
	_ypostop = _ypos;
	_vscreenheight = _vm->virtscr[0].height;
	_vm->updateDirtyRect(0, _left, _right+1,_top,_bottom,1<<a->number);

	if (_top >= (int)_vscreenheight || _bottom <= 0)
		return 0;

	_ypitch = _height * 320;
	_docontinue = 0;
	b = 1;
	if (_left > 319 || _right <= 0)
		return 0;
	if (_mirror) {
		_ypitch--;
		if (scaling==0) {
			s = -_xpos;
		}
		if (s > 0) {
			_width2 -= s;
			ignorePakCols(s);
			_xpos = 0;
			_docontinue = 1;
		} else {
			s = _right - 320;
			if (s<=0) {
				b = 2;
			} else {
				_width2 -= s;
			}
		}
	} else {
		_ypitch++;
		if(scaling==0)
			s = _right - 320;
		if (s > 0) {
			_width2 -= s;
			ignorePakCols(s);
			_xpos = 319;
			_docontinue = 1;
		} else {
			s = -1 - _left;
			if (s <= 0)
				b = 2;
			else
				_width2 -= s;
		}
	}

	if (_width2==0)
		return 0;

	if ((uint)_top > (uint)_vscreenheight)
		_top = 0;

	if (_left<0) _left=0;

	if ((uint)_bottom > _vscreenheight)
		_bottom = _vscreenheight;

	if (a->top > _top)
		a->top = _top;

	if (a->bottom < _bottom)
		a->bottom = _bottom;

	if (_height2 + _top >= 256) {
		CHECK_HEAP
		return 2;
	}


	_bgbak_ptr = _vm->getResourceAddress(rtBuffer, 5) + _vm->virtscr[0].xstart + _ypos*320 + _xpos;
	_backbuff_ptr = _vm->getResourceAddress(rtBuffer, 1) + _vm->virtscr[0].xstart + _ypos*320 + _xpos;
	charsetmask = _vm->hasCharsetMask(_left, _top + _vm->virtscr[0].topline, _right, _vm->virtscr[0].topline + _bottom);
	masking = 0;

	if (_zbuf) {
		masking = _vm->isMaskActiveAt(_left, _top, _right, _bottom,
			_vm->getResourceAddress(rtBuffer, 9) + _vm->gdi._imgBufOffs[_zbuf] + _vm->_screenStartStrip
		);
	}

	if (_zbuf || charsetmask) {
		_mask_ptr = _vm->getResourceAddress(rtBuffer, 9) + _ypos*40 + _vm->_screenStartStrip;

		_imgbufoffs = _vm->gdi._imgBufOffs[_zbuf];
		if (!charsetmask && _zbuf!=0)
			_mask_ptr += _imgbufoffs;
		_mask_ptr_dest = _mask_ptr + _xpos / 8;
	}

	CHECK_HEAP

	switch ((scaling<<2)|(masking<<1)|charsetmask) {
	case 0: 
		proc6();
		break;
	case 1: case 2:
		proc5();
		break;
	case 3:
		proc4();
		break;
	case 4:
		proc1();
		break;
	case 5:case 6:
		proc2();
		break;
	case 7:
		proc3();
		break;
	}

	CHECK_HEAP
	return b;
}

void CostumeRenderer::proc6() {
	byte len;
	byte *src, *dst;
	byte width,height,pcolor;
	int color;
	uint y;
	uint scrheight;

	y = _ypos;
	len = _replen;
	src = _srcptr;
	dst = _backbuff_ptr;
	color = _repcolor;
	scrheight = _vscreenheight;
	width = _width2;
	height = _height2;

	if (_docontinue) goto StartPos;

	do {
		len = *src++;
		color = len>>_shrval;
		len &= _maskval;
		if (!len) len = *src++;

		do {
			if (color && y < scrheight) {
				pcolor = _palette[color];
				if (pcolor==13) {
					pcolor = _transEffect[*dst];
				}
				*dst = pcolor;
			}

			dst += 320;
			y++;
			if (!--height) {
				if (!--width)
					return;
				height = _height;
				dst -= _ypitch;
				y = _ypostop;
			}
StartPos:;
		} while (--len);
	} while (1);
}

void CostumeRenderer::proc5() {
	byte *mask,*src,*dst;
	byte maskbit,len,height,pcolor;
	uint y,scrheight;
	int color;

	mask = _mask_ptr = _mask_ptr_dest;
	maskbit = revBitMask[_xpos&7];
	y = _ypos;
	src = _srcptr;
	dst = _backbuff_ptr;
	len = _replen;
	color = _repcolor;
	scrheight = _vscreenheight;
	height = _height2;

	if (_docontinue) goto StartPos;

	do {
		len = *src++;
		color = len>>_shrval;
		len &= _maskval;
		if (!len) len = *src++;

		do {
			if (color && y<scrheight && !(*mask&maskbit)) {
				pcolor = _palette[color];
				if (pcolor==13)
					pcolor = _transEffect[*dst];
				*dst = pcolor;
			}
			dst += 320;
			y++;
			mask += 40;
			if (!--height) {
				if(!--_width2)
					return;
				height = _height;
				dst -= _ypitch;
				y = _ypostop;
				if(_scaleIndexXStep!=1) {
					maskbit<<=1;
					if (!maskbit) {
						maskbit=1;
						_mask_ptr--;
					}
				} else {
					maskbit>>=1;
					if (!maskbit) {
						maskbit=0x80;
						_mask_ptr++;
					}
				}
				mask = _mask_ptr;
			}
StartPos:;
		} while (--len);
	} while(1);
}

void CostumeRenderer::proc4() {
	byte *mask,*src,*dst;
	byte maskbit,len,height,pcolor;
	uint y,scrheight;
	int color;
	
	mask = _mask_ptr = _mask_ptr_dest;
	maskbit = revBitMask[_xpos&7];
	y = _ypos;
	src = _srcptr;
	dst = _backbuff_ptr;
	len = _replen;
	color = _repcolor;
	scrheight = _vscreenheight;
	height = _height2;

	if (_docontinue) goto StartPos;

	do {
		len = *src++;
		color = len>>_shrval;
		len &= _maskval;
		if (!len) len = *src++;
		
		do {
			if (color && y<scrheight && !((*mask|mask[_imgbufoffs])&maskbit)) {
				pcolor = _palette[color];
				if (pcolor==13)
					pcolor = _transEffect[*dst];
				*dst = pcolor;
			}
			dst += 320;
			y++;
			mask += 40;
			if (!--height) {
				if(!--_width2)
					return;
				height = _height;
				dst -= _ypitch;
				y = _ypostop;
				if(_scaleIndexXStep!=1) {
					maskbit<<=1;
					if (!maskbit) {
						maskbit=1;
						_mask_ptr--;
					}
				} else {
					maskbit>>=1;
					if (!maskbit) {
						maskbit=0x80;
						_mask_ptr++;
					}
				}
				mask = _mask_ptr;
			}
StartPos:;
		} while (--len);
	} while(1);
}

void CostumeRenderer::proc3() {
	byte *mask,*src,*dst;
	byte maskbit,len,height,pcolor,width;
	int color,t;
	uint y;
	
	mask = _mask_ptr_dest;
	dst = _backbuff_ptr;
	height = _height2;
	width = _width2;
	len = _replen;
	color = _repcolor;
	src = _srcptr;
	maskbit = revBitMask[_xpos&7];
	y = _ypos;

	if (_docontinue) goto StartPos;

	do {
		len = *src++;
		color = len>>_shrval;
		len &= _maskval;
		if (!len) len = *src++;
		do {
			if (cost_scaleTable[_scaleIndexY++] < _scaleY) {
				if (color && y < _vscreenheight && !((*mask|mask[_imgbufoffs])&maskbit)) {
					pcolor = _palette[color];
					if (pcolor==13)
						pcolor = _transEffect[*dst];
					*dst = pcolor;
				}
				dst += 320;
				mask += 40;
				y++;
			}
			if (!--height) {
				if(!--width)
					return;
				height = _height;
				y = _ypostop;
				_scaleIndexY = _scaleIndexYTop;
				t = _scaleIndexX;
				_scaleIndexX = t + _scaleIndexXStep;
				if (cost_scaleTable[t] < _scaleX) {
					_xpos += _scaleIndexXStep;
					if (_xpos >= 320)
						return;
					maskbit = revBitMask[_xpos&7];
					_backbuff_ptr += _scaleIndexXStep;
				}
				dst = _backbuff_ptr;
				mask = _mask_ptr + (_xpos>>3);
			}
StartPos:;
		} while (--len);
	} while(1);
}

void CostumeRenderer::proc2() {
	byte *mask,*src,*dst;
	byte maskbit,len,height,pcolor,width;
	int color,t;
	uint y;
	
	mask = _mask_ptr_dest;
	dst = _backbuff_ptr;
	height = _height2;
	width = _width2;
	len = _replen;
	color = _repcolor;
	src = _srcptr;
	maskbit = revBitMask[_xpos&7];
	y = _ypos;

	if (_docontinue) goto StartPos;

	do {
		len = *src++;
		color = len>>_shrval;
		len &= _maskval;
		if (!len) len = *src++;
		do {
			if (cost_scaleTable[_scaleIndexY++] < _scaleY) {
				if (color && y < _vscreenheight && !(*mask&maskbit)) {
					pcolor = _palette[color];
					if (pcolor==13)
						pcolor = _transEffect[*dst];
					*dst = pcolor;
				}
				dst += 320;
				mask += 40;
				y++;
			}
			if (!--height) {
				if(!--width)
					return;
				height = _height;
				y = _ypostop;
				_scaleIndexY = _scaleIndexYTop;
				t = _scaleIndexX;
				_scaleIndexX = t + _scaleIndexXStep;
				if (cost_scaleTable[t] < _scaleX) {
					_xpos += _scaleIndexXStep;
					if (_xpos >= 320)
						return;
					maskbit = revBitMask[_xpos&7];
					_backbuff_ptr += _scaleIndexXStep;
				}
				dst = _backbuff_ptr;
				mask = _mask_ptr + (_xpos>>3);
			}
StartPos:;
		} while (--len);
	} while(1);

}

void CostumeRenderer::proc1() {
	byte *mask,*src,*dst,*dstorg;
	byte maskbit,len,height,pcolor,width;
	uint y;
	int color;
	int t;

	mask = _mask_ptr = _mask_ptr_dest;
	maskbit = revBitMask[_xpos&7];
	y = _ypos;

	dstorg = dst = _backbuff_ptr;
	height = _height2;
	width = _width2;
	len = _replen;
	color = _repcolor;
	src = _srcptr;

	if (_docontinue) goto StartPos;

	do {
		len = *src++;
		color = len>>_shrval;
		len &= _maskval;
		if (!len) len = *src++;
		
		do {
			if (cost_scaleTable[_scaleIndexY++] < _scaleY) {
				if (color && y < _vscreenheight) {
					pcolor = _palette[color];
					if (pcolor==13)
						pcolor = _transEffect[*dst];
					*dst = pcolor;
				}
				dst += 320;
				y++;
			}
			if (!--height) {
				if(!--width)
					return;
				height = _height;
				y = _ypostop;
				_scaleIndexY = _scaleIndexYTop;
				t = _scaleIndexX;
				_scaleIndexX = t + _scaleIndexXStep;
				if (cost_scaleTable[t] < _scaleX) {
					_xpos += _scaleIndexXStep;
					if (_xpos >= 320)
						return;
					_backbuff_ptr += _scaleIndexXStep;
				}
				dst = _backbuff_ptr;
			}
StartPos:;
		} while (--len);
	} while(1);
}

void CostumeRenderer::loadCostume(int id) {
	_ptr = _vm->getResourceAddress(3, id);
	
	if (_vm->_majorScummVersion == 6) {
		_ptr += 8;
	} else {
		_ptr += 2;
	}

	switch(_ptr[7]&0x7F) {
	case 0x58:
		_numColors = 16;
		break;
	case 0x59:
		_numColors = 32;
		break;
	case 0x60: /* New since version 6 */
		_numColors = 16;
		break;
	case 0x61: /* New since version 6 */
		_numColors = 32;
		break;
	default:
		error("Costume %d is invalid", id);
	}

	_dataptr = _ptr + READ_LE_UINT16(_ptr + _numColors + 8);
}

byte CostumeRenderer::drawOneSlot(Actor *a, int slot) {
	int i;
	int code;
	CostumeData *cd = &a->cost;

	if (cd->a[slot]==0xFFFF || cd->hdr & (1<<slot))
		return 0;
	
	i = cd->a[slot]&0x7FFF;
	_frameptr = _ptr + READ_LE_UINT16(_ptr + _numColors + slot*2 + 10);
	code = _dataptr[i]&0x7F;
	_srcptr = _ptr + READ_LE_UINT16(_frameptr + code*2);

	if (code != 0x7B) {
		return mainRoutine(a, slot, code);
	}

	return 0;
}

byte CostumeRenderer::drawCostume(Actor *a) {
	int i;
	byte r = 0;

	_xmove = _ymove = 0;
	for (i=0; i<16; i++)
		r|=drawOneSlot(a, i);
	return r;
}

byte CostumeRenderer::animateOneSlot(Actor *a, int slot) {
	int highflag;
	int i,end;
	byte code,nc;

	if (a->cost.a[slot]==0xFFFF)
		return 0;

	highflag = a->cost.a[slot]&0x8000;
	i = a->cost.a[slot]&0x7FFF;
	end = a->cost.c[slot];
	code=_dataptr[i]&0x7F;

	do {
		if (!highflag) {
			if (i++ >= end)
				i = a->cost.b[slot];
		} else {
			if (i != end)
				i++;
		}

		nc = _dataptr[i];

		if (nc==0x7C) {
			a->cost.animCounter1++;
			if(a->cost.b[slot] != end)
				continue;
		} else {
			if (_vm->_majorScummVersion == 6) {
				if (nc>=0x71 && nc<=0x78) {
					_vm->addSoundToQueue2(a->sound[nc-0x71]);
					if(a->cost.b[slot] != end)
						continue;
				}
			} else {
				if (nc==0x78) {
					a->cost.animCounter2++;
					if(a->cost.b[slot] != end)
						continue;
				}
			}
		}

		a->cost.a[slot] = i|highflag;
		return (_dataptr[i]&0x7F) != code;
	} while(1);
}

byte CostumeRenderer::animate(Actor *a) {
	int i;
	byte r = 0;

	for (i=0; i<16; i++) {
		if(a->cost.a[i]!=0xFFFF)
			r+=animateOneSlot(a, i);
	}
	return r;
}

