// C16KPageView.cpp: файл реализации
//

#include "stdafx.h"
#include "BK.h"
#include "16KPageView.h"
#include "SprWnd.h"
#include "Screen.h"
#include "Globals.h"

// C16KPageView

IMPLEMENT_DYNAMIC(C16KPageView, CWnd)

C16KPageView::C16KPageView(BYTE *buffer, size_t size, UINT num, const CString &strName)
	: m_pBuffer(buffer)
	, m_nBufSize(size)
	, m_nPageNum(num)
	, m_strScrName(strName)
{
	//если рендер D3D - то карту памяти выводим, используя DrawDIB
	m_pSprScr = new CScreen((g_Config.m_nScreenRenderType == CONF_SCREEN_RENDER_D3D ? CONF_SCREEN_RENDER_DIB : g_Config.m_nScreenRenderType),
	                        m_pBuffer, m_nBufSize);

	if (m_pSprScr)
	{
		m_bColorMode = m_pSprScr->IsColorMode();
		m_bBWAdaptMode = m_pSprScr->IsAdaptMode();
	}
}

C16KPageView::~C16KPageView()
{
	SAFE_DELETE(m_pSprScr);
}

BEGIN_MESSAGE_MAP(C16KPageView, CWnd)
	ON_WM_CREATE()
	ON_COMMAND_RANGE(ID_BUTTON_16KPV_COLOR_MODE, ID_BUTTON_16KPV_COLOR_MODE_END, &OnViewColormode)
	ON_COMMAND_RANGE(ID_BUTTON_16KPV_BW_MODE, ID_BUTTON_16KPV_BW_MODE_END, &OnViewBWMode)
	ON_COMMAND_RANGE(ID_BUTTON_16KPV_SPRITE, ID_BUTTON_16KPV_SPRITE_END, &OnViewSprite)
	ON_COMMAND_RANGE(ID_BUTTON_16KPV_LOAD, ID_BUTTON_16KPV_LOAD_END, &OnLoadImg)
	ON_COMMAND_RANGE(ID_BUTTON_16KPV_SAVE, ID_BUTTON_16KPV_SAVE_END, &OnSaveImg)
	ON_WM_DESTROY()
END_MESSAGE_MAP()


// обработчики сообщений C16KPageView


