
#include "pch.h"
#include "AZBKDumpView.h"
#include "Config.h"
#include "Platform.h"
#include "Screen_Sizes.h"

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

constexpr auto COLUMN_WIDTH = 60;
constexpr auto COLUMN_WIDTH_ACS = 30;

static auto BG_COLOR = ::GetSysColor(COLOR_BTNFACE);

constexpr auto COLNUM_STD = 2;
constexpr auto COLNUM_MAP = 3;
constexpr auto ROWNUM_DMA = 5;
constexpr auto ROWNUM_COVOX = 5;
constexpr auto ROWNUM_AZBK = 4;
constexpr auto ROWNUM_SCR = 2;
constexpr auto ROWNUM_PAL = 2;
constexpr auto ROWNUM_MAP = 16;
constexpr auto ROWNUM_MCR = 4;
constexpr auto ROWNUM_UART = 4;

static const ColParam_t arLSTCols[COLNUM_STD] =
{
	{COL_WIDTH | COL_COLOR, COLUMN_WIDTH, BG_COLOR},
	{COL_WIDTH | COL_EDITABLE, COLUMN_WIDTH, 0}
};

static const ColParam_t arLSTMAPCols[COLNUM_MAP] =
{
	{COL_WIDTH | COL_COLOR, COLUMN_WIDTH, BG_COLOR},
	{COL_WIDTH | COL_EDITABLE, COLUMN_WIDTH, 0},
	{COL_WIDTH | COL_COLOR, COLUMN_WIDTH_ACS, BG_COLOR}
};

