#ifdef _WIN32
#include <windows.h>
#endif
#include "DespairPal.h"
#include "jclib.h"
#include "llscreen.h"
//#include "tinyptc/tinyptc.h"
#include "main.h"

PUBLIC void VGA_SetMode(byte mode)
{
    assert(!"NYI");
}

void DRW_DoHorizontalDraw(byte *dest, const byte *data, int skip, int width)
{
    assert(!"NYI");
}

void RenderScan(byte *dest, byte *src, dword np)
{
	int i;
	int sum;
	for (i=0;i<np;i++) {
		sum = dest[i]+src[i];
		if (sum>127)
			sum = 127;
		dest[i]=sum;
	}
#ifdef USE_ASM
  __asm
  {
    mov edi, dest
    mov esi, src
    mov ecx, np
    pushad
  LABELl:	
    xor	eax,eax
	  mov	bl,[esi]
	  inc	esi
	  mov	al,[edi]
	  add	al,bl
	  cmp	eax,127
	  ja	LABELcorrect
	  mov	[edi],al
	  inc	edi
	  dec	ecx
	  jnz	LABELl
	  popad
	  jmp LABELEND

  LABELcorrect:
	  mov	[byte ptr edi],127
	  inc	edi
	  dec	ecx
	  jnz	LABELl
	  popad

LABELEND:
	  
  }
#endif
}

void DRW_DoVerticalDraw(byte *dest, const byte *data)
{
    assert(!"NYI");
}

void DRW_DoVerticalDraw1(byte *dest, const byte *data)
{
    assert(!"NYI");
}

void DRW_DoVerticalDraw2(byte *dest, const byte *data)
{
    assert(!"NYI");
}

void DRW_DoVerticalDraw3(byte *dest, const byte *data)
{
    assert(!"NYI");
}

void DRW_DoVerticalDraw4(byte *dest, const byte *data)
{
    assert(!"NYI");
}

void DRW_DoVerticalDraw640(byte *dest, const byte *data)
{
  assert(!"NYI");
}

void DRW_DoTransparentDraw(byte *dest, const byte *data)
{
  assert(!"NYI");
}

void DRW_DoTransparentDraw640(byte *dest, const byte *data)
{
  assert(!"NYI");
}

PUBLIC void  LLK_Init(void)
{
  return;
}

PUBLIC void  LLK_End(void)
{
  return;
}



//extern HWND wnd;
void InitMusicSystem(void)
{
  //if (BASS_Init(-1, 44100, 0, wnd, 0)!=TRUE)
  if (BASS_Init(-1, 44100, 0, 0, 0)!=TRUE)
  {
    exit(0);
    return;
  }

  BASS_Start();
}

void DoneMusicSystem(void)
{
  BASS_Free();
}

byte* Music;
HMUSIC hMusic;
void InitMusic(const char *fname)
{
  dword dwSize;
  FILE* f;

  dwSize = JCLIB_FileSize(fname);
  if (!dwSize)
  {
      exit(0);
  }


  Music = (byte*) malloc(dwSize);

  f = JCLIB_Open(fname);
   
  if (fread(Music, dwSize, 1, f) != 1)
  {
      exit(0);
  }

  JCLIB_Close(f);

  hMusic = BASS_MusicLoad(TRUE, Music, 0, dwSize, 0, 0); //TODO: confirm freq
  //BASS_MusicPlay(hMusic);
  BASS_ChannelPlay(hMusic, TRUE);
  BASS_ErrorGetCode();
}

bool IsMusicDone = 0;
void DoneMusic(void)
{
  BASS_Stop();
  BASS_Free();
  IsMusicDone = 1;
}

void PollMusic(void)
{
  assert(!"NYI");
}

void CallMusicHandle(void)
{
  assert(!"NYI");
}

bool IsMusicPlaying(void)
{
  //assert(!"NYI");
  return IsMusicDone?FALSE:TRUE;
}

bool IsThereMusic(void)
{
  assert(!"NYI");
  return FALSE;
}

int GetMusicPattern(void)
{
  QWORD qw;
  
  qw = BASS_ChannelGetPosition(hMusic, BASS_POS_MUSIC_ORDER);
    
  return LOWORD(qw)+1;
}

int GetMusicPos(void)
{
  QWORD qw;
  
  qw = BASS_ChannelGetPosition(hMusic,BASS_POS_MUSIC_ORDER);
  
  return HIWORD(qw)+1;
}

void SetMusicPattern(int pattern)
{
  BASS_ChannelSetPosition(hMusic, pattern, BASS_POS_MUSIC_ORDER);    
}

void SetMusicPos(int pos)
{
}