int C16KPageView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == - 1)
	{
		return -1;
	}

	CRect rect;
	GetWindowRect(rect);
	int x = rect.left;
	int y = rect.top;
	MoveWindow(x, y, x + SCREEN_WIDTH + VW16K_BUTTONPAD_WIDTH, SCREEN_HEIGHT + VW16K_BUTTON_SPACE, FALSE);
	//смастрячим кнопку смены цветного режима
	rect = CRect(SCREEN_WIDTH + VW16K_BUTTON_X, VW16K_BUTTON_Y, VW16K_BUTTON_WIDTH + SCREEN_WIDTH + VW16K_BUTTON_X, VW16K_BUTTON_HEIGHT + VW16K_BUTTON_Y);
	m_btnColorMode.Create(nullptr,
	                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_CENTER,
	                      rect,
	                      this,
	                      ID_BUTTON_16KPV_COLOR_MODE + m_nPageNum
	                     );
	auto bmpBtn = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_COLOR_MODE_BTN), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);

	if (bmpBtn)
	{
		m_btnColorMode.SetBitmap(bmpBtn);
	}

	//смастрячим кнопку смены ЧБ режима
	rect.top += rect.bottom + VW16K_BUTTON_SPACE;
	rect.bottom = rect.top + VW16K_BUTTON_HEIGHT;
	m_btnBWMode.Create(nullptr,
	                   WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_CENTER,
	                   rect,
	                   this,
	                   ID_BUTTON_16KPV_BW_MODE + m_nPageNum
	                  );
	bmpBtn = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_BW_MODE_BTN), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);

	if (bmpBtn)
	{
		m_btnBWMode.SetBitmap(bmpBtn);
	}

	//смастрячим кнопку открытия окошка спрайтового вида
	rect.bottom = SCREEN_HEIGHT - VW16K_BUTTON_Y;
	rect.top = rect.bottom - VW16K_BUTTON_HEIGHT;
	m_btnSprite.Create(nullptr,
	                   WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_CENTER,
	                   rect,
	                   this,
	                   ID_BUTTON_16KPV_SPRITE + m_nPageNum
	                  );
	bmpBtn = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_SPRITE_BTN), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);

	if (bmpBtn)
	{
		m_btnSprite.SetBitmap(bmpBtn);
	}

	//смастрячим кнопку сохранения
	rect.bottom = rect.top - VW16K_BUTTON_SPACE;
	rect.top = rect.bottom - VW16K_BUTTON_HEIGHT;
	m_btnSave.Create(nullptr,
	                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_CENTER,
	                 rect,
	                 this,
	                 ID_BUTTON_16KPV_SAVE + m_nPageNum
	                );
	bmpBtn = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_SAVE_BTN), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);

	if (bmpBtn)
	{
		m_btnSave.SetBitmap(bmpBtn);
	}

	//смастрячим кнопку загрузки
	rect.bottom = rect.top - VW16K_BUTTON_SPACE;
	rect.top = rect.bottom - VW16K_BUTTON_HEIGHT;
	m_btnLoad.Create(nullptr,
	                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_CENTER,
	                 rect,
	                 this,
	                 ID_BUTTON_16KPV_LOAD + m_nPageNum
	                );
	bmpBtn = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_LOAD_BTN), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);

	if (bmpBtn)
	{
		m_btnLoad.SetBitmap(bmpBtn);
	}

	if (m_pSprScr)
	{
		//размеры - константа 512х256
		if (!m_pSprScr->Create(nullptr, _T("BKScreen_") + m_strScrName,
		                       WS_VISIBLE | WS_CHILD,
		                       CRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
		                       this, 0)
		   )
		{
			TRACE(_T("Не удалось создать экран ") + m_strScrName + _T("\n"));
			return -1; // не удалось создать
		}
	}
	else
	{
		TRACE(_T("Недостаточно памяти для создания ") + m_strScrName + _T("\n"));
		return -1; // не удалось создать
	}

	m_btnBWMode.EnableWindow(!m_bColorMode);
	return 0;
}

void C16KPageView::Draw16KView()
{
	m_pSprScr->ReDrawScreen();
}

void C16KPageView::OnViewColormode(UINT id)
{
	if (id == ID_BUTTON_16KPV_COLOR_MODE + m_nPageNum)
	{
		m_bColorMode = !m_bColorMode;
		m_pSprScr->SetColorMode(m_bColorMode);
		m_btnBWMode.EnableWindow(!m_bColorMode);
		UINT nButtonStyle = m_btnColorMode.GetButtonStyle();
		m_btnColorMode.SetButtonStyle(m_bColorMode ? (nButtonStyle | TBBS_CHECKED) : (nButtonStyle & ~TBBS_CHECKED));
	}
}

void C16KPageView::OnViewBWMode(UINT id)
{
	if (id == ID_BUTTON_16KPV_BW_MODE + m_nPageNum)
	{
		m_bBWAdaptMode = !m_bBWAdaptMode;
		m_pSprScr->SetAdaptMode(m_bBWAdaptMode);
		UINT nButtonStyle = m_btnBWMode.GetButtonStyle();
		m_btnBWMode.SetButtonStyle(m_bBWAdaptMode ? (nButtonStyle | TBBS_CHECKED) : (nButtonStyle & ~TBBS_CHECKED));
	}
}