static const CellParam_t arDMARows[ROWNUM_DMA][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177160"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177162"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177164"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177166"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177170"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const CellParam_t arCOVOXRows[ROWNUM_COVOX][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177200"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177202"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177204"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177206"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177212"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const CellParam_t arAZBKRows[ROWNUM_AZBK][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177220"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177222"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177224"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177226"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const CellParam_t arSCRRows[ROWNUM_SCR][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177230"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177232"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const CellParam_t arPALRows[ROWNUM_PAL][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177234"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177236"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const CellParam_t arMAPRRows[ROWNUM_MAP][COLNUM_MAP] =
{
	{ {COL_VALUE | COL_COLOR, _T("177300"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177302"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177304"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177306"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177310"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177312"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177314"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177316"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177320"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177322"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177324"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177326"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177330"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177332"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177334"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
	{ {COL_VALUE | COL_COLOR, _T("177336"), BG_COLOR}, {COL_EDITABLE, nullptr, 0}, {COL_COLOR, nullptr, BG_COLOR} },
};

static const CellParam_t arMCRRows[ROWNUM_MCR][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177340"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177342"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177344"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177346"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const CellParam_t arUARTRows[ROWNUM_UART][COLNUM_STD] =
{
	{ {COL_VALUE | COL_COLOR, _T("177560"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177562"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177564"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} },
	{ {COL_VALUE | COL_COLOR, _T("177566"), BG_COLOR}, {COL_EDITABLE, nullptr, 0} }
};

static const uint16_t arDMARegs[ROWNUM_DMA] =
{ 0177160, 0177162, 0177164, 0177166, 0177170 };

static const uint16_t arCOVOXRegs[ROWNUM_COVOX] =
{ 0177200, 0177202, 0177204, 0177206, 0177212 };

static const uint16_t arAZBKRegs[ROWNUM_AZBK] =
{ 0177220, 0177222, 0177224, 0177226 };

static const uint16_t arSCRRegs[ROWNUM_SCR] =
{ 0177230, 0177232 };

static const uint16_t arPALRegs[ROWNUM_PAL] =
{ 0177234, 0177236 };

static const uint16_t arMAPRegs[ROWNUM_MAP] =
{
	0177300, 0177302, 0177304, 0177306, 0177310, 0177312, 0177314, 0177316,
	0177320, 0177322, 0177324, 0177326, 0177330, 0177332, 0177334, 0177336
};

static const uint16_t arMCRRegs[ROWNUM_MCR] =
{ 0177340, 0177342, 0177344, 0177346 };

static const uint16_t arUARTRegs[ROWNUM_UART] =
{ 0177560, 0177562, 0177564, 0177566 };


/////////////////////////////////////////////////////////////////////////////
// CAZBKDumpView
IMPLEMENT_DYNAMIC(CAZBKDumpView, CDocPaneDlgViewBase)

CAZBKDumpView::CAZBKDumpView()
    = default;

CAZBKDumpView::~CAZBKDumpView()
    = default;

void CAZBKDumpView::DoDataExchange(CDataExchange *pDX)
{
	CDocPaneDlgViewBase::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_AZBK_DMAREG, m_listDMA);
	DDX_Control(pDX, IDC_LIST_AZBK_COVOX, m_listCOVOX);
	DDX_Control(pDX, IDC_LIST_AZBK_MAIN, m_listAZBK);
	DDX_Control(pDX, IDC_LIST_AZBK_SCRREG, m_listScr);
	DDX_Control(pDX, IDC_LIST_AZBK_PALREG, m_listPalette);
	DDX_Control(pDX, IDC_LIST_AZBK_MAPPERREG, m_listMapper);
	DDX_Control(pDX, IDC_LIST_AZBK_MAPCTRLREG, m_listMapCtrl);
	DDX_Control(pDX, IDC_LIST_AZBK_UARTREG, m_listUART);
}

BEGIN_MESSAGE_MAP(CAZBKDumpView, CDocPaneDlgViewBase)
	ON_MESSAGE(WM_INITDIALOG, &CAZBKDumpView::HandleInitDialog)
END_MESSAGE_MAP()

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

LRESULT CAZBKDumpView::HandleInitDialog(WPARAM wp, LPARAM lp)
{
	CDocPaneDlgViewBase::HandleInitDialog(wp, lp);
	std::vector <ListParam_t> vList;    // приходится делать так, т.к. нельзя в статическом массиве делать
// ссылки на нестатические объекты
	vList.push_back({ COLNUM_STD, ROWNUM_DMA, IDC_STATIC_AZBK_DMATITLE, &m_listDMA, &arLSTCols[0], &arDMARows[0][0] });
	vList.push_back({ COLNUM_STD, ROWNUM_COVOX, IDC_STATIC_AZBK_COVOXTTL, &m_listCOVOX, &arLSTCols[0], &arCOVOXRows[0][0] });
	vList.push_back({ COLNUM_STD, ROWNUM_AZBK, IDC_STATIC_AZBK_MAINTTL,  &m_listAZBK, &arLSTCols[0], &arAZBKRows[0][0] });
	vList.push_back({ COLNUM_STD, ROWNUM_SCR, IDC_STATIC_AZBK_SCRTITLE, &m_listScr, &arLSTCols[0], &arSCRRows[0][0] });
	vList.push_back({ COLNUM_STD, ROWNUM_PAL, IDC_STATIC_AZBK_PALTITLE, &m_listPalette, &arLSTCols[0], &arPALRows[0][0] });
	int nMaxGrpWidth = 0;
	CPoint ptLT{ 0, 0 };

	// Создаём списки регистров
	for (auto &i : vList)
	{
		CreateList(i);
		ResizeList(i, ptLT, nMaxGrpWidth);  // и сразу подвинем
	}

	// теперь сделаем группы все одной ширины
	for (auto &i : vList)
	{
		SetGrpWidth(GetDlgItem(i.nGrpId), nMaxGrpWidth);
	}

	vList.clear(); //всё, больше этот вектор не нужен
	// теперь вторая порция списков
	vList.push_back({ COLNUM_MAP, ROWNUM_MAP, IDC_STATIC_AZBK_MAPPERTTL, &m_listMapper, &arLSTMAPCols[0], &arMAPRRows[0][0] });
	vList.push_back({ COLNUM_STD, ROWNUM_MCR, 0, &m_listMapCtrl, &arLSTCols[0], &arMCRRows[0][0] });
	vList.push_back({ COLNUM_STD, ROWNUM_UART, IDC_STATIC_AZBK_UARTTITLE,  &m_listUART, &arLSTCols[0], &arUARTRows[0][0] });

	for (auto &i : vList)
	{
		CreateList(i);
	}

	// теперь реорганизация вручную
	int nPixelW = ::GetDeviceCaps(GetDC()->GetSafeHdc(), LOGPIXELSX);
	int padX = ::MulDiv(4, nPixelW, DEFAULT_DPIX); // отступ группы от списка слева и справа
	CRect rect;
	m_listDMA.GetWindowRect(&rect);
	ScreenToClient(&rect);
	CPoint ptLT2{ rect.right + padX * 3, rect.top }; // вот с этого верхнего левого угла начнём
	int t = 0;
	ResizeList(vList[0], ptLT2, t); // разместим первый список - маппер
	CPoint ptLT3{ ptLT2.x + t, rect.top }; //  это для регистров управления маппера
	ResizeList(vList[1], ptLT3, t); //  разместим второй список - управление маппера
	CRect rect2;
	m_listMapCtrl.GetWindowRect(&rect2);
	ScreenToClient(&rect2);
	nMaxGrpWidth = (rect2.right - ptLT2.x) + padX * 2;
	ResizeList(vList[2], ptLT2, t); // теперь третий список
	SetGrpWidth(GetDlgItem(vList[0].nGrpId), nMaxGrpWidth);
	SetGrpWidth(GetDlgItem(vList[2].nGrpId), nMaxGrpWidth);
	vList.clear(); //всё,
	//и под конец изменим размеры самого окна
	GetWindowRect(&rect);
	ScreenToClient(&rect);
	rect.bottom = max(ptLT.y, ptLT2.y) - ::GetSystemMetrics(SM_CYCAPTION) / 2;
	rect.right = rect2.right + padX * 3;
	m_sizeDefault = rect.Size();
	return TRUE;  // return TRUE unless you set the focus to a control
	// Исключение: страница свойств OCX должна возвращать значение FALSE
}


void CAZBKDumpView::DisplayRegDump()
{
	if (IsWindowVisible())
	{
		// выводим на экран данные
		DisplayRegs();
	}
}

void CAZBKDumpView::CreateList(ListParam_t &p)
{
	int nPixelW = ::GetDeviceCaps(GetDC()->GetSafeHdc(), LOGPIXELSX);
	p.pList->SetFont(&m_hFont, FALSE);
	p.pList->SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER | LVS_EX_SNAPTOGRID);

	// создаём колонки
	for (int col = 0; col < p.nCols; ++col)
	{
		if (p.pCP[col].dwState & COL_WIDTH)
		{
			p.pList->InsertColumn(col, LPSTR_TEXTCALLBACK, LVCFMT_LEFT, ::MulDiv(p.pCP[col].nWidth, nPixelW, DEFAULT_DPIX));
		}
		else
		{
			p.pList->InsertColumn(col, LPSTR_TEXTCALLBACK, LVCFMT_LEFT);
		}
	}

	// добавляем строки и значения в ячейки
	for (int row = 0; row < p.nRows; ++row)
	{
		const CellParam_t *pRow = &p.pRP[row * p.nCols];

		for (int col = 0; col < p.nCols; ++col)
		{
			if (col == 0)
			{
				p.pList->InsertItem(row, (pRow[col].dwState & COL_VALUE) ? pRow[col].strValue.GetString() : LPSTR_TEXTCALLBACK);
			}
			else
			{
				p.pList->SetItem(row, col, LVIF_TEXT, (pRow[col].dwState & COL_VALUE) ? pRow[col].strValue.GetString() : LPSTR_TEXTCALLBACK, 0, 0, 0, 0);
			}

			// можно задавать свойства отдельных ячеек
//          if (pRow[col].dwState & COL_COLOR)
//          {
//              p.pList->SetItemBkColor(p.pCP[col].color, row, col);
//          }
//
//          p.pList->EnableEdit(!!(p.pCP[col].dwState & COL_EDITABLE), row, col);
		}
	}

	// задаём свойства колонок, их можно применять только к уже созданным элементам,
	// поэтому повторный цикл уже после создания строк
	for (int col = 0; col < p.nCols; ++col)
	{
		if (p.pCP[col].dwState & COL_COLOR)
		{
			p.pList->SetColumnBkColor(p.pCP[col].color, col);
		}

		p.pList->EnableColumnEdit(!!(p.pCP[col].dwState & COL_EDITABLE), col);
	}
}

// Вход: p - элемент параметров списка
// Вход-Выход: ptLT - координата верхней левой точки списка, если 0 - то берутся текущие
//				maxGW - максимальная ширина группы
void CAZBKDumpView::ResizeList(ListParam_t &p, CPoint &ptLT, int &maxGW)
{
	CRect listRect, grpRect, itemRect;
	p.pList->GetWindowRect(&listRect);      // текущие размеры списка
	ScreenToClient(&listRect);

	if (ptLT.x)
	{
		listRect.left = ptLT.x;
	}

	if (ptLT.y)
	{
		listRect.top = ptLT.y;
	}

	CWnd *grp = p.nGrpId ? GetDlgItem(p.nGrpId) : nullptr;

	if (grp)
	{
		grp->GetWindowRect(&grpRect);   // текущие размеры группы
		ScreenToClient(&grpRect);
	}

	p.pList->GetItemRect(0, itemRect, LVIR_BOUNDS); // получим размеры элемента строки списка
	// и на их основе расчитаем новые размеры списка, чтобы был точно по размерам элементов
	register int cx = itemRect.Width() + ::GetSystemMetrics(SM_CXDLGFRAME);
	register int cy = itemRect.Height() * p.nRows + ::GetSystemMetrics(SM_CYDLGFRAME);
	p.pList->SetWindowPos(nullptr, listRect.left, listRect.top, cx, cy, SWP_SHOWWINDOW); // изменяем размеры списка
	int nPixelW = ::GetDeviceCaps(GetDC()->GetSafeHdc(), LOGPIXELSX);
	int nPixelH = ::GetDeviceCaps(GetDC()->GetSafeHdc(), LOGPIXELSY);
	int padX = ::MulDiv(4, nPixelW, DEFAULT_DPIX); // отступ группы от списка слева и справа
	int padY = ::MulDiv(5, nPixelH, DEFAULT_DPIY); // отступ группы от списка сверху и снизу

	if (grp)
	{
		grpRect.left = listRect.left - padX;
		grpRect.top = listRect.top - ::GetSystemMetrics(SM_CYCAPTION) / 2 - padY;
		grpRect.right = listRect.left + cx + padX;
		grpRect.bottom = listRect.top + cy + padY;
		grp->SetWindowPos(nullptr, grpRect.left, grpRect.top, grpRect.Width(), grpRect.Height(), SWP_SHOWWINDOW); // изменяем размеры группы

		if (maxGW < grpRect.Width())
		{
			maxGW = grpRect.Width();
		}

		ptLT.x = listRect.left;
		ptLT.y = grpRect.bottom + ::GetSystemMetrics(SM_CYCAPTION) / 2 + padY;
	}
}


void CAZBKDumpView::SetGrpWidth(CWnd *grp, int width)
{
	CRect grpRect;
	grp->GetWindowRect(&grpRect);   // текущие размеры группы
	ScreenToClient(&grpRect);
	grp->SetWindowPos(nullptr, grpRect.left, grpRect.top, width, grpRect.Height(), SWP_SHOWWINDOW); // изменяем размеры группы
}

void CAZBKDumpView::DisplayRegs()
{
	// выводим регистры dma
	for (int n = 0; n < ROWNUM_DMA; ++n)
	{
		m_listDMA.SetItemWithModified(g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arDMARegs[n]), n, 1);
	}

	// выводим регистры covox
	for (int n = 0; n < ROWNUM_COVOX; ++n)
	{
		m_listCOVOX.SetItemWithModified(g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arCOVOXRegs[n]), n, 1);
	}

	// выводим регистры контроллера
	for (int n = 0; n < ROWNUM_AZBK; ++n)
	{
		m_listAZBK.SetItemWithModified(g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arAZBKRegs[n]), n, 1);
	}

	// выводим регистры экрана
	for (int n = 0; n < ROWNUM_SCR; ++n)
	{
		m_listScr.SetItemWithModified(g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arSCRRegs[n]), n, 1);
	}

	// выводим регистры палитры
	for (int n = 0; n < ROWNUM_PAL; ++n)
	{
		m_listPalette.SetItemWithModified(g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arPALRegs[n]), n, 1);
	}

	// выводим регистры управления маппером
	uint16_t mmucsr[ROWNUM_MCR];

	for (int n = 0; n < ROWNUM_MCR; ++n)
	{
		mmucsr[n] = g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arMCRRegs[n]);
		m_listMapCtrl.SetItemWithModified(mmucsr[n], n, 1);
	}

	// выводим регистры маппера
	for (int n = 0, bit = 1; n < ROWNUM_MAP; ++n, bit <<= 1)
	{
		if (mmucsr[2] & bit)
		{
			m_listMapper.SetRowColor(RGB(0xcc, 0xcc, 0xcc), n);
		}
		else
		{
			m_listMapper.SetRowColor(0, n);
		}

		uint16_t nBank = g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arMAPRegs[n]);
		m_listMapper.SetItemWithModified(nBank, n, 1);
		CString str = _T("--");

		if (mmucsr[0] & bit)
		{
			if (64 <= nBank && nBank < 128)
			{
				str = _T("RO"); // безусловно аппаратное ограничение
			}
			else
			{
				str = (mmucsr[1] & bit) ? _T("RO") : _T("RW");
			}
		}

		m_listMapper.SetItemWithModifiedASCII(str, n, 2);
	}

	// выводим регистры UART
	for (int n = 0; n < ROWNUM_UART; ++n)
	{
		m_listUART.SetItemWithModified(g_Platform.GetDebuggerPtr()->GetDebugAZBKRegs(arUARTRegs[n]), n, 1);
	}
}


