// cDDPicture.cpp: implementation of the cDDPicture class.
//
//////////////////////////////////////////////////////////////////////

#include "cPicture.h"
#include "cDisplay.h"

cDisplay * cPicture::pDisplay = NULL;
LPDIRECTDRAW cPicture::pDD = NULL;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

cPicture::cPicture(){
	pDDS = NULL;
	pMedium = NULL;
	SurfaceType = ST_NONE;
}

cPicture::~cPicture(){
	Destroy();
}

//////////////////////////////////////////////////////////////////////
// Setting Display
//////////////////////////////////////////////////////////////////////

void cPicture::SetDisplay(cDisplay *_pDisplay){
	pDisplay = _pDisplay; 
	pDD = *pDisplay; 
}


//////////////////////////////////////////////////////////////////////
// 표면 생성
//////////////////////////////////////////////////////////////////////
BOOL cPicture::Create(int BackBufferCount){
	DDSURFACEDESC ddsd;

	ddsd.dwSize = sizeof(DDSURFACEDESC);

	if(BackBufferCount > 0){
		ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
		ddsd.dwBackBufferCount = BackBufferCount;
	}else{
		ddsd.dwFlags = DDSD_CAPS;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	}

	if(pDD->CreateSurface(&ddsd, &pDDS, NULL) != DD_OK) 
		return false;
	SurfaceType = ST_MEMORY_SURFACE;
	return true;
}


BOOL cPicture::Create(DWORD Width, DWORD Height){
	DDSURFACEDESC ddsd; 

	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;  
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; 
	ddsd.dwWidth = Width;
	ddsd.dwHeight = Height; 

	if(pDD->CreateSurface(&ddsd, &pDDS, NULL) != DD_OK) 
		return false;
	SurfaceType = ST_MEMORY_SURFACE;
	return true;
}


BOOL cPicture::Create(cPicture &Src){
	DDSURFACEDESC ddsd;
	
	ddsd.dwSize = sizeof(DDSURFACEDESC);

	if(Src.GetDDS()->GetSurfaceDesc(&ddsd) != DD_OK)
		return false;

	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; 

	if(pDD->CreateSurface(&ddsd, &pDDS, NULL) != DD_OK)
		return false;
	SurfaceType = ST_MEMORY_SURFACE;
	return true;
}

BOOL cPicture::CreateReference(cPicture &Src, int Left, int Top, int Right, int Bottom){
	pDDS = Src.pDDS;
	SetRect(&Rect, Left, Top, Right, Bottom);
	SurfaceType = ST_PIECE_SURFACE; // 그림의 종류
	return TRUE;
}

void cPicture::SetArea(LPDIRECTDRAWSURFACE _pDDS, RECT *pRect){
	// 다이렉트 드로우 표면의 일부를 그림으로 한다.
	pDDS = _pDDS;
	pDDS->AddRef();
	Rect = *pRect;
}

// 그림 파일로 부터 표면을 생성해 낸다.
BOOL cPicture::Create(cMedium *_pMedium){
	HDC hdcSurface, hdcBitmap;
	HBITMAP hBitmap;
	BITMAP BitmapInfo;

	if(_pMedium) pMedium = _pMedium;
	if(!pMedium) return false;

	hBitmap = (HBITMAP) LoadImage(NULL, pMedium->GetFileName(), IMAGE_BITMAP,
		0, 0, LR_LOADFROMFILE);
	if(hBitmap == NULL) return false;

	// 비트맵의 정보를 읽어 온다.
	if(!GetObject(hBitmap, sizeof(BITMAP), &BitmapInfo)){
		DeleteObject(hBitmap);
		return false;
	}

	// 비트맵에 맞추어 표면을 새로 만든다.
	Destroy();
	if(!Create(BitmapInfo.bmWidth, BitmapInfo.bmHeight)){
		DeleteObject(hBitmap);
		return false;
	}
	// 비트맵을 표면으로 복사한다.
	if(pDDS->GetDC(&hdcSurface) != DD_OK){
		DeleteObject(hBitmap);
		return false;
	}
	hdcBitmap = CreateCompatibleDC(hdcSurface);
	SelectObject(hdcBitmap, hBitmap);
	BitBlt(hdcSurface, 0, 0, BitmapInfo.bmWidth, BitmapInfo.bmHeight, 
		hdcBitmap, 0, 0, SRCCOPY);
	DeleteDC(hdcBitmap);
	pDDS->ReleaseDC(hdcSurface);
	DeleteObject(hBitmap);
	SurfaceType = ST_PICTURE_SURFACE;

	DDCOLORKEY ck;

	ck.dwColorSpaceLowValue = 0;
    ck.dwColorSpaceHighValue = 0;
	pDDS->SetColorKey(DDCKEY_SRCBLT/* | DDCKEY_COLORSPACE*/, &ck);

	return true;
}

//////////////////////////////////////////////////////////////////////
// 표면 제거
//////////////////////////////////////////////////////////////////////
void cPicture::Destroy(){
	if(!pDDS) return;

	switch(SurfaceType){
//	case ST_NONE:
	case ST_MEMORY_SURFACE:
	case ST_PICTURE_SURFACE:
		pDDS->Release();
		pDDS = NULL;
	}
	SurfaceType = ST_NONE;
}

BOOL cPicture::GetSize(LPSIZE psz){
	DDSURFACEDESC ddsd;
	
	if(SurfaceType == ST_PIECE_SURFACE){
		psz->cx = GetWidth();
		psz->cy = GetHeight();
		return true;
	}
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	if(!pDDS) return FALSE;
	if(pDDS->GetSurfaceDesc(&ddsd) != DD_OK) return FALSE;
	
	psz->cx = ddsd.dwWidth;
	psz->cy = ddsd.dwHeight;
	return true;

}

