#include "pch.h"
#include "MFC/CWizard.h"
#include "MFC/CWizardPage.h"

CWizard::CWizard(UINT nIDTemplate, CWnd* pParentWnd)
	: CBCGPDialog(nIDTemplate, pParentWnd)
{
	EnableVisualManagerStyle();
	
}

BOOL CWizard::OnInitDialog()
{
	CBCGPDialog::OnInitDialog();

	fontTitle.CreatePointFont(fontSize_title, L"楷体");
	fontDescription.CreatePointFont(fontSize_desc, L"楷体");

	labelTitle.SetFont(&fontTitle);
	labelDesc.SetFont(&fontDescription);

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

void CWizard::OnSize(UINT nType, int cx, int cy)
{
	CBCGPDialog::OnSize(nType, cx, cy);

	if (!labelTitle.m_hWnd) return;
	// TODO: 在此处添加消息处理程序代码
	CRect r;
	GetClientRect(&r);

	int x = globalUtils.ScaleByDPI(titleLeft);
	int y = globalUtils.ScaleByDPI(titleTop);
	int h = globalUtils.ScaleByDPI(titleHeight);
	labelTitle.MoveWindow(x, y, r.Width() - x * 2, h);

	x = globalUtils.ScaleByDPI(descLeft);
	y += globalUtils.ScaleByDPI(descTop)+h;
	h = globalUtils.ScaleByDPI(descHeight);
	labelDesc.MoveWindow(x, y, r.Width() - x * 2, h);

	headH = y + h + globalUtils.ScaleByDPI(descBottom);
	
	int btnW = globalUtils.ScaleByDPI(btnWidth), btnH = globalUtils.ScaleByDPI(btnHeight);
	int btnS = globalUtils.ScaleByDPI(btnSpace), btnR = globalUtils.ScaleByDPI(btnRight), btnB = globalUtils.ScaleByDPI(btnBottom);
	footH = btnB + btnH + globalUtils.ScaleByDPI(btnTop);

	x = r.right - btnW - btnR;
	y = r.bottom - btnH - btnB;

	if (opts & HaveHelpButton) {
		btns[HelpButton].MoveWindow(x, y, btnW, btnH); x -= btnW + btnS;
	}
	else {
		btns[HelpButton].ShowWindow(SW_HIDE);
	}
	btns[CancelButton].MoveWindow(x, y, btnW, btnH); x -= btnW + btnS;
	// 下面3个键位置重合
	btns[FinishButton].MoveWindow(x, y, btnW, btnH); //x -= btnW + btnS;
	btns[CommitButton].MoveWindow(x, y, btnW, btnH); //x -= btnW + btnS;
	btns[NextButton].MoveWindow(x, y, btnW, btnH); x -= btnW + btnS;
	btns[BackButton].MoveWindow(x, y, btnW, btnH);
}

void CWizard::reset()
{
	if (current != -1) {
		currentPage()->ShowWindow(SW_HIDE);
		cleanupPagesNotInHistory();
		for (auto i = history.size() - 1; i >= 0; --i)
			cleanupPage(history.at(i));
		history.clear();
		for (auto &[id, p] : pageMap)
			p->initialized = false;

		current = -1;
	//	emit q->currentIdChanged(-1);
	}

	if (current != -1) {

		current = -1;
	}
}

void CWizard::cleanupPagesNotInHistory()
{
	for (auto &[idx, p] : pageMap) {
		auto it = std::find(history.begin(), history.end(), idx);
		if (p->initialized && it == history.end()) {
			cleanupPage(idx);
			p->initialized = false;
		}
	}
}

void CWizard::switchToPage(int newId, Direction direction)
{
    // disableUpdates();

    int oldId = current;
    if (CWizardPage* oldPage = currentPage()) {
        oldPage->ShowWindow(SW_HIDE);

        if (direction == Backward) {
            if (!(opts & CWizard::IndependentPages)) {
                cleanupPage(oldId);
                oldPage->initialized = false;
            }
            ASSERT(history.back() == oldId);
            history.pop_back();
            ASSERT(history.back() == newId);
        }
    }

    current = newId;

    CWizardPage* newPage = currentPage();
    if (newPage) {
        if (direction == Forward) {
            if (!newPage->initialized) {
                newPage->initialized = true;
                initializePage(current);
            }
            history.push_back(current);
        }
        newPage->ShowWindow(SW_SHOW);
		labelTitle.SetWindowText(newPage->title());
		labelDesc.SetWindowText(newPage->desc());
    }

    canContinue = (nextId() != -1);
    canFinish = (newPage && newPage->isFinalPage());

	_q_updateButtonStates();
    updateButtonTexts();

    const CWizard::WizardButton nextOrCommit =
        newPage && newPage->isCommitPage() ? CWizard::CommitButton : CWizard::NextButton;
    CBCGPButton* nextOrFinishButton =
        &btns[canContinue ? nextOrCommit : CWizard::FinishButton];
    //CWnd* candidate = nullptr;

    ///*
    //    If there is no default button and the Next or Finish button
    //    is enabled, give focus directly to it as a convenience to the
    //    user. This is the normal case on OS X.

    //    Otherwise, give the focus to the new page's first child that
    //    can handle it. If there is no such child, give the focus to
    //    Next or Finish.
    //*/
    //if ((opts & QWizard::NoDefaultButton) && nextOrFinishButton->isEnabled()) {
    //    candidate = nextOrFinishButton;
    //}
    //else if (newPage) {
    //    candidate = iWantTheFocus(newPage);
    //}
    //if (!candidate)
    //    candidate = nextOrFinishButton;
    //candidate->setFocus();

    //if (wizStyle == QWizard::MacStyle)
    //    q->updateGeometry();

    //enableUpdates();
    //updateLayout();
    //updatePalette();

    //emit q->currentIdChanged(current);
}

void CWizard::updateCurrentPage()
{
	if (currentPage()) {
		canContinue = (nextId() != -1);
		canFinish = currentPage()->isFinalPage();
	}
	else {
		canContinue = false;
		canFinish = false;
	}
	_q_updateButtonStates();
	updateButtonTexts();
}

/*!
    \property QWizard::startId
    \brief the ID of the first page

    If this property isn't explicitly set, this property defaults to
    the lowest page ID in this wizard, or -1 if no page has been
    inserted yet.

    \sa restart(), nextId()
*/
void CWizard::setStartId(int theid)
{
	int newStart = theid;
	if (theid == -1)
		newStart = pageMap.size() ? pageMap.begin()->first : -1;

	if (start == newStart) {
		startSetByUser = theid != -1;
		return;
	}

	if (pageMap.find(newStart) != pageMap.end()) {
		//alog->warn("CWizard::setStartId: Invalid page ID {}", newStart);
		return;
	}
	start = newStart;
	startSetByUser = theid != -1;
}

static void setVisible(CWnd* w, bool en)
{
	ASSERT(w != nullptr);
	w->ShowWindow(en ? SW_SHOW : SW_HIDE);
}

void CWizard::_q_updateButtonStates()
{
	//disableUpdates();

	const auto* p = currentPage();
	bool complete = p && p->isComplete();

	btns[BackButton].EnableWindow(history.size() > 1
		&& !page(history.at(history.size() - 2))->isCommitPage()
		&& (!canFinish || !(opts & CWizard::DisabledBackButtonOnLastPage)));
	btns[NextButton].EnableWindow(canContinue && complete);
	btns[CommitButton].EnableWindow(canContinue && complete);
	btns[FinishButton].EnableWindow(canFinish && complete);

	const bool backButtonVisible = 
		(history.size() > 1 || !(opts & CWizard::NoBackButtonOnStartPage))
		&& (canContinue || !(opts & CWizard::NoBackButtonOnLastPage));
	bool commitPage = p && p->isCommitPage();
	setVisible(&btns[BackButton], backButtonVisible);
	setVisible(&btns[NextButton], !commitPage && (canContinue || (opts & CWizard::HaveNextButtonOnLastPage)));
	setVisible(&btns[CommitButton], commitPage && canContinue);
	setVisible(&btns[FinishButton], (canFinish || (opts & CWizard::HaveFinishButtonOnEarlyPages)));

	if (!(opts & CWizard::NoCancelButton))
		setVisible(&btns[CancelButton], (canContinue || !(opts & CWizard::NoCancelButtonOnLastPage)));

	//bool useDefault = !(opts & CWizard::NoDefaultButton);
	//if (QPushButton* nextPush = qobject_cast<QPushButton*>(btn.next))
	//	nextPush->setDefault(canContinue && useDefault && !commitPage);
	//if (QPushButton* commitPush = qobject_cast<QPushButton*>(btn.commit))
	//	commitPush->setDefault(canContinue && useDefault && commitPage);
	//if (QPushButton* finishPush = qobject_cast<QPushButton*>(btn.finish))
	//	finishPush->setDefault(!canContinue && useDefault);
}

#include "CLang.h"
static const wchar_t* buttonDefaultText(int which)
{
	if (gLang == CLang::zh) {
		switch (which) {
		case CWizard::BackButton:
			return L"< 上一步";
		case CWizard::NextButton:
			return L"下一步 >";
		case CWizard::CommitButton:
			return L"提交";
		case CWizard::FinishButton:
			return L"完成";
		case CWizard::CancelButton:
			return L"取消";
		case CWizard::HelpButton:
			return L"帮助";
		default:
			return L"";
		}
	}
	else {
		switch (which) {
		case CWizard::BackButton:
			return L"< Back";
		case CWizard::NextButton:
			return L"Next >";
		case CWizard::CommitButton:
			return L"Commit";
		case CWizard::FinishButton:
			return L"Finish";
		case CWizard::CancelButton:
			return L"Cancel";
		case CWizard::HelpButton:
			return L"Help";
		default:
			return L"";
		}
	}
	
}
void CWizard::updateButtonTexts()
{
	for (int i = 0; i < CWizard::NButtons; ++i) {
		if (currentPage() && currentPage()->buttonCustomTexts.find(i) != currentPage()->buttonCustomTexts.end()) {
			btns[i].SetWindowText(currentPage()->buttonCustomTexts.find(i)->second);
		}
		else {
			auto it = buttonCustomTexts.find(i);
			if (it != buttonCustomTexts.end())
				btns[i].SetWindowText(it->second);
			else if (i < NStandardButtons)
				btns[i].SetWindowText(buttonDefaultText(i));
		}
	}
}

void CWizard::updateButtonLayout()
{
	// Back Next Commit Finish Cancel Help

}

/*!
	Adds the given \a page to the wizard, and returns the page's ID.

	The ID is guaranteed to be larger than any other ID in the
	QWizard so far.

	\sa setPage(), page(), pageAdded()
*/
int CWizard::addPage(CWizardPage* page)
{
	int theid = 0;
	if (!pageMap.empty())
		theid = pageMap.rbegin()->first + 1;
	setPage(theid, page);
	return theid;
}

/*!
    \fn void QWizard::setPage(int id, QWizardPage *page)

    Adds the given \a page to the wizard with the given \a id.

    \note Adding a page may influence the value of the startId property
    in case it was not set explicitly.

    \sa addPage(), page(), pageAdded()
*/
void CWizard::setPage(int theid, CWizardPage* page)
{

    if (!page) {
        alog->error("CWizard::setPage: Cannot insert null page");
        return;
    }

    if (theid == -1) {
        alog->error("CWizard::setPage: Cannot insert page with ID -1");
        return;
    }

    auto it = pageMap.find(theid);

    if (it != pageMap.end()) {
        alog->error("CWizard::setPage: Page with duplicate ID {} ignored", theid);
        return;
    }

    //page->setParent(d->pageFrame);

	//QVector<QWizardField>& pendingFields = page->d_func()->pendingFields;
	//	for (int i = 0; i < pendingFields.count(); ++i)
	//		d->addField(pendingFields.at(i));
	//	pendingFields.clear();

	//	connect(page, SIGNAL(completeChanged()), this, SLOT(_q_updateButtonStates()));

    pageMap.insert({ theid, page });
    page->_wizard = this;

    page->ShowWindow(SW_HIDE);

    if (!startSetByUser && pageMap.cbegin()->first == theid)
        start = theid;
    //emit pageAdded(theid);
}


/*!
    Removes the page with the given \a id. cleanupPage() will be called if necessary.

    \note Removing a page may influence the value of the startId property.

    \since 4.5
    \sa addPage(), setPage(), pageRemoved(), startId()
*/
void CWizard::removePage(int id)
{
    CWizardPage* removedPage = nullptr;

    // update startItem accordingly
    if (pageMap.size() > 0) { // only if we have any pages
        auto it = pageMap.cbegin();
        if (start == id) {
            const int firstId = it->first;
            if (firstId == id) {
                if (pageMap.size() > 1)
                    start = (++it)->first; // secondId
                else
                    start = -1; // removing the last page
            }
            else { // startSetByUser has to be "true" here
                start = firstId;
            }
            startSetByUser = false;
        }
    }

    auto it = pageMap.find(id);
    if (it != pageMap.end()) {
		//emit pageRemoved(id);
		auto fi = std::find(history.begin(), history.end(), id);
		if (fi == history.end()) {
			// Case 1: removing a page not in the history
			removedPage = it->second;
			pageMap.erase(it);
			updateCurrentPage();
		}
		else if (id != current) {
			// Case 2: removing a page in the history before the current page
			removedPage = it->second;
			pageMap.erase(it);
			history.erase(fi);
			_q_updateButtonStates();
		}
		else if (history.size() == 1) {
			// Case 3: removing the current page which is the first (and only) one in the history
			reset();
			removedPage = it->second;
			pageMap.erase(it);
			if (pageMap.empty())
				updateCurrentPage();
			else
				restart();
		}
		else {
			// Case 4: removing the current page which is not the first one in the history
			back();
			removedPage = it->second;
			pageMap.erase(it);
			updateCurrentPage();
		}

		if (removedPage) {
			if (removedPage->initialized) {
				cleanupPage(id);
				removedPage->initialized = false;
			}

			//d->pageVBoxLayout->removeWidget(removedPage);
		}
    }
        
}

/*!
	Sets the text on button \a which to be \a text.

	By default, the text on buttons depends on the wizardStyle. For
	example, on \macos, the \uicontrol Next button is called \uicontrol
	Continue.

	To add extra buttons to the wizard (e.g., a \uicontrol Print button),
	one way is to call setButtonText() with CustomButton1,
	CustomButton2, or CustomButton3 to set their text, and make the
	buttons visible using the HaveCustomButton1, HaveCustomButton2,
	and/or HaveCustomButton3 options.

	Button texts may also be set on a per-page basis using QWizardPage::setButtonText().

	\sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText()
*/
void CWizard::setButtonText(WizardButton which, const CString& text)
{

//	if (!d->ensureButton(which))
//		return;
	ASSERT(which >= 0);
	ASSERT(which < NButtons);

	buttonCustomTexts.insert({ which, text });

	auto* curPage = currentPage();

	if (!curPage || curPage->buttonCustomTexts.find(which) == curPage->buttonCustomTexts.end()) {
		btns[which].SetWindowText(text);
	}
		
}

/*!
	Goes back to the previous page.

	This is equivalent to pressing the \uicontrol Back button.

	\sa next(), accept(), reject(), restart()
*/
void CWizard::back()
{
	auto n = history.size() - 2;
	if (n < 0)
		return;
	switchToPage(history[n], CWizard::Backward);
}

/*!
	Advances to the next page.

	This is equivalent to pressing the \uicontrol Next or \uicontrol Commit button.

	\sa nextId(), back(), accept(), reject(), restart()
*/
void CWizard::next()
{

	if (current == -1)
		return;

	if (validateCurrentPage()) {
		int next = nextId();
		if (next != -1) {

			if (std::find(history.begin(), history.end(), next) != history.end()) {
				alog->warn("CWizard::next: Page {} already met", next);
				return;
			}

			if (pageMap.find(next) == pageMap.end()) {
				alog->warn("CWizard::next: No such page {}", next);
				return;
			}

			
			switchToPage(next, CWizard::Forward);
		}
	}
}

/*!
	Restarts the wizard at the start page. This function is called automatically when the
	wizard is shown.

	\sa startId()
*/
void CWizard::restart()
{
	//disableUpdates();
	reset();
	switchToPage(startId(), CWizard::Forward);
	//enableUpdates();
}

/*!
	\fn void QWizard::initializePage(int id)

	This virtual function is called by QWizard to prepare page \a id
	just before it is shown either as a result of QWizard::restart()
	being called, or as a result of the user clicking \uicontrol Next. (However, if the \l
	QWizard::IndependentPages option is set, this function is only
	called the first time the page is shown.)

	By reimplementing this function, you can ensure that the page's
	fields are properly initialized based on fields from previous
	pages.

	The default implementation calls QWizardPage::initializePage() on
	page(\a id).

	\sa QWizardPage::initializePage(), cleanupPage()
*/
void CWizard::initializePage(int theid)
{
	CWizardPage* p = this->page(theid);
	if (p) p->initializePage();
}

/*!
	\fn void QWizard::cleanupPage(int id)

	This virtual function is called by QWizard to clean up page \a id just before the
	user leaves it by clicking \uicontrol Back (unless the \l QWizard::IndependentPages option is set).

	The default implementation calls QWizardPage::cleanupPage() on
	page(\a id).

	\sa QWizardPage::cleanupPage(), initializePage()
*/
void CWizard::cleanupPage(int theid)
{
	CWizardPage* p = this->page(theid);
	if (p)
		p->cleanupPage();
}

/*!
	This virtual function is called by QWizard when the user clicks
	\uicontrol Next or \uicontrol Finish to perform some last-minute validation.
	If it returns \c true, the next page is shown (or the wizard
	finishes); otherwise, the current page stays up.

	The default implementation calls QWizardPage::validatePage() on
	the currentPage().

	When possible, it is usually better style to disable the \uicontrol
	Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
	by reimplementing QWizardPage::isComplete()) than to reimplement
	validateCurrentPage().

	\sa QWizardPage::validatePage(), currentPage()
*/
bool CWizard::validateCurrentPage()
{
	CWizardPage* page = currentPage();
	if (!page)
		return true;

	return page->validatePage();
}

/*!
	This virtual function is called by QWizard to find out which page
	to show when the user clicks the \uicontrol Next button.

	The return value is the ID of the next page, or -1 if no page follows.

	The default implementation calls QWizardPage::nextId() on the
	currentPage().

	By reimplementing this function, you can specify a dynamic page
	order.

	\sa QWizardPage::nextId(), currentPage()
*/
int CWizard::nextId() const
{
	const CWizardPage* p = currentPage();
	if (!p)
		return -1;

	return p->nextId();
}

void CWizard::onChangeTitle()
{
	ASSERT(currentPage() != nullptr);
	labelTitle.SetWindowText(currentPage()->title());

}

void CWizard::onChangeDesc()
{
	ASSERT(currentPage() != nullptr);
	labelDesc.SetWindowText(currentPage()->desc());
}

