/*
 * Copyright 2006-2012, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */

#include "SwatchGroup.h"

#include <stdio.h>

#include "support_ui.h"
#include "ui_defines.h"
#include "rgb_hsv.h"

#include "AlphaSlider.h"
#include "ColorField.h"
#include "ColorPickerPanel.h"
#include "ColorSlider.h"
#include "CurrentColor.h"
#include "Group.h"
#include "SwatchView.h"


enum {
	MSG_SET_COLOR		= 'stcl',
	MSG_COLOR_PICKER	= 'clpk',
	MSG_ALPHA_SLIDER	= 'alps',
};


#define SWATCH_VIEW_WIDTH 20
#define SWATCH_VIEW_HEIGHT 15


SwatchGroup::SwatchGroup(BRect frame)
	:
	BView(frame, "style view", B_FOLLOW_NONE, 0),

	fCurrentColor(NULL),
	fIgnoreNotifications(false),

	fColorPickerPanel(NULL),
	fColorPickerMode(H_SELECTED),
	fColorPickerFrame(100.0, 100.0, 200.0, 200.0)
{
	frame = BRect(0, 0, 100, 15);
	fTopSwatchViews = new Group(frame, "top swatch group");
	fBottomSwatchViews = new Group(frame, "bottom swatch group");

	// create swatch views with rainbow default palette
	float h = 0;
	float s = 1.0;
	float v = 1.0;
	rgb_color color;
	color.alpha = 255;
	float r = 0.0f;
	float g = 0.0f;
	float b = 0.0f;
	for (int32 i = 0; i < 20; i++) {
		if (i < 10) {
			h = ((float)i / 9.0) * 6.0;
		} else {
			h = ((float)(i - 9) / 10.0) * 6.0;
			v = 0.5;
		}

		HSV_to_RGB(h, s, v, r, g, b);
		color.red = (uint8)(255.0 * r);
		color.green = (uint8)(255.0 * g);
		color.blue = (uint8)(255.0 * b);
		fSwatchViews[i] = new SwatchView("swatch", new BMessage(MSG_SET_COLOR),
			this, color, SWATCH_VIEW_WIDTH, SWATCH_VIEW_HEIGHT);

		fSwatchViews[i]->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);

		if (i < 10)
			fTopSwatchViews->AddChild(fSwatchViews[i]);
		else
			fBottomSwatchViews->AddChild(fSwatchViews[i]);
	}

	// create current color swatch view
	fCurrentColorSV = new SwatchView("current swatch",
		new BMessage(MSG_COLOR_PICKER), this, color, 28.0, 28.0);

	// When the color of this swatch changes via drag&drop, we want to
	// adopt it as current color.
	fCurrentColorSV->SetDroppedMessage(new BMessage(MSG_SET_COLOR));

	// create color field and slider
	fColorField = new ColorField(BPoint(0.0, 0.0), H_SELECTED,
		1.0, B_HORIZONTAL);
	fColorSlider = new ColorSlider(BPoint(0.0, 0.0), H_SELECTED,
		1.0, 1.0, B_HORIZONTAL);
	fAlphaSlider = new AlphaSlider(B_HORIZONTAL,
		new BMessage(MSG_ALPHA_SLIDER));

	// layout gui
	fTopSwatchViews->SetSpacing(0, 0);
	fTopSwatchViews->ResizeToPreferred();
	fTopSwatchViews->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
	fBottomSwatchViews->SetSpacing(0, 0);
	fBottomSwatchViews->ResizeToPreferred();
	fBottomSwatchViews->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);

	float paletteHeight = fBottomSwatchViews->Frame().Height()
		+ fTopSwatchViews->Frame().Height() + 1;

	fTopSwatchViews->MoveTo(paletteHeight + 2, 4);
	fBottomSwatchViews->MoveTo(paletteHeight + 2,
		fTopSwatchViews->Frame().bottom + 1);

	fCurrentColorSV->MoveTo(0, fTopSwatchViews->Frame().top);
	fCurrentColorSV->ResizeTo(paletteHeight, paletteHeight);
	fCurrentColorSV->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_TOP);

	float width = fTopSwatchViews->Frame().right
		- fCurrentColorSV->Frame().left;

	fColorField->ResizeTo(width, 40);
	fColorField->FrameResized(width, 40);
	fColorSlider->ResizeTo(width, 11);
	fColorSlider->FrameResized(width, 11);
	fAlphaSlider->ResizeTo(width, 11);
	fAlphaSlider->FrameResized(width, 11);

	fColorField->MoveTo(0, fBottomSwatchViews->Frame().bottom + 3);
	fColorSlider->MoveTo(0, fColorField->Frame().bottom + 1);
	fAlphaSlider->MoveTo(0, fColorSlider->Frame().bottom + 1);
	fAlphaSlider->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);

	// configure self
	ResizeTo(width, fAlphaSlider->Frame().bottom + 4);
	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);

	// add views
	AddChild(fCurrentColorSV);
	AddChild(fTopSwatchViews);
	AddChild(fBottomSwatchViews);
	AddChild(fColorField);
	AddChild(fColorSlider);
	AddChild(fAlphaSlider);
}


