/*
 * Copyright 2011-2013, Haiku, Inc. All rights reserved.
 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
 * Distributed under the terms of the MIT License.
 */
 
 
#include "FolderConfigWindow.h"
 
#include <Alert.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <LayoutBuilder.h>
#include <ListItem.h>
#include <ScrollView.h>
#include <SpaceLayoutItem.h>
#include <StringView.h>
 
#include <StringForSize.h>
 
#include "Settings.h"
#include "Utilities.h"
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "IMAPFolderConfig"
 
 
class EditableListItem {
public:
								EditableListItem();
	virtual						~EditableListItem() {}
 
	virtual void				MouseDown(BPoint where) = 0;
	virtual	void				MouseUp(BPoint where) = 0;
 
			void				SetListView(BListView* list)
									{ fListView = list; }
 
protected:
			BListView*			fListView;
};
 
 
class CheckBoxItem : public BStringItem, public EditableListItem {
public:
								CheckBoxItem(const char* text, bool checked);
 
			void				DrawItem(BView* owner, BRect itemRect,
									bool drawEverything = false);
 
			void				MouseDown(BPoint where);
			void				MouseUp(BPoint where);
 
			bool				Checked() { return fChecked; }
private:
			BRect				fBoxRect;
			bool				fChecked;
			bool				fMouseDown;
};
 
 
class EditListView : public BListView {
public:
								EditListView(const char* name,
									list_view_type type
										= B_SINGLE_SELECTION_LIST,
									uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS
										| B_NAVIGABLE);
 