int cPicture::GetWidth(){
	DDSURFACEDESC ddsd;

	if(SurfaceType == ST_PIECE_SURFACE){
		return Rect.right - Rect.left;
	}
	
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	if(!pDDS) return FALSE;
	if(pDDS->GetSurfaceDesc(&ddsd) != DD_OK) return FALSE;
	
	return ddsd.dwWidth;
}

int cPicture::GetHeight(){
	DDSURFACEDESC ddsd;
	
	if(SurfaceType == ST_PIECE_SURFACE){
		return Rect.bottom - Rect.top;
	}
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	if(!pDDS) return FALSE;
	if(pDDS->GetSurfaceDesc(&ddsd) != DD_OK) return FALSE;
	
	return ddsd.dwHeight;
}


BOOL cPicture::SetClipWindow(HWND hWnd){
	pDisplay->GetClipper()->SetHWnd(0, hWnd);
	pDDS->SetClipper(pDisplay->GetClipper());
	return TRUE;
}

//////////////////////////////////////////////////////////////////////
// 배경 얻기
//////////////////////////////////////////////////////////////////////
LPDIRECTDRAWSURFACE cPicture::GetBackPic(int Index){
	LPDIRECTDRAWSURFACE pDDSBack;
	DDSCAPS ddscaps;

	ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
	if(pDDS->GetAttachedSurface(&ddscaps, &pDDSBack) != DD_OK) 
		return NULL;
	return pDDSBack;
}

//////////////////////////////////////////////////////////////////////
// 복구
//////////////////////////////////////////////////////////////////////
BOOL cPicture::Restore(){
// 잃어버린 표면을 복구한다.
	DDSURFACEDESC ddsd;

//	if(SurfaceType == PIECE_SURFACE) return true;

	if(pDDS->IsLost() != DDERR_SURFACELOST) return true;

	pDisplay->DestroyDoubleBuffer();
	pDisplay->CreateDoubleBuffer();

	if(pDDS->IsLost() != DDERR_SURFACELOST) return true;

	ddsd.dwSize = sizeof(DDSURFACEDESC);

	if(pDDS->GetSurfaceDesc(&ddsd) != DD_OK)
		return false;

	Destroy();

	switch(SurfaceType){
	case ST_MEMORY_SURFACE:
		if(pDD->CreateSurface(&ddsd, &pDDS, NULL) != DD_OK)
			return false;
		break;
	case ST_PICTURE_SURFACE:
		if(pMedium){
			if(!Create(pMedium)) 
				return false;
		}else{
			if(pDD->CreateSurface(&ddsd, &pDDS, NULL) != DD_OK)
				return false;
		}
		break;
	}

	return true;
}


void cPicture::operator=(LPDIRECTDRAWSURFACE _pDDS){
	if(_pDDS == NULL) return;
	Destroy();
	pDDS = _pDDS;
	pDDS->AddRef();
}


BOOL cPicture::Blt(LPRECT lpDestRect, cPicture *pSrc, 
				   LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx){
	RECT rcDest, rcSrc;
	HRESULT hr;

	if(!pDDS) return FALSE;

	if(SurfaceType == ST_PIECE_SURFACE){
		if(lpDestRect){
			rcDest.left = Rect.left + lpDestRect->left;
			rcDest.top = Rect.top + lpDestRect->top;
			rcDest.right = Rect.left + lpDestRect->right;
			rcDest.bottom = Rect.top + lpDestRect->bottom;
			lpDestRect = &rcDest;
		}else{
			lpDestRect = &Rect;
		}
	}
	
	if(pSrc->SurfaceType == ST_PIECE_SURFACE){
		if(lpSrcRect){
			rcSrc.left = pSrc->Rect.left + lpSrcRect->left;
			rcSrc.top = pSrc->Rect.top + lpSrcRect->top;
			rcSrc.right = pSrc->Rect.left + lpSrcRect->right;
			rcSrc.bottom = pSrc->Rect.top + lpSrcRect->bottom;
			lpSrcRect = &rcSrc;
		}else{
			lpSrcRect = &pSrc->Rect;
		}
	}
	

	hr = pDDS->Blt(lpDestRect, *pSrc, lpSrcRect, dwFlags, lpDDBltFx);
	
	if(hr == DDERR_SURFACELOST){
		if(!Restore()){
			MessageBox(NULL, "디스플레이 메모리 복구 실패", NULL, MB_OK);
		}
		if(!pSrc->Restore()){
			MessageBox(NULL, "디스플레이 메모리 복구 실패", NULL, MB_OK);
		}
		pDDS->Blt(lpDestRect, *pSrc, lpSrcRect, dwFlags, lpDDBltFx);
	}

	return hr == DD_OK;
}

BOOL cPicture::BltFast(DWORD x, DWORD y, cPicture *pSrc, 
					   LPRECT lpSrcRect, DWORD dwFlags){
	HRESULT hr;
	hr = pDDS->BltFast(x, y, *pSrc, lpSrcRect, dwFlags);
	if(hr == DDERR_SURFACELOST){
		if(!Restore()){
			MessageBox(NULL, "디스플레이 메모리 복구 실패", NULL, MB_OK);
		}
		if(!pSrc->Restore()){
			MessageBox(NULL, "디스플레이 메모리 복구 실패", NULL, MB_OK);
		}
		pDDS->BltFast(x, y, *pSrc, lpSrcRect, dwFlags);
	}
	return hr == DD_OK;
}