//!!! было бы неплохо создавать только одно окошко, а при повторном клике
// переводить на него фокус и перерисовывать содержимое
void C16KPageView::OnViewSprite(UINT id)
{
	if (id == ID_BUTTON_16KPV_SPRITE + m_nPageNum)
	{
		auto pSprWnd = new CSprWnd(m_pBuffer, m_nBufSize); //обязательно создавать динамически.

		if (pSprWnd)
		{
			WNDCLASS wc;
			memset(&wc, 0, sizeof(wc));
			wc.style = CS_HREDRAW | CS_VREDRAW;
			wc.hInstance = AfxGetInstanceHandle();
			wc.lpfnWndProc = ::DefWindowProc;
			wc.lpszClassName = _T("MySprClass");
			wc.hbrBackground = (HBRUSH)::GetStockObject(GRAY_BRUSH);
			AfxRegisterClass(&wc);

			if (pSprWnd->Create(
			            _T("MySprClass"), //Имя класса виндовс
			            _T("SpriteView: ") + m_strScrName, //имя окна
			            WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME, // window styles
			            CRect(0, 0, 200, 200), //размер окна
			            theApp.GetMainWnd(), //родитель окна
			            nullptr,
			            WS_EX_TOOLWINDOW /*| WS_EX_OVERLAPPEDWINDOW*/ //extended window styles
			        ))
			{
				pSprWnd->ShowWindow(SW_SHOW);
			}
		}

		UINT nButtonStyle = m_btnSprite.GetButtonStyle();
		m_btnSprite.SetButtonStyle(nButtonStyle & ~(TBBS_CHECKED | TBBS_PRESSED));
	}
}

void C16KPageView::OnSaveImg(UINT id)
{
	if (id == ID_BUTTON_16KPV_SAVE + m_nPageNum)
	{
		HBITMAP hBm;
		DWORD *pNewBits = nullptr;

		if (m_bColorMode)
		{
			//сохранение страницы как изображения. совершенно особый подход.
			//сохранять будем как есть
			pNewBits = new DWORD[256 * 256]; //новое битовое поле

			if (pNewBits)
			{
				int nbi = 0;

				//формируем битмап
				for (int y = 0; y < 256; ++y)
				{
					//алгоритм не оптимален, но нам нужна принципиальная работоспособность
					//оптимизируем потом
					for (int x = 0; x < 256; ++x)
					{
						//берём очередной бит.
						int b = y * 0100 + x / 4;
						int m = ((x % 4) * 2);
						BYTE v = m_pBuffer[b];
						int c = (v >> m) & 3;
						pNewBits[nbi++] = g_pColorPalettes[0][c];
					}
				}

				hBm = CreateBitmap(256, 256, 1, 32, pNewBits);
			}
		}
		else
		{
			//создаём чёрно-белую картинку
			pNewBits = new DWORD[512 * 256]; //новое битовое поле

			if (pNewBits)
			{
				int nbi = 0;

				//формируем битмап
				for (int y = 0; y < 256; ++y)
				{
					//алгоритм не оптимален, но нам нужна принципиальная работоспособность
					//оптимизируем потом
					for (int x = 0; x < 512; ++x)
					{
						//берём очередной бит.
						int b = y * 0100 + x / 8;
						int m = (x % 8);
						BYTE v = m_pBuffer[b];
						int c = (v >> m) & 1;
						pNewBits[nbi++] = g_pMonochromePalette[0][c];
					}
				}

				hBm = CreateBitmap(512, 256, 1, 32, pNewBits);
			}
		}

		if (pNewBits)
		{
			CImage img;
			img.Attach(hBm);
			CString strFilter;
			CSimpleArray<GUID> aguidFileTypes;
			img.GetExporterFilterString(strFilter, aguidFileTypes);
			CFileDialog dlg(false, _T("png"), nullptr,
			                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
			                strFilter, this);

			if (dlg.DoModal() == IDOK)
			{
				DWORD nFilterSave = dlg.m_ofn.nFilterIndex;
				GUID guid = nFilterSave > 0 ? aguidFileTypes[nFilterSave - 1] : GUID(GUID_NULL);
				CString strFileName = dlg.GetPathName();

				if (dlg.GetFileExt().IsEmpty())
				{
					if (strFileName[strFileName.GetLength() - 1] == '.')
					{
						strFileName = strFileName.Left(strFileName.GetLength() - 1);
					}

					CString strExt;

					if (nFilterSave == 0)
					{
						strExt = _T(".jpg"); // default to JPEG
					}
					else
					{
						// Look up the first extension in the filters
						int nCount = (nFilterSave * 2) - 1;
						int nLeft = 0;

						while (nCount)
						{
							if (strFilter[nLeft++] == '|')
							{
								nCount--;
							}
						}

						ASSERT(nLeft < strFilter.GetLength());
						strExt = strFilter.Tokenize(_T(";|"), nLeft).MakeLower();
						strExt = ::PathFindExtension(strExt);
					}

					strFileName += strExt;
				}

				img.Save(strFileName, guid);
			}

			img.Detach();
			DeleteObject(hBm);
			delete[] pNewBits;
		}
	}

	UINT nButtonStyle = m_btnSave.GetButtonStyle();
	m_btnSave.SetButtonStyle(nButtonStyle & ~(TBBS_CHECKED | TBBS_PRESSED));
	m_pSprScr->SetFocus();
}

