/**
   * Program entry point. Displays the clock an main menu.
   *
   * @param args (not used)
   */
  public static void main(String[] args) {
    // lights on!
    jcontrol.io.Backlight.setBrightness(jcontrol.io.Backlight.MAX_BRIGHTNESS);

    lcd = new Display();
    key = new Keyboard();
    lcd.setFont(null);
    {
      String d = Management.getProperty("system.standbytimer");
      if (d != null) Management.powerOff(Integer.parseInt(d)); // get power off delay
    }
    int select = 0;
    Clock clk;
    for (; ; ) { // main loop
      lcd.clearDisplay();
      if (RTC.isAlarm()) {
        Time wakeup = new Time();
        RTC.getAlarm(wakeup);
        RTC.resetAlarm();
        Date alarmDate = Date.getNextDate(wakeup);
        if (alarmDate != null) {
          lcd.drawString(time2string(alarmDate.time), 0, 45);
          lcd.drawString("$".equals(alarmDate.text) ? "no subject" : alarmDate.text, 0, 56);
          updateAlarm(alarmDate.time);
        }
        clk = new Clock(45, 0, 40, 40, lcd, true); // show the alarm clock
      } else {
        clk = new Clock(35, 2, 60, 80, lcd, false); // show the normal clock
      }
      while (clk.isRunning()) {
        try {
          ThreadExt.sleep(500);
        } catch (InterruptedException e) {
          clk.stop();
        }
        if (key.getKey() != 0) clk.stop();
      }
      if (RTC.isAlarm()) continue; // begin from above...
      key:
      for (; ; ) { // the menu loop
        try {
          lcd.drawImage(new Resource("organizer.jcif"), 0, 0); // draw the background image
        } catch (java.io.IOException e) {
        }
        lcd.setFont(null);
        select =
            MenuSelector.listSelect(
                new String[] { // let the user select one of the specified menu items
                  "display time", "new appointment", "view appointments", "turn off"
                },
                select,
                lcd,
                key);
        Date date = null;
        switch (select) {
          case -1:
          case 0: // show the current time
            select = 0;
            break key;
          case 2: // adjust appointment
            date = listDates(); // show all stored dates and return date to change
            if (date == null) break;
          case 1: // new appointment
            date =
                DateInput.getDate(
                    lcd, key,
                    date); // get a new date (date==null) or modify an existing (date!=null)
            if (date != null) { // if the new date is valid
              try {
                date.store();
              } catch (IOException e) {
              }
              date = null; // this object may be garbage collected now
              updateAlarm(null);
            }
            break;
          case 3: // standby
            lcd.clearDisplay();
            while (key.getKey() != 0) {}
            jcontrol.system.Management.powerOff(-1);
            break;
        }
      }
    }
  }
  /**
   * Display the error message.
   *
   * @param code the error code.
   * @param message the error message (textual error description).
   * @param jpc points to the last bytecode to be executed when the error occured.
   * @param npc points to the machine instruction that was producing the error.
   * @param jpcext set if jpc points to external memory (e.g. flash).
   * @see onError(int code, int jpc, int npc, boolean jpcext).
   */
  public static void onError(int code, String message, int jpc, int npc, boolean jpcext) {

    // set current flash bank where error occured
    String jpcbank;
    if (jpcext) {
      jpcbank = Management.getProperty("system.userbank");
      if (jpcbank == null) jpcbank = "0";
    } else {
      // when jpcext is set, error was caused by ROM code
      jpcbank = "f";
    }

    // Print the error message to the RS232 port, using standard
    // communication parameters.
    // When the RS232-Terminal of the JControl/IDE is opened, this message
    // is used to
    // back-trace the error automatically.
    try {
      RS232 rs232 = new RS232();
      String errorString =
          "\nJControl/ErrorHandler: Code="
              .concat(String.valueOf(code))
              .concat(" NPC=0xf")
              .concat(Integer.toHexString(npc))
              .concat(" JPC=0x")
              .concat(jpcbank)
              .concat(Integer.toHexString(jpc))
              .concat("\n");
      rs232.write(errorString.getBytes(), 0, errorString.length());
      rs232.close();
    } catch (IOException e) {
    }

    // create keyboard instance
    m_keyboard = new Keyboard();

    // start timing thread to control buzzer signals and reboot
    Thread t = new Thread(new ErrorHandler());
    t.setDaemon(true);
    t.start();

    // generate error message string when not passed by caller
    if (message == null) {
      if ((code < 0) || (code >= ERROR_MESSAGES.length)) {
        message = "ErrorCode 0x".concat(Integer.toHexString(code));
      } else {
        message = ERROR_MESSAGES[code];
      }
    }

    // Print the error message to the RS232 port, using standard
    // communication parameters.
    // When the RS232-Terminal of the JControl/IDE is opened, this message
    // is used to
    // back-trace the error automatically.
    try {
      RS232 rs232 = new RS232();
      String errorString =
          "\nJControl/ErrorHandler: Code="
              .concat(String.valueOf(code))
              .concat(" NPC=0xf")
              .concat(Integer.toHexString(npc))
              .concat(" JPC=0x")
              .concat(jpcbank)
              .concat(Integer.toHexString(jpc))
              .concat("\n");
      rs232.write(errorString.getBytes(), 0, errorString.length());
      rs232.close();
    } catch (IOException e) {
    }

    // wait for keypress
    m_keyboard.read();
  }
 /**
  * This method will list all date entries found in persistent storage. The user may select one of
  * the entries and either change the selected entry or delete it or even quit without any change.
  *
  * @return Date object chosen or <code>null</code>.
  */
 private static Date listDates() {
   Date result = null;
   Date[] list = new Date[4]; // the entry list (the size will be changed if necessary)
   // two items for every tlv entry (date and subject message)
   {
     int index = 0;
     Date current = Date.getNextDate(new Time(2000, 1, 1, 0, 0, 0, 0)); // start searching here
     while (current != null) {
       if (index >= list.length) { // if list array is too small
         Date[] newlist = new Date[list.length + 4];
         for (int c = 0; c < list.length; c++) newlist[c] = list[c]; // enlarge the array
         list = newlist;
       }
       list[index++] = current;
       // the subject message
       Time next = current.time;
       current =
           Date.getNextDate(
               new Time(next.year, next.month, next.day, next.dow, next.hour, next.minute + 1, 0));
     }
     if (index <= 0) { // if there are no entries, return immediately
       return null;
     } else {
       if (index < list.length) { // trim to size
         Date[] newlist = new Date[index];
         for (int c = 0; c < index; c++) newlist[c] = list[c];
         list = newlist;
       }
     }
   } // all entries were found and set into the list
   { // now, create a choose dialog and wait for user input
     lcd.clearRect(1, 20, 126, 43); // clear the main rectangle area
     {
       String[] image = new String[] {"\u040E\u1F0E\u0400"}; // the up and down arrow image
       // create menu items
       lcd.drawImage(image, 22, 57, 5, 3, 0, 0); // up
       lcd.drawImage(image, 7, 57, 5, 3, 0, 2); // down
       lcd.drawString("modify", 35, 55); // change
       lcd.drawString("delete", 65, 55); // delete
       lcd.drawString("back", 100, 55); // quit
     }
     lcd.setDrawMode(Display.XOR);
     char c = 0;
     int shift = 0;
     int select = 0;
     for (int i = 0;
         i < 4;
         i++) { // draw four entries on the screen (there isn't enough space for more than four
                // :-))
       if (i + shift < list.length) {
         int pos = lcd.drawString(time2string(list[i + shift].time), 9, 21 + (i << 3)) + 14;
         if (!"$".equals(list[i + shift].text))
           lcd.drawString(list[i + shift].text, pos, 21 + (i << 3), 116 - pos, 10, 0, 0);
       }
     }
     lcd.fillRect(6, 20, 120, 8); // draw a rectangle around the first entry as it is selected
     // select one of the menu items
     lcd.fillRect(
         select == 0 ? 2 : select == 1 ? 17 : select == 2 ? 30 : select == 3 ? 60 : 95,
         55,
         select < 2 ? 15 : select == 3 ? 36 : 32,
         7);
     mainloop:
     for (; ; ) {
       c = key.read(); // wait for keypress
       switch (c) {
         case 'R': // select the next menu item
         case 'U':
         case 'u':
           lcd.fillRect(
               select == 0 ? 2 : select == 1 ? 17 : select == 2 ? 30 : select == 3 ? 60 : 95,
               55,
               select < 2 ? 15 : select == 3 ? 36 : 32,
               7);
           select++;
           select %= 5;
           lcd.fillRect(
               select == 0 ? 2 : select == 1 ? 17 : select == 2 ? 30 : select == 3 ? 60 : 95,
               55,
               select < 2 ? 15 : select == 3 ? 36 : 32,
               7);
           break;
         case 'L': // select the previous menu item
         case 'D':
         case 'd':
           lcd.fillRect(
               select == 0 ? 2 : select == 1 ? 17 : select == 2 ? 30 : select == 3 ? 60 : 95,
               55,
               select < 2 ? 15 : select == 3 ? 36 : 32,
               7);
           select += 4;
           select %= 5;
           lcd.fillRect(
               select == 0 ? 2 : select == 1 ? 17 : select == 2 ? 30 : select == 3 ? 60 : 95,
               55,
               select < 2 ? 15 : select == 3 ? 36 : 32,
               7);
           break;
         case 'S': // button was pressed -> do something
           switch (select) {
             case 0: // down
             case 1: // up
               // scroll around
               for (int i = 0; i < 4; i++) {
                 if (i + shift < list.length) {
                   int pos =
                       lcd.drawString(time2string(list[i + shift].time), 9, 21 + (i << 3)) + 14;
                   if (!"$".equals(list[i + shift].text))
                     lcd.drawString(list[i + shift].text, pos, 21 + (i << 3), 116 - pos, 10, 0, 0);
                 }
               }
               if (select == 1) shift = (shift <= 0 ? 0 : shift - 1);
               if (select == 0) shift = (shift >= list.length - 1 ? list.length - 1 : shift + 1);
               for (int i = 0; i < 4; i++) {
                 if (i + shift < list.length) {
                   int pos =
                       lcd.drawString(time2string(list[i + shift].time), 9, 21 + (i << 3)) + 14;
                   if (!"$".equals(list[i + shift].text))
                     lcd.drawString(list[i + shift].text, pos, 21 + (i << 3), 116 - pos, 10, 0, 0);
                 }
               }
               break;
             case 2: // change
               result = list[shift];
             case 3: // delete
               try {
                 list[shift].remove();
                 updateAlarm(null);
               } catch (IOException e) {
               }
             case 4:
               break mainloop; // quit without change
           }
           break;
       }
     }
     lcd.setDrawMode(Display.NORMAL);
     return result;
   }
 }