/*
 * Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
 * Copyright 2009, Pier Luigi Fiorini.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
 *		Brian Hill, supernova@tycho.email
 */
 
#include <stdio.h>
#include <stdlib.h>
 
#include <vector>
 
#include <Alert.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Font.h>
#include <LayoutBuilder.h>
#include <Node.h>
#include <Path.h>
#include <Query.h>
#include <Roster.h>
#include <String.h>
#include <SymLink.h>
#include <Volume.h>
#include <VolumeRoster.h>
 
#include <notification/Notifications.h>
 
#include "GeneralView.h"
#include "NotificationsConstants.h"
#include "SettingsHost.h"
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "GeneralView"
 
const uint32 kToggleNotifications = '_TSR';
const uint32 kWidthChanged = '_WIC';
const uint32 kTimeoutChanged = '_TIC';
const uint32 kPositionChanged = '_NPC';
const uint32 kServerChangeTriggered = '_SCT';
const BString kSampleMessageID("NotificationsSample");
 
 
static int32
notification_position_to_index(uint32 notification_position) {
	if (notification_position == B_FOLLOW_NONE)
		return 0;
	else if (notification_position == (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM))
		return 1;
	else if (notification_position == (B_FOLLOW_LEFT | B_FOLLOW_BOTTOM))
		return 2;
	else if (notification_position == (B_FOLLOW_RIGHT | B_FOLLOW_TOP))
		return 3;
	else if (notification_position == (B_FOLLOW_LEFT | B_FOLLOW_TOP))
		return 4;
	return 0;
}
 
 
GeneralView::GeneralView(SettingsHost* host)
	:
	SettingsPane("general", host)
{
	// Notification server
	fNotificationBox = new BCheckBox("server",
		B_TRANSLATE("Enable notifications"),
		new BMessage(kToggleNotifications));
	BBox* box = new BBox("box");
	box->SetLabel(fNotificationBox);
 
	// Window width
	int32 minWidth = int32(kMinimumWidth / kWidthStep);
	int32 maxWidth = int32(kMaximumWidth / kWidthStep);
	fWidthSlider = new BSlider("width", B_TRANSLATE("Window width:"),
		new BMessage(kWidthChanged), minWidth, maxWidth, B_HORIZONTAL);
	fWidthSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
	fWidthSlider->SetHashMarkCount(maxWidth - minWidth + 1);
	BString minWidthLabel;
	minWidthLabel << int32(kMinimumWidth);
	BString maxWidthLabel;
	maxWidthLabel << int32(kMaximumWidth);
	fWidthSlider->SetLimitLabels(
		B_TRANSLATE_COMMENT(minWidthLabel.String(), "Slider low text"),
		B_TRANSLATE_COMMENT(maxWidthLabel.String(), "Slider high text"));
 
	// Display time
	fDurationSlider = new BSlider("duration", B_TRANSLATE("Duration:"),
		new BMessage(kTimeoutChanged), kMinimumTimeout, kMaximumTimeout,
		B_HORIZONTAL);
	fDurationSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
	fDurationSlider->SetHashMarkCount(kMaximumTimeout - kMinimumTimeout + 1);
	BString minLabel;
	minLabel << kMinimumTimeout;
	BString maxLabel;
	maxLabel << kMaximumTimeout;
	fDurationSlider->SetLimitLabels(
		B_TRANSLATE_COMMENT(minLabel.String(), "Slider low text"),
		B_TRANSLATE_COMMENT(maxLabel.String(), "Slider high text"));
 
	// Notification Position
	fPositionMenu = new BPopUpMenu(B_TRANSLATE("Follow Deskbar"));
	const char* positionLabels[] = {
		B_TRANSLATE_MARK("Follow Deskbar"),
		B_TRANSLATE_MARK("Lower right"),
		B_TRANSLATE_MARK("Lower left"),
		B_TRANSLATE_MARK("Upper right"),
		B_TRANSLATE_MARK("Upper left")
	};
	const uint32 positions[] = {
		B_FOLLOW_DESKBAR,                   // Follow Deskbar
		B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT,   // Lower right
		B_FOLLOW_BOTTOM | B_FOLLOW_LEFT,    // Lower left
		B_FOLLOW_TOP    | B_FOLLOW_RIGHT,   // Upper right
		B_FOLLOW_TOP    | B_FOLLOW_LEFT     // Upper left
	};
	for (int i=0; i < 5; i++) {
		BMessage* message = new BMessage(kPositionChanged);
		message->AddInt32(kNotificationPositionName, positions[i]);
 
		fPositionMenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(
			positionLabels[i]), message));
	}
	BMenuField* positionField = new BMenuField(B_TRANSLATE("Position:"), 
		fPositionMenu);
 
	box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
		.SetInsets(B_USE_DEFAULT_SPACING)
		.Add(fWidthSlider)
		.Add(fDurationSlider)
		.Add(positionField)
		.AddGlue()
		.View());
	
	BLayoutBuilder::Group<>(this, B_VERTICAL)
		.SetInsets(B_USE_WINDOW_SPACING)
		.Add(box)
	.End();
}
 
 
void
GeneralView::AttachedToWindow()
{
	BView::AttachedToWindow();
	fNotificationBox->SetTarget(this);
	fWidthSlider->SetTarget(this);
	fDurationSlider->SetTarget(this);
	fPositionMenu->SetTargetForItems(this);
}
 
 
void
GeneralView::MessageReceived(BMessage* msg)
{
	switch (msg->what) {
		case kToggleNotifications:
		{
			SettingsPane::SettingsChanged(false);
			_EnableControls();
			break;
		}
		case kWidthChanged: {
			int32 value = fWidthSlider->Value() * 50;
			_SetWidthLabel(value);
			SettingsPane::SettingsChanged(true);
			break;
		}
		case kTimeoutChanged:
		{
			int32 value = fDurationSlider->Value();
			_SetTimeoutLabel(value);
			SettingsPane::SettingsChanged(true);
			break;
		}
		case kPositionChanged:
		{
			int32 position;
			if (msg->FindInt32(kNotificationPositionName, &position) == B_OK) {
				fNewPosition = position;
				SettingsPane::SettingsChanged(true);
			}
			break;
		}
		default:
			BView::MessageReceived(msg);
			break;
	}
}
 
 
status_t
GeneralView::Load(BMessage& settings)
{
	bool autoStart = settings.GetBool(kAutoStartName, true);
	fNotificationBox->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
 
	if (settings.FindFloat(kWidthName, &fOriginalWidth) != B_OK
		|| fOriginalWidth > kMaximumWidth
		|| fOriginalWidth < kMinimumWidth)
		fOriginalWidth = kDefaultWidth;
 
	if (settings.FindInt32(kTimeoutName, &fOriginalTimeout) != B_OK
		|| fOriginalTimeout > kMaximumTimeout
		|| fOriginalTimeout < kMinimumTimeout)
		fOriginalTimeout = kDefaultTimeout;
// TODO need to save again if values outside of expected range
	int32 setting;
	if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
		fOriginalIconSize = kDefaultIconSize;
	else
		fOriginalIconSize = (icon_size)setting;
 
	int32 position;
	if (settings.FindInt32(kNotificationPositionName, &position) != B_OK)
		fOriginalPosition = kDefaultNotificationPosition;
	else
		fOriginalPosition = position;
 
	_EnableControls();
	
	return Revert();
}
 
 
status_t
GeneralView::Save(BMessage& settings)
{
	bool autoStart = (fNotificationBox->Value() == B_CONTROL_ON);
	settings.AddBool(kAutoStartName, autoStart);
 
	int32 timeout = fDurationSlider->Value();
	settings.AddInt32(kTimeoutName, timeout);
 
	float width = fWidthSlider->Value() * 50;
	settings.AddFloat(kWidthName, width);
 
	icon_size iconSize = B_LARGE_ICON;
	settings.AddInt32(kIconSizeName, (int32)iconSize);
 
	settings.AddInt32(kNotificationPositionName, (int32)fNewPosition);
 
	return B_OK;
}
 
 
status_t
GeneralView::Revert()
{
	fDurationSlider->SetValue(fOriginalTimeout);
	_SetTimeoutLabel(fOriginalTimeout);
	
	fWidthSlider->SetValue(fOriginalWidth / 50);
	_SetWidthLabel(fOriginalWidth);
 
	fNewPosition = fOriginalPosition;
	BMenuItem* item = fPositionMenu->ItemAt(
		notification_position_to_index(fNewPosition));
	if (item != NULL)
		item->SetMarked(true);
	
	return B_OK;
}
 
 
bool
GeneralView::RevertPossible()
{
	int32 timeout = fDurationSlider->Value();
	if (fOriginalTimeout != timeout)
		return true;
	
	int32 width = fWidthSlider->Value() * 50;
	if (fOriginalWidth != width)
		return true;
 
	if (fOriginalPosition != fNewPosition)
		return true;
 
	return false;
}
 
 
status_t
GeneralView::Defaults()
{
	fDurationSlider->SetValue(kDefaultTimeout);
	_SetTimeoutLabel(kDefaultTimeout);
 
	fWidthSlider->SetValue(kDefaultWidth / 50);
	_SetWidthLabel(kDefaultWidth);
 
	fNewPosition = kDefaultNotificationPosition;
	BMenuItem* item = fPositionMenu->ItemAt(
		notification_position_to_index(fNewPosition));
	if (item != NULL)
		item->SetMarked(true);
 
	return B_OK;
}
 
 
bool
GeneralView::DefaultsPossible()
{
	int32 timeout = fDurationSlider->Value();
	if (kDefaultTimeout != timeout)
		return true;
 
	int32 width = fWidthSlider->Value() * 50;
	if (kDefaultWidth != width)
		return true;
 
	if (kDefaultNotificationPosition != fNewPosition)
		return true;
	
	return false;
}
 
 
bool
GeneralView::UseDefaultRevertButtons()
{
	return true;
}
 
 
void
GeneralView::_EnableControls()
{
	bool enabled = fNotificationBox->Value() == B_CONTROL_ON;
	fWidthSlider->SetEnabled(enabled);
	fDurationSlider->SetEnabled(enabled);
	BMenuItem* item = fPositionMenu->ItemAt(
		notification_position_to_index(fOriginalPosition));
	if (item != NULL)
		item->SetMarked(true);
}
 
 
void
GeneralView::_SetTimeoutLabel(int32 value)
{
	BString label(B_TRANSLATE("Timeout:"));
	label.Append(" ");
	label << value;
	label.Append(" ").Append(B_TRANSLATE("seconds"));
	fDurationSlider->SetLabel(label.String());
}
 
 
void
GeneralView::_SetWidthLabel(int32 value)
{
	BString label(B_TRANSLATE("Width:"));
	label.Append(" ");
	label << value;
	label.Append(" ").Append(B_TRANSLATE("pixels"));
	fWidthSlider->SetLabel(label.String());
}
 
 
bool
GeneralView::_IsServerRunning()
{
	return be_roster->IsRunning(kNotificationServerSignature);
}

V773 Visibility scope of the 'message' pointer was exited without releasing the memory. A memory leak is possible.