/*
Copyright (C) 2015 Runtime Revolution Ltd.

This file is part of LiveCode.

LiveCode is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free
Software Foundation.

LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with LiveCode.  If not see <http://www.gnu.org/licenses/>.  */

/*
This widget consists of an analogue clock. The clock has day and night style displays which
are used depending on the time. The period interpreted as day by the clock is controlled by
a pair of constants kStartDay and kEndDay, currently set at 6 and 20 respectively. Thus the
clock will display in night style between the hours of 8pm and 6am. The only settable property
implemented by this clock widget is the timeZone, an integer which adjusts the time displayed
relative to universal time (UTC).

Name: timeZone
Type: property
Syntax: set the timeZone of <widget> to <pTimeZone>
Syntax: get the timeZone of <widget>

Summary: The time zone of the clock widget.

pTimeZone (integer): An integer representing the timeZone in which to calculate the time.

Example:
// Make the clocks display the correct time in Japan and Panama respectively
set the timeZone of widget "Japan Clock" to 9
set the timeZone of widget "Panama Clock" to -5

Description:
The <timeZone> property determines what time is displayed on the clock. If the <timeZone> is 0, the clock will display
the universal time (UTC).

*>Note:* The timeZone is restricted to being an integer.

*/

-- declaring extension as widget, followed by identifier
widget com.livecode.widget.clock
--

-- dependency declarations
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
--

-- adding metadata to ensure the extension displays correctly in livecode
metadata title is "Clock"
metadata author is "LiveCode"
metadata version is "1.0.3"
metadata preferredSize is "76,76"
metadata svgicon is "M31.7,0C14.2,0,0,14.2,0,31.7s14.2,31.7,31.7,31.7s31.7-14.2,31.7-31.7S49.2,0,31.7,0z M30.2,3.2h2.8v7.3h-2.8V3.2zM10.4,33.1H3.1v-2.8h7.3V33.1z M12.5,52.9l-2-2l5.2-5.2l2,2L12.5,52.9z M15.7,17.7l-5.2-5.2l2-2l5.2,5.2L15.7,17.7z M33.1,60.2h-2.8v-7.3h2.8V60.2z M42.3,45.4L31.8,34.9c0,0-0.1,0-0.1,0c-1.7,0-3-1.3-3-3c0-1.1,0.6-2.1,1.6-2.6V12.9h2.8v16.3c0.9,0.5,1.6,1.5,1.6,2.6c0,0.2,0,0.4-0.1,0.5L45,42.8L42.3,45.4z M50.8,52.9l-5.2-5.2l2-2l5.2,5.2L50.8,52.9z M47.7,17.7l-2-2l5.2-5.2l2,2L47.7,17.7z M52.9,33.1v-2.8h7.3v2.8H52.9z"

metadata timeZone.default is "0"
--

-- property declarations
property isDay 			get mIsDay
metadata isDay.user_visible is "false"
property timeZone		get mTimeZone		set setTimeZone
metadata timeZone.editor is "com.livecode.pi.timezone"
metadata timeZone.label is "Timezone"
--

-- private instance variables
private variable mIsDay			as Boolean
private variable mTimeZone		as Number
private variable mCurrentTime 	as List
private variable mFontSize 		as Real
--

-- constants
constant kStartDay is 6
constant kEndDay is 20
constant kWhite is [1,1,1]
constant kBlack is [0,0,0]
constant kRed is [1,0,0]
--

public handler OnSave(out rProperties as Array)
	put the empty array into rProperties

	put mTimeZone into rProperties["timeZone"]

	return rProperties
end handler

public handler OnLoad(in pProperties as Array)
	put pProperties["timeZone"] into mTimeZone
end handler

-- called when widget is created
public handler OnCreate()
	put 0 into mTimeZone
	put false into mIsDay
	put [0,0,0] into mCurrentTime
	put 12 into mFontSize
end handler

public handler OnOpen()
	put getTimeComponents() into mCurrentTime
	schedule timer in computeNextTimer() seconds
end handler

-- called whenever LiveCode needs to redraw the widget
public handler OnPaint()

	-- draw the face of the clock
    set the paint of this canvas to getPaint("clock face")
    fill getPath("clock face") on this canvas
    --

    -- draw the outer circle nub in the middle to cover the ends of the hands
    set the paint of this canvas to getPaint("outer nub")
    fill getPath("outer nub") on this canvas
    --

    -- draw the numbers
    set the paint of this canvas to getPaint("numbers")
   	set the font of this canvas to font "Helvetica-Light" at size mFontSize

   	variable tX as Integer
   	variable tX_string as String
   	variable tLabel as String

   	repeat with tX from 1 up to 12
   		put tX formatted as string into tX_string
   		if tX < 10 then
   			put the first char of tX_string into tLabel
   		else
   			put char 1 to 2 of tX_string into tLabel
   		end if
   		fill text tLabel at getPoint(tLabel) on this canvas
   	end repeat
    --

    -- draw the minute hand
    -- rotate canvas so that minute hand is assumed to be pointing up
    save state of this canvas
    set the stroke width of this canvas to getStrokeWidth("minute hand")
    set the paint of this canvas to getPaint("minute hand")

    variable tMinuteHand as Path
    put getPath("minute hand") into tMinuteHand

    translate this canvas by [(my width)/2, (my height)/2]
    rotate this canvas by 6*(element 2 of mCurrentTime)
    stroke tMinuteHand on this canvas
    restore state of this canvas
    --

    -- draw the hour hand
    -- rotate canvas so that hour hand is assumed to be pointing up
    save state of this canvas
    set the stroke width of this canvas to getStrokeWidth("hour hand")
    set the paint of this canvas to getPaint("hour hand")

    variable tHourHand as Path
    put getPath("hour hand") into tHourHand

    translate this canvas by [(my width)/2, (my height)/2]
    rotate this canvas by 30*((element 1 of mCurrentTime) + (element 2 of mCurrentTime/60)) --mikey added fractional hour
    stroke tHourHand on this canvas
    restore state of this canvas
    --

    -- draw the second hand in red
    -- rotate canvas so that second hand is assumed to be pointing up
    save state of this canvas
    set the stroke width of this canvas to getStrokeWidth("second hand")
    set the paint of this canvas to getPaint("second hand")

    variable tSecondHand as Path
    put getPath("second hand") into tSecondHand

    translate this canvas by [(my width)/2, (my height)/2]
    rotate this canvas by 6*(element 3 of mCurrentTime)
  	stroke tSecondHand on this canvas
    restore state of this canvas
    --

    -- draw the inner circle nub in the middle to cover the ends of the second hand
    set the paint of this canvas to getPaint("inner nub")
    fill getPath ("inner nub") on this canvas
    --