void C16KPageView::OnLoadImg(UINT id)
{
	if (id == ID_BUTTON_16KPV_LOAD + m_nPageNum)
	{
		CImage img;
		CString strImporters;
		CSimpleArray<GUID> aguidFileTypes;
		img.GetImporterFilterString(strImporters, aguidFileTypes, _T("All Image Files"));
		CFileDialog dlg(true, _T("png"), nullptr,
		                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
		                strImporters, this);

		if (dlg.DoModal() == IDOK)
		{
			if (SUCCEEDED(img.Load(dlg.GetPathName())))
			{
				int w = img.GetWidth();
				int h = img.GetHeight();

				if (m_bColorMode)
				{
					w = min(256, w); //если подсовывают большую картинку -
					h = min(256, h); //будем тупо обрезать её

					for (int y = 0; y < h; ++y)
					{
						//алгоритм не оптимален, но нам нужна принципиальная работоспособность
						//оптимизируем потом
						for (int x = 0; x < w; ++x)
						{
							//берём очередной пиксел.
							COLORREF c = img.GetPixel(x, y);
							int b = y * 0100 + x / 4; //байт
							int m = ((x % 4) * 2); //маска в нём
							//теперь определим, какой цвет у пикселя
							int bc = 0; //чёрный

							if ((c & 0xff0000) && !(c & 0x00f8f8)) //если в синем канале что-то есть, а в остальных почти нету
							{
								bc = 1; //то это синий цвет
							}
							else if ((c & 0x00ff00) && !(c & 0xf800f8)) //если в зелёном канале что-то есть, а в остальных почти нету
							{
								bc = 2; //то это зелёный цвет
							}
							else if ((c & 0x0000ff) && !(c & 0xf8f800)) //если в красном канале что-то есть, а в остальных почти нету
							{
								bc = 3; //то это красный цвет
							}

							//все остальные комбинации - чёрный цвет. нефиг тут.
							m_pBuffer[b] &= ~(3 << m);
							m_pBuffer[b] |= bc << m; //задаём цвет
						}
					}
				}
				else
				{
					w = min(512, w); //если подсовывают большую картинку -
					h = min(256, h); //будем тупо обрезать её

					for (int y = 0; y < h; ++y)
					{
						//алгоритм не оптимален, но нам нужна принципиальная работоспособность
						//оптимизируем потом
						for (int x = 0; x < w; ++x)
						{
							//берём очередной пиксел.
							COLORREF c = img.GetPixel(x, y);
							int b = y * 0100 + x / 8; //байт
							int m = (x % 8); //маска в нём
							//теперь определим, какой цвет у пикселя
							int bc = 0; //чёрный

							if (c & 0xf8f8f8) //хоть в одном канале есть что-то не совсем чёрное
							{
								bc = 1; //то это белый цвет
							}

							//все остальные комбинации - чёрный цвет. нефиг тут.
							m_pBuffer[b] &= ~(1 << m);
							m_pBuffer[b] |= bc << m; //задаём цвет
						}
					}
				}

				img.Destroy();
			}
		}
	}

	UINT nButtonStyle = m_btnLoad.GetButtonStyle();
	m_btnLoad.SetButtonStyle(nButtonStyle & ~(TBBS_CHECKED | TBBS_PRESSED));
	m_pSprScr->SetFocus();
}

void C16KPageView::OnDestroy()
{
	m_pSprScr->DestroyWindow();
	SAFE_DELETE(m_pSprScr);
	CWnd::OnDestroy();
}