dword dwStartTime;
PUBLIC void TIMER_Init(dword speed)
{
  dwStartTime = SDL_GetTicks();
}

PUBLIC dword TIMER_GetTime()
{
  dword dwNewTime = SDL_GetTicks();
  return (dword) (70.0f*(float)(dwNewTime - dwStartTime)/1000.0f);
}

PUBLIC void TIMER_SetSpeed(dword speed)
{
  assert(!"NYI");
}

PUBLIC void TIMER_End(void)
{
}


PUBLIC void VGA_FadePalette   (const byte *src,  byte *dst, int n, const byte *target)
{

  int i;
  for (i = 0 ; i < n*3 ; i++)
  {
    if (dst[i] != target[i])
    {
      if (dst[i] < target[i])
      {
        dst[i]++;
      }
      else
      {
        dst[i]--;
      }
    }
  }

  
}

extern byte *FB_Texture;
int count;
void FB_Draw320x200(byte *dest, int n, sint32 x, sint32 y, sint32 _dx, sint32 dy)
{
	sint32 c_eax,c_ebx,c_ecx, c_al;
	int i;

	c_ebx = y;
	c_ecx = x;

	for (i=0; i <n;i++) {
	c_eax = c_ebx>>24;	//MOV     EAX,EBX;
					//SHR     EAX,24
	c_ebx += dy;		//ADD     EBX,EDX
	__asm {
		mov eax, [c_eax]
		mov ecx, [c_ecx]
		shld eax,ecx,8
		mov [c_eax],eax
	}
	//	c_eax <<= 8;	//SHLD    EAX,ECX,8
	//	c_eax |= (c_ecx & 0xff);
	c_ecx += _dx;			//ADD     ECX,EBP
	c_al = FB_Texture[c_eax]; //MOV     AL,[ESI+EAX]
	c_al += dest[0];		//ADD     AL,[EDI+0]
#if 0
	__asm {
		mov eax,[c_al];
		rcr al,1;
		mov [c_al],eax
	}
#else
	c_al /= 2;			//RCR     AL,1
#endif
	dest[0] = c_al;		//MOV     [EDI+0],AL
	dest++;
	}
	//edi = dest;
	//esi = n;
	//eax = x;
	//ebx = y;
	//ecx = _dx;
	//edx = dy;
	//esi/=4; (count)
	//esi = FB_Texture;
 return;
#ifdef USE_ASM
  
  __asm
  {
    mov     edi, dest
    mov     esi, n
    mov     eax, x
    mov     ebx, y
    mov     ecx, _dx
    mov     edx, dy
    PUSH    EBP
    SHR     ESI,2
    MOV     count,ESI
    MOV     ESI,FB_Texture
    MOV     EBP,ECX
    MOV     ECX,EAX

     LABELl:
          MOV     EAX,EBX
                  ADD     EBX,EDX
                  SHR     EAX,24
                  SHLD    EAX,ECX,8
                  ADD     ECX,EBP
                  MOV     AL,[ESI+EAX]
                  ADD     AL,[EDI+0]
                  RCR     AL,1
                  MOV     [EDI+0],AL

          MOV     EAX,EBX
                  ADD     EBX,EDX
                  SHR     EAX,24
                  SHLD    EAX,ECX,8
                  ADD     ECX,EBP
                  MOV     AL,[ESI+EAX]
                  ADD     AL,[EDI+1]
                  RCR     AL,1
                  MOV     [EDI+1],AL

          MOV     EAX,EBX
                  ADD     EBX,EDX
                  SHR     EAX,24
                  SHLD    EAX,ECX,8
                  ADD     ECX,EBP
                  MOV     AL,[ESI+EAX]
                  ADD     AL,[EDI+2]
                  RCR     AL,1
                  MOV     [EDI+2],AL

          MOV     EAX,EBX
                  ADD     EBX,EDX
                  SHR     EAX,24
                  SHLD    EAX,ECX,8
                  ADD     ECX,EBP
                  MOV     AL,[ESI+EAX]
                  ADD     AL,[EDI+3]
                  RCR     AL,1
                  MOV     [EDI+3],AL
          
          ADD     EDI,4
          DEC     count
          JNZ     LABELl

          POP     EBP
  }
#endif
}