SwatchGroup::~SwatchGroup()
{
	SetCurrentColor(NULL);
}


void
SwatchGroup::ObjectChanged(const Observable* object)
{
	if (object != fCurrentColor || fIgnoreNotifications)
		return;

	rgb_color color = fCurrentColor->Color();

	float h, s, v;
	RGB_to_HSV(color.red / 255.0, color.green / 255.0, color.blue / 255.0,
		h, s, v);

	_SetColor(h, s, v, color.alpha);
}


// #pragma mark -


void
SwatchGroup::AttachedToWindow()
{
	fColorField->SetTarget(this);
	fColorSlider->SetTarget(this);
	fAlphaSlider->SetTarget(this);
}


void
SwatchGroup::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case MSG_SET_COLOR:
		{
			rgb_color color;
			if (restore_color_from_message(message, color) == B_OK) {
// TODO: fix color picker panel to respect alpha
if (message->HasRect("panel frame"))
color.alpha = fAlphaSlider->Value();
//
				if (fCurrentColor != NULL)
					fCurrentColor->SetColor(color);
			}
			// if message contains these fields,
			// then it comes from the color picker panel.
			// it also means the panel has died.
			BRect frame;
			SelectedColorMode mode;
			if (message->FindRect("panel frame", &frame) == B_OK
				&& message->FindInt32("panel mode", (int32*)&mode) == B_OK) {
				// message came from the color picker panel
				// we remember the settings of the panel for later
				fColorPickerFrame = frame;
				fColorPickerMode = mode;
				// color picker panel has quit
				fColorPickerPanel = NULL;
			}
			break;
		}

		case MSG_COLOR_FIELD:
		{
			// get h from color slider
			float h = ((255 - fColorSlider->Value()) / 255.0) * 6.0;
			float s, v;
			// s and v are comming from the message
			if (message->FindFloat("value", &s) == B_OK
				&& message->FindFloat("value", 1, &v) == B_OK) {
				_SetColor(h, s, v, fAlphaSlider->Value());
			}
			break;
		}

		case MSG_COLOR_SLIDER:
		{
			float h;
			float s, v;
			fColorSlider->GetOtherValues(&s, &v);
			// h is comming from the message
			if (message->FindFloat("value", &h) == B_OK)
				_SetColor(h, s, v, fAlphaSlider->Value());
			break;
		}

		case MSG_ALPHA_SLIDER:
		{
			float h = (1.0 - (float)fColorSlider->Value() / 255.0) * 6;
			float s, v;
			fColorSlider->GetOtherValues(&s, &v);
			_SetColor(h, s, v, fAlphaSlider->Value());
			break;
		}

		case MSG_COLOR_PICKER:
		{
			rgb_color color;
			if (restore_color_from_message(message, color) < B_OK)
				break;

			if (fColorPickerPanel == NULL) {
				fColorPickerPanel = new ColorPickerPanel(fColorPickerFrame,
					color, fColorPickerMode, Window(),
					new BMessage(MSG_SET_COLOR), this);
				fColorPickerPanel->Show();
			} else {
				if (fColorPickerPanel->Lock()) {
					fColorPickerPanel->SetColor(color);
					fColorPickerPanel->Activate();
					fColorPickerPanel->Unlock();
				}
			}
			break;
		}

		default:
			BView::MessageReceived(message);
			break;
	}
}


// #pragma mark -


void
SwatchGroup::SetCurrentColor(CurrentColor* color)
{
	if (fCurrentColor == color)
		return;

	if (fCurrentColor != NULL)
		fCurrentColor->RemoveObserver(this);

	fCurrentColor = color;

	if (fCurrentColor != NULL) {
		fCurrentColor->AddObserver(this);

		ObjectChanged(fCurrentColor);
	}
}


// #pragma mark -


void
SwatchGroup::_SetColor(rgb_color color)
{
	fCurrentColorSV->SetColor(color);
}


void
SwatchGroup::_SetColor(float h, float s, float v, uint8 a)
{
	float r = 0.0f;
	float g = 0.0f;
	float b = 0.0f;
	HSV_to_RGB(h, s, v, r, g, b);

	rgb_color color;
	color.red = (uint8)(r * 255.0);
	color.green = (uint8)(g * 255.0);
	color.blue = (uint8)(b * 255.0);
	color.alpha = a;

	if (!fColorField->IsTracking()) {
		fColorField->SetFixedValue(h);
		fColorField->SetMarkerToColor(color);
	}
	if (!fColorSlider->IsTracking()) {
		fColorSlider->SetOtherValues(s, v);
		fColorSlider->SetValue(255 - (int32)((h / 6.0) * 255.0 + 0.5));
	}
	if (!fAlphaSlider->IsTracking()) {
		fAlphaSlider->SetColor(color);
		fAlphaSlider->SetValue(a);
	}

	fIgnoreNotifications = true;

	if (fCurrentColor)
		fCurrentColor->SetColor(color);
	_SetColor(color);

	fIgnoreNotifications = false;
}
