/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/** 
 *  @file   fugenPrinter.cpp
 *  @brief  Implementation for class fugenPrinter.
 */
#include "stdafx.h"

#include "fugenDoc.h"
#include "fugenView.h"
#include "Misc/fugenPrinter.h"
#include "mg/PickObjects.h"
#include "mgGL/Color.h"
#include "mgGL/OpenGLView.h"
#include "mgGL/glslProgram.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

fugenPrinter::fugenPrinter():m_pView(0){
}

fugenPrinter::~fugenPrinter(){
}

void fugenPrinter::setUpDIB(
	const CRect& rcClient,///<display target windowのCRectを入力する
	CDC* pDC,///<PrinterのDC
	CPrintInfo* pInfo
){
	ASSERT_VALID(pDC);
	ASSERT(pInfo);

	// Get page size.
	m_szPage.cx = pDC->GetDeviceCaps(HORZRES);
	m_szPage.cy = pDC->GetDeviceCaps(VERTRES);

	CSize szDIB;
	if(pInfo->m_bPreview){
		// Use screen resolution for preview.
		szDIB.cx = rcClient.Width();
		szDIB.cy = rcClient.Height();
		m_fScreenScale = 1.0f;
	}else{	// Printing
		float fClientRatio = static_cast<float>(rcClient.Height())/rcClient.Width();
		// Use higher resolution for printing.
		// Adjust size according screen's ratio.
		if(m_szPage.cy > fClientRatio*m_szPage.cx){
			// View area is wider than Printer area.
			// 平たく言うと用紙が縦長
			szDIB.cx = m_szPage.cx;
			szDIB.cy = static_cast<long>(fClientRatio*m_szPage.cx);
			m_fScreenScale = (float)m_szPage.cx / rcClient.Width();
		}else{
			// View area is narrower than Printer area.
			// 用紙が横長
			szDIB.cx = static_cast<long>(static_cast<float>(m_szPage.cy)/fClientRatio);
			szDIB.cy = m_szPage.cy;
			m_fScreenScale = (float)m_szPage.cy / rcClient.Height();
		}
		// Reduce the resolution if the Bitmap size is too big.
		// Adjust the maximum value, which is depends on printer's memory.
		// I use 20 MB. 
		while (szDIB.cx * szDIB.cy > 20e6){
			szDIB.cx /= 2;
			szDIB.cy /= 2;
		}
	}
	// Note that (szDIB.cy / szDIB.cx == fClientRatio) is true.
	// fugenViewのクライアント部分とszDIB四角形の相似比は次の3通り考えられる:
	// 1. 印刷プレビューモードのときは必ず 1 : 1
	// 2. 印刷本番で用紙が縦長の場合は     rcClient.Width() : m_szPage.cx
	// 3. 印刷本番で用紙が横長の場合は     rcClient.Height() : m_szPage.cy

	if(!m_dibImpl.Create(pDC->GetSafeHdc(), szDIB)){
		m_dibImpl.Clear();
		return;
	}
}

void fugenPrinter::endUpDIB(fugenView* pView){
	m_dibImpl.Clear();// Release memory.
	pView->make_RC_current();
}

void fugenPrinter::PostRenderScene(fugenView* pView, CDC* pDC, CPrintInfo* pInfo){
	ASSERT(pView == m_pView);

	// Calculate the target size according to the image size, 
	// and orientation of the paper.
	float fBmiRatio = static_cast<float>(m_dibImpl.GetBmpHeight()) / m_dibImpl.GetBmpWidth();
	CSize szTarget;
	if (m_szPage.cx > m_szPage.cy){	 // Landscape page
		if(fBmiRatio<1){	  // Landscape image
			szTarget.cx = m_szPage.cx;
			szTarget.cy = static_cast<long>(fBmiRatio * m_szPage.cx);
		}else{			  // Portrait image
			szTarget.cx = static_cast<long>(m_szPage.cy/fBmiRatio);
			szTarget.cy = m_szPage.cy;
		}
	}else{		    // Portrait page
		if(fBmiRatio<1){	  // Landscape image
			szTarget.cx = m_szPage.cx;
			szTarget.cy = static_cast<long>(fBmiRatio * m_szPage.cx);
		}else{			  // Portrait image
			szTarget.cx = static_cast<long>(m_szPage.cy/fBmiRatio);
			szTarget.cy = m_szPage.cy;
		}
	}

	// Write the document's name.
	CString filename(m_pView->GetDocument()->GetTitle());

	// TODO: 英語版対応
	// MS ゴシックを利用する。
	// タイトルが日本語文字を含むと文字化けを引き起こすため。
	CString strFont;
	VERIFY(strFont.LoadString(IDS_MS_GOTHIC));
	CFont font;
	VERIFY(font.CreatePointFont(100, strFont, pDC));

	// 旧フォントを一時退避
	const int nSaved = pDC->SaveDC();
	// 新フォントを設定
	pDC->SelectObject(&font);

	// テキストのサイズをあらかじめ算定して、描画する。
	CSize sz(pDC->GetTextExtent(filename));
	ASSERT(sz.cx <= m_szPage.cx);
	pDC->TextOut((m_szPage.cx-sz.cx)/2, sz.cy, filename);

	// 旧フォントを復帰し、作業用リソースを削除する。
	pDC->RestoreDC(nSaved);
	font.DeleteObject();

	CSize szOffset;
	szOffset.cx = (m_szPage.cx - szTarget.cx) / 2;
	szOffset.cy = (m_szPage.cy - szTarget.cy) / 2 + 3 * sz.cy;

	// Stretch image to fit in the target size.
	m_dibImpl.StretchDIBits(pDC->GetSafeHdc(), szOffset, szTarget);
}