end handler

handler getTimeComponents() returns List
	-- Fetch the local date.
    -- This a list in the form [day, month, year, hours,minutes,seconds]
    variable tDate as List
    put the local date into tDate

    -- We only want the time, so delete the first 3 elements.
    delete element 1 to 3 of tDate

    -- Adjust for the timezone.
	variable tHour as Number
    put element 1 of tDate into tHour
    put (tHour + mTimeZone) mod 24 into tHour

    -- Compute whether it is 'day' or 'night'
	if tHour > kStartDay and tHour < kEndDay then
		put true into mIsDay
	else
		put false into mIsDay
	end if

    put tHour into element 1 of tDate

	return tDate
end handler

handler computeNextTimer() returns Number
    get the universal time
    return 1 - (the result - the floor of the result)
end handler

public handler OnTimer()
	-- the seconds have changed so fetch the new time
	put getTimeComponents() into mCurrentTime
	redraw all
	schedule timer in computeNextTimer() seconds
end handler

private handler getPaint(in pObject as String) returns Paint

	if pObject is "clock face" then
		if mIsDay is true then
			return solid paint with color [224/255, 224/255, 224/255, 0.25]
		else
			return solid paint with color kBlack
		end if

	else if pObject is "second hand" or pObject is "inner nub" then
		return solid paint with color kRed

	else
		if mIsDay is true then
			return solid paint with color kBlack
		else
			return solid paint with color kWhite
		end if

	end if
end handler

private handler getPath(in pString as String) returns Path

	if pString is "clock face" then
		put (my width)/10 into mFontSize
		return circle path centered at point [(my width)/2, (my height)/2] with radius (my width)/2

	else if pString is "second hand" or pString is "minute hand" then
		return line path from point [0, 0] to point [0, -0.7*(my width)/2]

	else if pString is "hour hand" then
		return line path from point [0,0] to point [0, -0.45*(my width/2)]

	else if pString is "outer nub" then
		return circle path centered at point [(my width)/2, (my height)/2] with radius 0.06*(my width/2)

	else if pString is "inner nub" then
		return circle path centered at point [(my width)/2, (my height)/2] with radius 0.02*(my width/2)

	end if
end handler

private handler getPoint(in pString as String) returns Point
	if pString is "1" then
		return point [(my width)/2+0.35*(my width)/2, (my height)/2-0.65*(my width)/2]
	else if pString is "2" then
		return point [(my width)/2+0.65*(my width)/2, (my height)/2-0.35*(my width)/2]
	else if pString is "3" then
		return point [(my width)/2+0.75*(my width)/2, (my height)/2+mFontSize/2]
	else if pString is "4" then
		return point [(my width)/2+0.65*(my width)/2, (my height)/2+0.35*(my width)/2 + 3*mFontSize/4]
	else if pString is "5" then
		return point [(my width)/2+0.35*(my width)/2, (my height)/2+0.65*(my width)/2 + 3*mFontSize/4]
	else if pString is "6" then
		return point [(my width)/2-mFontSize/4, (my height)/2+0.75*(my width)/2 + 3*mFontSize/4]
	else if pString is "7" then
		return point [(my width)/2-0.35*(my width)/2 - mFontSize/2, (my height)/2+0.65*(my width)/2 + 3*mFontSize/4]
	else if pString is "8" then
		return point [(my width)/2-0.65*(my width)/2 - mFontSize/2, (my height)/2+0.35*(my width)/2 + 3*mFontSize/4]
	else if pString is "9" then
		return point [(my width)/2-0.75*(my width)/2 - mFontSize/2, (my height)/2+mFontSize/2]
	else if pString is "10" then
		return point [(my width)/2-0.65*(my width)/2 - 3*mFontSize/4, (my height)/2-0.35*(my width)/2]
	else if pString is "11" then
		return point [(my width)/2-0.35*(my width)/2 - 3*mFontSize/4, (my height)/2-0.65*(my width)/2]
	else if pString is "12" then
		return point [(my width)/2-mFontSize/2, (my height)/2-0.75*(my width)/2]
	end if
end handler

private handler getStrokeWidth(in pHand as String) returns Real
	if pHand is "minute hand" or pHand is "hour hand" then
		return (my width/2)*0.03

	else if pHand is "second hand" then
		return (my width/2)*0.015

	end if
end handler

public handler setTimeZone(in pTimeZone as Number) returns nothing
	put pTimeZone into mTimeZone
	put getTimeComponents() into mCurrentTime
	redraw all
end handler

end widget