	virtual void				MouseDown(BPoint where);
	virtual void				MouseUp(BPoint where);
	virtual void				FrameResized(float newWidth, float newHeight);
 
private:
			EditableListItem*	fLastMouseDown;
};
 
 
class StatusWindow : public BWindow {
public:
	StatusWindow(BWindow* parent, const char* text)
		:
		BWindow(BRect(0, 0, 10, 10), B_TRANSLATE("status"), B_MODAL_WINDOW_LOOK,
			B_MODAL_APP_WINDOW_FEEL, B_NO_WORKSPACE_ACTIVATION | B_NOT_ZOOMABLE
				| B_AVOID_FRONT | B_NOT_RESIZABLE | B_NOT_ZOOMABLE
				| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
	{
		BLayoutBuilder::Group<>(this)
			.SetInsets(B_USE_DEFAULT_SPACING)
			.Add(new BStringView("text", text));
		CenterIn(parent->Frame());
	}
};
 
 
const uint32 kMsgApplyButton = '&Abu';
const uint32 kMsgInit = '&Ini';
 
 
// #pragma mark -
 
 
EditableListItem::EditableListItem()
	:
	fListView(NULL)
{
 
}
 
 
// #pragma mark -
 
 
CheckBoxItem::CheckBoxItem(const char* text, bool checked)
	:
	BStringItem(text),
	fChecked(checked),
	fMouseDown(false)
{
}
 
 
void
CheckBoxItem::DrawItem(BView* owner, BRect itemRect, bool drawEverything)
{
	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
	rgb_color lowColor = owner->LowColor();
	uint32 flags = 0;
	if (fMouseDown)
		flags |= BControlLook::B_CLICKED;
	if (fChecked)
		flags |= BControlLook::B_ACTIVATED;
 
	font_height fontHeight;
	owner->GetFontHeight(&fontHeight);
	BRect boxRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent),
		ceilf(5.0f + fontHeight.ascent));
 
	owner->PushState();
 
	float left = itemRect.left;
	fBoxRect.left = left + 3;
	fBoxRect.top = itemRect.top + (itemRect.Height() - boxRect.Height()) / 2;
	fBoxRect.right = fBoxRect.left + boxRect.Width();
	fBoxRect.bottom = itemRect.top + boxRect.Height();
 
	itemRect.left = fBoxRect.right + be_control_look->DefaultLabelSpacing();
 
	if (IsSelected() || drawEverything) {
		if (IsSelected()) {
			owner->SetHighColor(tint_color(lowColor, B_DARKEN_2_TINT));
			owner->SetLowColor(owner->HighColor());
		} else
			owner->SetHighColor(lowColor);
 
		owner->FillRect(
			BRect(left, itemRect.top, itemRect.left, itemRect.bottom));
	}
 
	be_control_look->DrawCheckBox(owner, fBoxRect, fBoxRect, base, flags);
	owner->PopState();
 
	BStringItem::DrawItem(owner, itemRect, drawEverything);
}
 
 
void
CheckBoxItem::MouseDown(BPoint where)
{
	if (!fBoxRect.Contains(where))
		return;
 
	fMouseDown = true;
 
	fListView->InvalidateItem(fListView->IndexOf(this));
}
 
 
void
CheckBoxItem::MouseUp(BPoint where)
{
	if (!fMouseDown)
		return;
	fMouseDown = false;
 
	if (fBoxRect.Contains(where)) {
		if (fChecked)
			fChecked = false;
		else
			fChecked = true;
	}
 
	fListView->InvalidateItem(fListView->IndexOf(this));
}
 
 
// #pragma mark -
 
 
EditListView::EditListView(const char* name, list_view_type type, uint32 flags)
	:
	BListView(name, type, flags),
	fLastMouseDown(NULL)
{
}
 
 
void
EditListView::MouseDown(BPoint where)
{
	BListView::MouseDown(where);
 
	int32 index = IndexOf(where);
	EditableListItem* handler = dynamic_cast<EditableListItem*>(ItemAt(index));
	if (handler == NULL)
		return;
 
	fLastMouseDown = handler;
	handler->MouseDown(where);
	SetMouseEventMask(B_POINTER_EVENTS);
}
 
 
void
EditListView::MouseUp(BPoint where)
{
	BListView::MouseUp(where);
	if (fLastMouseDown) {
		fLastMouseDown->MouseUp(where);
		fLastMouseDown = NULL;
	}
 
	int32 index = IndexOf(where);
	EditableListItem* handler = dynamic_cast<EditableListItem*>(ItemAt(index));
	if (handler == NULL)
		return;
 
	handler->MouseUp(where);
}
 
 
void
EditListView::FrameResized(float newWidth, float newHeight)
{
	Invalidate();
}
 
 
// #pragma mark -
 
 
FolderConfigWindow::FolderConfigWindow(BRect parent, const BMessage& settings)
	:
	BWindow(BRect(0, 0, 350, 350), B_TRANSLATE("IMAP Folders"),
		B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
		B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
	fSettings("in", settings)
{
	fQuotaView = new BStringView("quota view",
		B_TRANSLATE("Failed to fetch available storage."));
	fQuotaView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
		B_ALIGN_VERTICAL_CENTER));
	fQuotaView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
 
	fFolderListView = new EditListView(B_TRANSLATE("IMAP Folders"));
	fFolderListView->SetExplicitPreferredSize(BSize(B_SIZE_UNLIMITED,
		B_SIZE_UNLIMITED));
 
	BButton* cancelButton = new BButton("cancel", B_TRANSLATE("Cancel"),
		new BMessage(B_QUIT_REQUESTED));
	BButton* applyButton = new BButton("apply", B_TRANSLATE("Apply"),
		new BMessage(kMsgApplyButton));
 
	BLayoutBuilder::Group<>(this, B_VERTICAL)
		.SetInsets(B_USE_DEFAULT_SPACING)
		.Add(fQuotaView)
		.Add(new BScrollView("scroller", fFolderListView, 0, false, true))
		.AddGroup(B_HORIZONTAL)
			.AddGlue()
			.Add(cancelButton)
			.Add(applyButton);
 
	PostMessage(kMsgInit);
	CenterIn(parent);
}
 
 
void
FolderConfigWindow::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case kMsgInit:
			_LoadFolders();
			break;
 
		case kMsgApplyButton:
			_ApplyChanges();
			PostMessage(B_QUIT_REQUESTED);
			break;
 
		default:
			BWindow::MessageReceived(message);
	}
}
 
 
void
FolderConfigWindow::_LoadFolders()
{
	StatusWindow* statusWindow = new StatusWindow(this,
		B_TRANSLATE("Fetching IMAP folders, have patience" B_UTF8_ELLIPSIS));
	statusWindow->Show();
 
	status_t status = fProtocol.Connect(fSettings.ServerAddress(),
		fSettings.Username(), fSettings.Password(), fSettings.UseSSL());
	if (status != B_OK) {
		statusWindow->PostMessage(B_QUIT_REQUESTED);
 
		// Show error message on screen
		BString message = B_TRANSLATE("Could not connect to server "
			"\"%server%\":\n%error%");
		message.ReplaceFirst("%server%", fSettings.Server());
		message.ReplaceFirst("%error%", strerror(status));
		BAlert* alert = new BAlert("IMAP error", message.String(),
			B_TRANSLATE("Ok"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
		alert->Go();
 
		PostMessage(B_QUIT_REQUESTED);
		return;
	}
 
	// TODO: don't get all of them at once, but retrieve them level by level
	fFolderList.clear();
	BString separator;
	fProtocol.GetFolders(fFolderList, separator);
	for (size_t i = 0; i < fFolderList.size(); i++) {
		IMAP::FolderEntry& entry = fFolderList[i];
		CheckBoxItem* item = new CheckBoxItem(
			MailboxToFolderName(entry.folder, separator), entry.subscribed);
		fFolderListView->AddItem(item);
		item->SetListView(fFolderListView);
	}
 
	uint64 used, total;
	if (fProtocol.GetQuota(used, total) == B_OK) {
		char buffer[256];
		BString quotaString = "Server storage: ";
		quotaString += string_for_size(used, buffer, 256);
		quotaString += " / ";
		quotaString += string_for_size(total, buffer, 256);
		quotaString += " used.";
		fQuotaView->SetText(quotaString);
	}
 
	statusWindow->PostMessage(B_QUIT_REQUESTED);
}
 
 
void
FolderConfigWindow::_ApplyChanges()
{
	bool haveChanges = false;
	for (size_t i = 0; i < fFolderList.size(); i++) {
		IMAP::FolderEntry& entry = fFolderList[i];
		CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i);
		if (entry.subscribed != item->Checked()) {
			haveChanges = true;
			break;
		}
	}
	if (!haveChanges)
		return;
 
	StatusWindow* status = new StatusWindow(this,
		B_TRANSLATE("Update subcription of IMAP folders, have patience"
		B_UTF8_ELLIPSIS));
	status->Show();
 
	for (size_t i = 0; i < fFolderList.size(); i++) {
		IMAP::FolderEntry& entry = fFolderList[i];
		CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i);
		if (entry.subscribed && !item->Checked())
			fProtocol.UnsubscribeFolder(entry.folder);
		else if (!entry.subscribed && item->Checked())
			fProtocol.SubscribeFolder(entry.folder);
	}
 
	status->PostMessage(B_QUIT_REQUESTED);
}
 

V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.