void RenderBandScan(byte *dest, int np, int value)
{
	//ABPORT: This is my educated guess as to what this might do.
	//I think it just does a add of "value" to everything in dest, one byte at a time.
 int i;
 int iv;
 iv = 0;
 
 for (i=0;i<np;i++) {
	dest[i] += value&0xff; 
 }
 return;
#ifdef USE_ASM
  __asm
  {
    mov   edi, dest
    mov   ecx, np
    mov   ebx, value

  LABELl1:
	  test	edi,3
	  jz	LABELalign

	  xor	eax,eax
	  mov	al,[edi]
	  add	al,bl
	  mov	[edi],al
	  inc	edi
	  dec	ecx
	  jnz	LABELl1
	  jmp LABELret

  LABELalign:
	  cmp	ecx,8
	  jb	LABELbypels

	  push	ecx
	  shr	ecx,3
	  push	edx
  LABELl4:
	  mov	eax,[edi]
	  mov	edx,[edi+4]
	  add	eax,ebx
	  add	edx,ebx
	  and	eax,0F0F0F0F0h
	  and	edx,0F0F0F0F0h
	  mov	[edi],eax
	  mov	[edi+4],edx
	  add	edi,8
	  dec	ecx
	  jnz	LABELl4

	  pop	edx

	  pop	ecx
	  and	ecx,7
	  jz	LABELret

  LABELbypels:
  LABELl:	xor	eax,eax
	  mov	al,[edi]
	  add	al,bl
	  mov	[edi],al
	  inc	edi
	  dec	ecx
	  jnz	LABELl
  LABELret:
	}
#endif
}


void LLV_PutBuffer(const byte *org, int miny, int maxy)
{
  assert(!"NYI");
}

void AADump(void * v)
{
	//ABPORT: I think this just does a blurred-copy of the original image buffer
//return; 
  int i,x,y;
  int cns = (64000-4*321)/2;
  byte* p = Mode13;
  byte *asmesi = v;
  int average;
  /*
  for (i=0;i<320*200;i++) {
	p[i] = asmesi[i];
  }*/
  for (y=0;y<200;y++)
	  for (x=0;x<320;x++) {
		  if ((y!=0)&&(x!=0)&&(y!=199)&&(x<319)) {
			   average = 0;
			   average += asmesi[0+x+y*320];
			   average += asmesi[1+x+y*320];
			   average += asmesi[320+x+y*320];
			   average += asmesi[321+x+y*320];
				average /= 4;
			   p[x+y*320] = average;
		  } else {
			  p[x+y*320] = asmesi[x+y*320];
		  }
	  }
#if 0
  __asm
  {
    mov esi, v
    pushad
	mov	edi,p
	mov	ebp,cns
	xor	eax,eax
	xor	ebx,ebx
	xor	ecx,ecx
	xor	edx,edx
LABELloop:
	mov	al,[esi]

	mov	cl,[esi+1]
	mov	bl,[esi+320]

	add	eax,ecx
	mov	dl,[esi+321]

	add	eax,ebx
	add	ecx,edx

	add	eax,edx
	mov	dl,[esi+2]

	shr	eax,2
	add	ecx,edx

	mov	[edi],al
	mov	dl,[esi+322]

	add	ecx,edx
	add	esi,2

	shr	ecx,2
	dec	ebp

	mov	[edi+1],cl
	lea	edi,[edi+2]

	jnz	LABELloop

	popad
	}
#endif
}

dword ExtTrueColorBackBuffer[320*200];
bool  useTrueColorBackBuffer = 0;
dword BackBuffer[640*480];
byte Mode13[640*480];
dword Palette[256];

extern void VGA_PutColor(int c, int r, int g, int b)
{
  Palette[c] = (r<<18) | (g<<10) | (b<<2);
}


void LLS_UpdateVGA(void)
{
  memcpy(Mode13, LLS_Screen_[0][0], 64000);
  //assert(!"NYI");
}


PUBLIC void VGA_DumpPalette   (const byte *src,  int start, int n)
{
  int i;
  for (i = 0 ; i < 256 ; i++)
  {
    Palette[i] = (src[i*3]<<18) | (src[i*3+1]<<10) | (src[i*3+2]<<2);
  }    
}

