/*
 * Copyright 1999, Be Incorporated. All Rights Reserved.
 * This file may be used under the terms of the Be Sample Code License.
 *
 */

#include "clock.h"
#include "cl_view.h"


#include <Alert.h>
#include <Bitmap.h>
#include <Debug.h>
#include <Dragger.h>
#include <Entry.h>
#include <Resources.h>
#include <Roster.h>


#include <time.h>


TOffscreenView::TOffscreenView(BRect frame, const char *name, short mRadius,
		short hRadius, short offset, long face, bool show)
	: BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW),
	  fHours(0),
	  fMinutes(0),
	  fSeconds(0),
	  fOffset(offset),
	  fHoursRadius(hRadius),
	  fMinutesRadius(mRadius),
	  fFace(face),
	  fShowSeconds(show),
	  fInner(NULL),
	  fCenter(NULL)
{
	for (short i = 0; i <= 8; i++)
		fClockFace[i] = NULL;

	status_t error;
	BResources rsrcs;
	error = rsrcs.SetToImage(&&dummy_label);
dummy_label:
	if (error == B_OK) {
		size_t len;
		void *picH;
		BRect theRect(0, 0, 82, 82);
		for (short loop = 0; loop <= 8; loop++) {
			if ((picH = rsrcs.FindResource('PICT', loop + 4, &len))) {
				fClockFace[loop] = new BBitmap(theRect, B_CMAP8);
				fClockFace[loop]->SetBits(picH, len, 0, B_CMAP8);
				free(picH);
			}
		}

		theRect.Set(0,0,15,15);
		if ((picH = rsrcs.FindResource(B_MINI_ICON_TYPE, "center", &len))) {
			fCenter = new BBitmap(theRect, B_CMAP8);
			fCenter->SetBits(picH, len, 0, B_CMAP8);
			free(picH);
		}

		theRect.Set(0,0,2,2);
		if ((picH = rsrcs.FindResource('PICT', 13, &len))) {
			fInner = new BBitmap(theRect, B_CMAP8);
			fInner->SetBits(picH, len, 0, B_CMAP8);
			free(picH);
		}
	}

	float x, y;
	float counter;
	short index = 0;

	// Generate minutes points array
	for (counter = 90; counter >= 0; counter -= 6, index++) {
		x = mRadius * cos(((360 - counter)/180.0) * 3.1415);
		x += 41;
		y = mRadius * sin(((360 - counter)/180.0) * 3.1415);
		y += 41;
		fMinutePoints[index].Set(x,y);
		x = hRadius * cos(((360 - counter)/180.0) * 3.1415);
		x += 41;
		y = hRadius * sin(((360 - counter)/180.0) * 3.1415);
		y += 41;
		fHourPoints[index].Set(x,y);
	}

	for (counter = 354; counter > 90; counter -= 6,index++) {
		x = mRadius * cos(((360 - counter)/180.0) * 3.1415);
		x += 41;
		y = mRadius * sin(((360 - counter)/180.0) * 3.1415);
		y += 41;
		fMinutePoints[index].Set(x,y);
		x = hRadius * cos(((360 - counter)/180.0) * 3.1415);
		x += 41;
		y = hRadius * sin(((360 - counter)/180.0) * 3.1415);
		y += 41;
		fHourPoints[index].Set(x,y);
	}
}


void
TOffscreenView::NextFace()
{
	fFace++;
	if (fFace > 8)
		fFace = 1;
};


void
TOffscreenView::DrawX()
{
	ASSERT(Window());

	if (Window()->Lock()) {
		if (fClockFace[fFace] != NULL)
			DrawBitmap(fClockFace[fFace], BPoint(0, 0));
#if defined( HoG_GENODE )
		else
		{
			// The BeOS/Haiku Clock does not handle gracefully a lack of bitmap; fix that:
			SetHighColor(65, 0, 0);
			FillRect(Bounds());
		}
#endif  // ~HoG_GENODE

		//
		// Draw hands
		//
		SetHighColor(0, 0, 0);
		int32 hours = fHours;
		if (hours >= 12)
			hours -= 12;
		hours *= 5;
		hours += (fMinutes / 12);
		SetDrawingMode(B_OP_OVER);
		StrokeLine(BPoint(fOffset, fOffset), fHourPoints[hours]);

		if (fCenter != NULL)
			DrawBitmap(fCenter, BPoint(fOffset - 3, fOffset - 3));
		StrokeLine(BPoint(fOffset, fOffset), fMinutePoints[fMinutes]);
		SetHighColor(180, 180, 180);
		if (fShowSeconds)
			StrokeLine(BPoint(fOffset, fOffset), fMinutePoints[fSeconds]);
		SetDrawingMode(B_OP_COPY);
		if (fInner != NULL)
			DrawBitmap(fInner, BPoint(fOffset - 1, fOffset - 1));
		Sync();
		Window()->Unlock();
	}
}


TOffscreenView::~TOffscreenView()
{
	for (int32 counter = 0; counter <= 8; counter++)
		delete fClockFace[counter];
};


//	#pragma mark -