/////////////////////////////////////////////////////////////////////////////////
//

void fugenPrinter::OnBeginPrinting(
	fugenView* pView,///<print対象の画面
	CDC* pDC,///<PrinterのDC
	CPrintInfo* pInfo
){
	if(!pInfo)
		return;

	ASSERT_VALID(pView);
	ASSERT_VALID(pDC);
	m_dibImpl.Clear();

	// Determine the DIB size for either printing or print preview.
	CRect rcClient;
	pView->GetClientRect(rcClient);

	setUpDIB(rcClient,pDC,pInfo);
}

void fugenPrinter::OnEndPrinting(fugenView* pView, CDC* pDC, CPrintInfo* pInfo){
	ASSERT_VALID(pView);
	ASSERT_VALID(pDC);
	ASSERT(pInfo);
	endUpDIB(pView);
}

void fugenPrinter::OnEndPrintPreview(
	fugenView* pView,
	CDC*           pDC,
	CPrintInfo*    pInfo,
	POINT          point,
	CPreviewView*  pPreviewView
){
	ASSERT_VALID(pView);
	ASSERT_VALID(pDC);
	ASSERT(pInfo);
	ASSERT(pPreviewView);
}

void fugenPrinter::OnPrepareDC(fugenView* pView, CDC* pDC, CPrintInfo* pInfo){
}

BOOL fugenPrinter::OnPreparePrinting(fugenView* pView, CPrintInfo* pInfo){
	ASSERT_VALID(pView);
	ASSERT(pInfo);

	pInfo->SetMinPage(1);
	pInfo->SetMaxPage(1);
	do{
		// Check if it is canceled.
		if(pView->DoPreparePrinting(pInfo) == FALSE){
			return FALSE;
		}
		// Prevent wasting of paper.
		if(pInfo->m_pPD->GetCopies() > 5){
			CString str;
			VERIFY(str.LoadString(IDS_WARNING_TOO_MANY_COPIES));
			AfxMessageBox(str);
		}
	}while(pInfo->m_pPD->GetCopies() > 5);
	return TRUE;
}

void fugenPrinter::OnPrint(fugenView* pView, CDC* pDC, CPrintInfo* pInfo){
	ASSERT_VALID(pView);
	ASSERT_VALID(pDC);
	ASSERT(pInfo);

	MGOpenGLView& originalGLView=*m_pView;
	int w,h;
	originalGLView.get_window(w,h);//Save window
	float orgDPI = originalGLView.get_output_dpi();

	pView->make_RC_current();
	//(1) originalなMGOpenGLViewで実装した例
	m_dibImpl.beginRender();
	originalGLView.set_window(m_dibImpl.GetBmpWidth(), m_dibImpl.GetBmpHeight());

	float dpiRatio = orgDPI*m_dibImpl.GetBmpWidth()/(float)w;
	originalGLView.set_output_dpi(dpiRatio );
	originalGLView.drawScene();
	
	/*//（２）次のコードはlocalなMGOpenGLView変数での実装例。
	//（１）とsysglを除いて同一なものを表示する
	MGOpenGLView printerGLView;
	printerGLView.copy(originalGLView);
	printerGLView.set_parent_OpenGLView(&originalGLView);
	printerGLView.make_RC_current();
	
	m_dibImpl.beginRender();
	printerGLView.set_window(m_dibImpl.GetBmpWidth(), m_dibImpl.GetBmpHeight());
	printerGLView.drawScene();*/

	m_dibImpl.endRender();
	originalGLView.set_window(w,h);//Resotre window.
	originalGLView.set_output_dpi(orgDPI);

	pView->release_dc();
	PostRenderScene(pView, pDC, pInfo);
}

void fugenPrinter::set_owner_COpenGLWindow(fugenView* pView){//Set the owner of this fugenPrinter.
	m_pView = pView;
}
