/*

Copyright © 1994 Apple Computer, Inc. All Rights Reserved.
author Mike Engber

False Alarm v2

This sample illustrates how to set alarms on the Newton. Do not be put
off by the large size of the package - most of that space is from a cool
sound resource which gets played when the alarm goes off. If this is
causing you grief, you can easily replace the sound resource with one of
your own choosing.

Version History

v1 - initial release
v2 - rebuilt using new platorm file (new alarm stub)
   - bug fix - if the FalseAlarm app was closed when the alarm went
     off, DoAlarm blindly sent messages to a child view (that didn't
     exist since FalseAlarm was closed)
 */

It is IMPORTANT that you have a copy of AlarmPkgUtil and thoroughly
read AlarmPkgUtil.ReadMe before you start working with alarms.

Using the alarm functions may cause about ~4K of your user's internal
store to be permanently taken up (with alarm code). Currently, the alarm
functions are the only ones in the NTK platform file that do this
permanent installation of code. Read AlarmPkgUtil.ReadMe for details.


Alarm API Description

The alarm API provides a way for 3rd parties to set alarms ala the
built-in Calendar app. These alarms will execute at the specified time,
waking up the Newton if necessary.

AddAlarm(alarmKey,time,notifyArgs,callBackFn,callBackParams)
  return value - alarmKey
  alarmKey - A string used to uniquely identify the alarm. You
    should use your signature or app symbol as a suffix to guarantee
    uniqueness among different applications. e.g. "wakeUpReg:PIEDTS".
    If there is already an alarm with with the specified alarmKey,
    it is removed and replaced with the new alarm.
  time - Integer encoding the time of the alarm in minutes since
    midnight, January 1, 1904. (ala Time())
  notifyArgs - An array of 3 arguments to pass to Notify when the
    alarm goes off. (passing nil means Notify won't be called)
  callBackFn - A closure to be executed when your alarm goes off.
    (passing nil means no closure should be executed).
  callBackParams - parameters to be passed to callBackFn. (pass nil
    if no callBackFn is being used)
AddAlarm is used to register an alarm to execute at a specified time. The
alarm will wake up the Newton if necessary. You can specify a system
notify message to be displayed. You can take other actions by specifying
a closure.

The time an alarm executes is approximate. There may be multiple alarms
scheduled for the same time, if one of the alarms does something time
consuming it will delay the execution of other alarms (or take long
enough so that other alarms go off). In the case of simultaneously
scheduled alarms the execution order is unspecified.

Once an alarm "goes off," it's removed. So you only need to call
RemoveAlarm if you want to abort an alarm. If you want to change an
existing alarm, simply call AddAlarm with the same alarmKey. The
new version of the alarm will replace the old one. If you want to
schedule an alarm periodically, your callBackFn should call AddAlarm
to schedule the next occurence.

Debugging your callBackFn will be difficult. When it is called, any
exceptions raised will be caught and ignored by the alarm mechanism. I
suggest debugging your callBackFn's thoroughly before passing them to
AddAlarm.

Alarms are kept in a soup so they will persist across restarts and when
your package is removed. Note that this means the argument you pass will
be stored in a soup - which means they will be deeply copied. You should
be carful to avoid indirectly referencing large objects For example,
closures reference their lexical environment and self at the time they
are created. Functions defined in "Project Data" or evaluate slot should
be ok. Functions created in the NTK inspector are not (their lexical
environment is the Newton global variables).

Further note that when your alarm runs your app may not be open. In
fact, your app may not even be installed. You need to be sure and
handle these cases appropriately. e.g.
 if GetRoot().(kAppSymbol) then
  GetRoot().(kAppSymbol):DoSomething()
 else
  GetRoot():Notify(...)


RemoveAlarm(alarmKey)
  return value - nil if no alarm with that key was found. An unspecified
    non-nil value if the alarm was found and removed.
  alarmKey - The unique string passed to AddAlarm which identifies the alarm.
RemoveAlarm unschedules an alarm. If you want your app's alarms to
execute only if your app is installed, you'll need to call RemoveAlarm
in your remove script.


GetAlarm(alarmKey)
  return value - frame containing the 5 slots described below or
    nil if no alarm is found. This frame and it contents should
    not be modified Any other (undocumented) slots you find in
    it should be ignored.
      key:            the unique string which identifies the alarm,
      time:           the time at which the alarm will "go off"
      notifyArgs:     array of 3 argument for Notify (or nil),
      callBackFn:     closure (or nil),
      callBackParams: array of arguments for callBackFn (or nil)
  alarmKey - The unique string passed to AddAlarm which identifies the alarm.
GetAlarm returns information on the specified alarm.

GetAppAlarmKeys(alarmKeySuffix)
  return value - array of alarmKeys (strings)
  alarmKeySuffix - string - specifies a suffix with which all your
    alarmKeys end. E.g. ":PIEDTS" or ":AlarmSample1:PIEDTS" 
Returns an array of all an app's alarmKeys. They will be in
execution order (earliest to latest).


RemoveAppAlarms(alarmKeySuffix)
  return value - integer, the number of alarms removed
  alarmKeySuffix - string - specifies a suffix with which all your
    alarmKeys end. E.g. ":PIEDTS" or ":AlarmSample1:PIEDTS" 
Removes all of an app's alarms. This would be a good routine to call
in your RemoveScript if your alarms can't meaningfully execute when
your app is not installed.


Usage Notes:

It is intended that apps will be courteous in their usage of alarms.
Limiting apps to a single alarm seems too harsh. On the other hand, we
don't want apps scheduling a daily wake-up alarm for the next year by
scheduling 365 different alarms. Remember, each alarm you schedule takes
up space in the Newton's internal store.

Similarly, your alarm actions should be quick so you don't block
other alarms. If you need to do something time consuming or
repetative, use a deferred action or set up an idle script.

Note that idle scripts stop when the Newton goes to sleep. So using an
idle script can only ensure the user notice your alarm next time they
turn on the Newton. You can set more alarms to make sure the Newton
wakes up periodically and tries to get the user's attention. However,
each wake up uses power so you probably want to put a limit on this or
you will eventually drain their Newton.