TOnscreenView::TOnscreenView(BRect rect, const char *title, short mRadius, 
		short hRadius, short offset)
	: BView(rect, title, B_FOLLOW_NONE, 
		B_WILL_DRAW | B_PULSE_NEEDED | B_DRAW_ON_CHILDREN),
	  fOffscreen(NULL),
	  fOffscreenView(NULL)
{
	InitObject(rect, mRadius, hRadius, offset, 1, TRUE);

	rect.OffsetTo(B_ORIGIN);
	rect.top = rect.bottom - 7;
	rect.left = rect.right - 7;
	BDragger *dw = new BDragger(rect, this);
	AddChild(dw);
}


void
TOnscreenView::InitObject(BRect rect, short mRadius, short hRadius,
	short offset, long face, bool show)
{
	fOffscreenView = new TOffscreenView(rect, "freqd", mRadius, hRadius, offset, face, show);
	fOffscreen = new BBitmap(rect, B_CMAP8, true);
	if (fOffscreen != NULL && fOffscreen->Lock()) {
		fOffscreen->AddChild(fOffscreenView);
		fOffscreen->Unlock();

		fOffscreenView->DrawX();
	}
}


TOnscreenView::~TOnscreenView()
{
	delete fOffscreen;
}


TOnscreenView::TOnscreenView(BMessage *data)
	: BView(data),
	  fOffscreen(NULL),
	  fOffscreenView(NULL)
{
	InitObject(data->FindRect("bounds"), data->FindInt32("mRadius"),
		data->FindInt32("hRadius"), data->FindInt32("offset"),
		data->FindInt32("face"), data->FindBool("seconds"));
}


status_t
TOnscreenView::Archive(BMessage *data, bool deep) const
{
	status_t status = BView::Archive(data, deep);
	if (status == B_OK)
		status = data->AddString("add_on", kAppSignature);

	if (status == B_OK)
		status = data->AddRect("bounds", Bounds());

	if (status == B_OK)
		status = data->AddInt32("mRadius", fOffscreenView->fMinutesRadius);

	if (status == B_OK)
		status = data->AddInt32("hRadius", fOffscreenView->fHoursRadius);

	if (status == B_OK)
		status = data->AddInt32("offset", fOffscreenView->fOffset);

	if (status == B_OK)
		status = data->AddBool("seconds", fOffscreenView->fShowSeconds);

	if (status == B_OK)
		status = data->AddInt32("face", fOffscreenView->fFace);

	return status;
}


BArchivable *
TOnscreenView::Instantiate(BMessage *data)
{
	if (!validate_instantiation(data, "TOnscreenView"))
		return NULL;
	return new TOnscreenView(data);
}


void
TOnscreenView::Pulse()
{
	ASSERT(fOffscreen);
	ASSERT(fOffscreenView);

	time_t current = time(0);
	struct tm *loctime = localtime(&current);

	short hours = loctime->tm_hour;
	short minutes = loctime->tm_min;
	short seconds = loctime->tm_sec;

	if ((fOffscreenView->fShowSeconds && (seconds != fOffscreenView->fSeconds))
		|| (minutes != fOffscreenView->fMinutes)) {
			fOffscreenView->fHours = hours;
			fOffscreenView->fMinutes = minutes;
			fOffscreenView->fSeconds = seconds;
			BRect b = Bounds();
			b.InsetBy(12,12);
			Draw(b);
#if defined( HoG_GENODE )
			// We don't implement "out of band" drawing like BeOS/Haiku, so need to notify the window that Draw() was called:
			Invalidate();
#endif  // ~HoG_GENODE
	}
}


void
TOnscreenView::UseFace(short face)
{
	fOffscreenView->fFace = face;
	BRect b = Bounds();
	b.InsetBy(12,12);
	Draw(b);
}


void
TOnscreenView::ShowSecs(bool secs)
{
	fOffscreenView->fShowSeconds = secs;
	BRect b = Bounds();
	b.InsetBy(12,12);
	Invalidate(b);
}


short
TOnscreenView::ReturnFace()
{
	return fOffscreenView->fFace;
}


short
TOnscreenView::ReturnSeconds()
{
	return fOffscreenView->fShowSeconds;
}


void
TOnscreenView::Draw(BRect rect)
{
	ASSERT(fOffscreen);
	ASSERT(fOffscreenView);

	if (fOffscreen->Lock()) {
		// Composite the clock offscreen...
		fOffscreenView->DrawX();
		DrawBitmap(fOffscreen, rect, rect);
		fOffscreen->Unlock();
	}
};


void
TOnscreenView::MouseDown( BPoint point )
{
	BPoint	cursor;
	uint32	buttons;
	BRect	bounds = Bounds();

	GetMouse(&cursor,&buttons);
	if (buttons & B_SECONDARY_MOUSE_BUTTON) {
		fOffscreenView->fShowSeconds = !fOffscreenView->fShowSeconds;
		bounds.InsetBy(12,12);
		Invalidate(bounds);
	} else {
		fOffscreenView->NextFace();
		Invalidate(bounds);
		BView *child = ChildAt(0);
		if (child)
			child->Invalidate();
	}
};


void
TOnscreenView::MessageReceived(BMessage *msg)
{
	switch(msg->what) {
		case B_ABOUT_REQUESTED:
		{
			BAlert *alert = new BAlert("About Clock", 
				"Clock (The Replicant version)\n\n(C)2002, 2003 OpenBeOS,\n"
				"2004 - 2007, Haiku, Inc.\n\nOriginally coded  by the folks "
				"at Be.\n  Copyright Be Inc., 1991 - 1998", "OK");
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
		}	break;

		default:
			BView::MessageReceived(msg);
	}
}