void LLS_DUMP(void)
{
	SDL_PixelFormat *fmt;
  int i,j,offset;
  int yofs, ofs;
  byte* p = LLS_Screen_;
  dword d,buf;
  dword* pd;
    dword *pdst;
    char *pdstc;
    char *psrcc;

  if (LLS_SizeX == 320 && LLS_SizeY == 200)
  {
    p = Mode13;
  }

  memset(BackBuffer, 0, 640*40*4); 
  memset(BackBuffer+640*480-640*40, 0, 640*40*4); 
  
  #define START 640*40
  if (useTrueColorBackBuffer)
  {
    pd = ExtTrueColorBackBuffer;
    for (j = 0 ; j < 200 ; j++)
    {
      offset = 640*j*2 + START;

        
    
      for (i = 0 ; i < 320 ; i++)
      {
        
      assert((pd[i] & 0xFF000000) == 0);
      BackBuffer[offset+i*2] = pd[i];
      BackBuffer[offset+i*2+1] = pd[i];
      }

      for (i = 0 ; i < 320 ; i++)
      {
        
        BackBuffer[offset+i*2+640] = pd[i];
        BackBuffer[offset+i*2+1+640] = pd[i];
      }
    
      pd += 320;
    }

      
  }
  else
  {
    for (i = 0 ; i < 256 ; i++)
    {
      //Palette[i] = i;
    }

    if (LLS_SizeX == 320 && LLS_SizeY == 200)
    {
      for (j = 0 ; j < 200 ; j++)
      {
        offset = 640*j*2 + START;
        for (i = 0 ; i < 320 ; i++)
        {
          d = Palette[p[i]];        
          BackBuffer[offset+i*2] = d;
          BackBuffer[offset+i*2+1] = d;
        }

        for (i = 0 ; i < 320 ; i++)
        {    
          d = Palette[p[i]];       
          BackBuffer[offset+i*2+640] = d;
          BackBuffer[offset+i*2+641] = d;
        }

        p+=320;
      
      }
    
    }
    else
    {
      for (i = 0 ; i < 640*480 ; i++)
      {
        BackBuffer[i] = Palette[p[i]];
      }    
    }
  }

  // Lock surface if needed
  if (SDL_MUSTLOCK(screen)) 
	  if (SDL_LockSurface(screen) < 0) 
		  return;
  // Draw to screen
  fmt = screen->format;
  yofs = 0;
  for (i = 0; i < 480; i++) {
	  for (j = 0, ofs = yofs; j < 640; j++, ofs++) {
            pdst = &((dword*)screen->pixels)[ofs];
            pdstc = (char *)pdst;
			psrcc = (char *) &BackBuffer[i*640+j];
			buf = SDL_MapRGB( fmt, psrcc[2], psrcc[1], psrcc[0] );
			*pdst = buf;			
			if (g_bgr) {
			pdstc[0] = psrcc[0];
			pdstc[1] = psrcc[1];
			pdstc[2] = psrcc[2];
			pdstc[3] = psrcc[3];
			}
			if (g_rgb) {
			pdstc[0] = psrcc[3];
			pdstc[1] = psrcc[2];
			pdstc[2] = psrcc[1];
			pdstc[3] = psrcc[0];
			}
	  }
	  yofs += screen->pitch / 4;
  }

  // Unlock if needed
  if (SDL_MUSTLOCK(screen)) 
	  SDL_UnlockSurface(screen);

  // Tell SDL to update the whole screen
  SDL_UpdateRect(screen, 0, 0, 640, 480);
  //ptc_update(BackBuffer);


}

bool LLS_Init(int mode, int vmode) 
{

    if (vmode != LLS_VMode) 
    {
      switch (vmode) 
      {
        case LLSVM_MODE13:
          LLS_SizeX = 320;
          LLS_SizeY = 200;

          break;
        case LLSVM_640x480x256:
          LLS_SizeX = 640;
          LLS_SizeY = 480;
          break;

        default:
          assert(!"NYI");
     
      }

      LLS_UpdateMinY = 0;
      LLS_UpdateMaxY = LLS_SizeY;
      LLS_Size = LLS_SizeX*LLS_SizeY;
      LLS_VMode = vmode;
      DISPOSE(LLS_Virtual1);
      DISPOSE(LLS_Virtual2);
    }

    LLS_UpdateMinY = 0;
    LLS_UpdateMaxY = LLS_SizeY;
    LLS_Size = LLS_SizeX*LLS_SizeY;
    LLS_VMode = vmode;
    DISPOSE(LLS_Virtual1);
    DISPOSE(LLS_Virtual2);
    switch(mode) {
        case LLSM_DIRECT:
            LLS_Screen_ = (*((TVGA200x320*)&(Mode13[0])));
            break;
        case LLSM_VIRTUAL:
            REQUIRE((LLS_Virtual1 = NEW(LLS_Size)) != NULL);
            memset(LLS_Virtual1, 0, LLS_Size);
            LLS_Screen_ = LLS_Virtual1;
            break;
        case LLSM_ADDITIVE:
            REQUIRE((LLS_Virtual1 = NEW(LLS_Size)) != NULL);
            REQUIRE((LLS_Virtual2 = NEW(LLS_Size)) != NULL);
            memset(LLS_Virtual1, 0, LLS_Size);
            memset(LLS_Virtual2, 0, LLS_Size);
//LLS_Virtual2 = 0xA0000;
            LLS_Screen_ = LLS_Virtual1;
            break;
    }
         
    return TRUE;
}
    