/**
   * Create an all-day appointment on the server
   *
   * @param account The appointment organizer
   * @param date The appointment start date
   * @param duration The appointment duration (in days)
   * @param subject The appointment subject
   * @param content The appointment text content
   * @param location The appointment location (null if none)
   * @param attendees A list of attendees (null for none)
   * @return
   * @throws HarnessException
   */
  public static AppointmentItem createAppointmentAllDay(
      ZimbraAccount account,
      Calendar date,
      int duration,
      String subject,
      String content,
      String location,
      List<ZimbraAccount> attendees)
      throws HarnessException {

    // If location is null, don't specify the loc attribute
    String loc = (location == null ? "" : "loc='" + location + "'");

    // Convert the calendar to a ZDate
    ZDate start =
        new ZDate(
            date.get(Calendar.YEAR),
            date.get(Calendar.MONTH) + 1,
            date.get(Calendar.DAY_OF_MONTH),
            12,
            0,
            0);

    account.soapSend(
        "<CreateAppointmentRequest xmlns='urn:zimbraMail'>"
            + "<m l='10'>"
            + "<inv>"
            + "<comp allDay='1' name='"
            + subject
            + "' "
            + loc
            + " draft='0' status='CONF' class='PUB' transp='O' fb='F'>"
            + "<s d='"
            + start.toYYYYMMDD()
            + "'/>"
            + "<e d='"
            + start.addDays(duration > 0 ? duration - 1 : 0).toYYYYMMDD()
            + "'/>"
            + "<or a='"
            + account.EmailAddress
            + "'/>"
            + "</comp>"
            + "</inv>"
            + "<su>"
            + subject
            + "</su>"
            + "<mp ct='text/plain'>"
            + "<content>"
            + content
            + "</content>"
            + "</mp>"
            + "</m>"
            + "</CreateAppointmentRequest>");

    AppointmentItem result =
        AppointmentItem.importFromSOAP(
            account, "subject:(" + subject + ")", start.addDays(-7), start.addDays(7));

    return (result);
  }
  public static AppointmentItem importFromSOAP(Element GetAppointmentResponse)
      throws HarnessException {

    if (GetAppointmentResponse == null) throw new HarnessException("Element cannot be null");

    AppointmentItem appt = null;

    try {

      // Make sure we only have the GetMsgResponse part
      Element getAppointmentResponse =
          ZimbraAccount.SoapClient.selectNode(
              GetAppointmentResponse, "//mail:GetAppointmentResponse");
      if (getAppointmentResponse == null)
        throw new HarnessException("Element does not contain GetAppointmentResponse");

      Element m = ZimbraAccount.SoapClient.selectNode(getAppointmentResponse, "//mail:appt");
      if (m == null) throw new HarnessException("Element does not contain an appt element");

      // Create the object
      appt = new AppointmentItem();

      if (m != null) {

        // Appointment id
        appt.dApptID = m.getAttribute("id");
      }

      String parentFolder = m.getAttribute("l");
      if (parentFolder != null) {

        // Parent folder
        appt.dFolder = parentFolder;
      }

      Element sElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:s");
      if (sElement != null) {

        // Start time
        appt.dStartTime = new ZDate(sElement);
      }

      Element eElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:e");
      if (eElement != null) {

        // End time
        appt.dEndTime = new ZDate(eElement);
      }

      Element compElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:comp");
      if (compElement != null) {

        // Subject
        appt.dSubject = compElement.getAttribute("name");

        // Location
        appt.dLocation = compElement.getAttribute("loc");

        // Display
        appt.dDisplay = compElement.getAttribute("fb");
      }

      Element oElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:or");
      if (oElement != null) {

        // Organizer
        appt.dOrganizer = oElement.getAttribute("a");
      }
      Element mpElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:mp");
      if (mpElement != null) {

        // Get multipart
        appt.dMultipart = mpElement;
      }
      // Parse the required attendees
      ArrayList<String> attendees = new ArrayList<String>();
      Element[] requiredElements =
          ZimbraAccount.SoapClient.selectNodes(m, "//mail:at[@role='REQ']");
      for (Element e : requiredElements) {
        attendees.add(e.getAttribute("a"));
      }
      if (attendees.size() > 0) {
        appt.dAttendees = AppointmentItem.StringListToCommaSeparated(attendees);
      }

      // Parse the optional attendees
      ArrayList<String> optionals = new ArrayList<String>();
      Element[] optionalElements =
          ZimbraAccount.SoapClient.selectNodes(m, "//mail:at[@role='OPT']");
      for (Element e : optionalElements) {
        optionals.add(e.getAttribute("a"));
      }
      if (optionals.size() > 0) {
        appt.dOptionals = AppointmentItem.StringListToCommaSeparated(optionals);
      }

      if (appt.dLocation == "") {

        Element equipElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:at[@cutype='RES']");
        if (equipElement != null) {

          // Equipment
          appt.dEquipment = equipElement.getAttribute("a");
        }

      } else if (appt.dLocation != null) {

        Element equipElement =
            ZimbraAccount.SoapClient.selectNode(m, "//mail:at[@cutype='RES'][2]");
        if (equipElement != null) {

          // Equipment
          appt.dEquipment = equipElement.getAttribute("a");
        }
      }

      Element descElement = ZimbraAccount.SoapClient.selectNode(m, "//mail:fr");
      if (descElement != null) {

        // Body
        appt.dContent = descElement.getTextTrim();
      }

      return (appt);

    } catch (Exception e) {
      throw new HarnessException(
          "Could not parse GetMsgResponse: " + GetAppointmentResponse.prettyPrint(), e);
    } finally {
      if (appt != null) logger.info(appt.prettyPrint());
    }
  }
  /**
   * Create a single-day appointment on the server
   *
   * @param account Appointment Organizer
   * @param start Start time of the appointment, which will be rounded to the nearest hour
   * @param duration Duration of the appointment, in minutes
   * @param timezone Timezone of the appointment (null if default)
   * @param subject Subject of the appointment
   * @param content Content of the appointment (text)
   * @param location Location of the appointment (null if none)
   * @param attendees List of attendees for the appointment
   * @return
   * @throws HarnessException
   */
  public static AppointmentItem createAppointmentSingleDay(
      ZimbraAccount account,
      Calendar start,
      int duration,
      TimeZone tz,
      String subject,
      String content,
      String location,
      List<ZimbraAccount> attendees)
      throws HarnessException {

    // If location is null, don't specify the loc attribute
    String loc = (location == null ? "" : "loc='" + location + "'");

    // TODO: determine the timezone
    String timezoneString = ZTimeZone.TimeZoneEST.getID();

    // Convert the calendar to a ZDate
    ZDate beginning =
        new ZDate(
            start.get(Calendar.YEAR),
            start.get(Calendar.MONTH) + 1,
            start.get(Calendar.DAY_OF_MONTH),
            start.get(Calendar.HOUR_OF_DAY),
            0,
            0);
    ZDate ending = beginning.addMinutes(duration);

    account.soapSend(
        "<CreateAppointmentRequest xmlns='urn:zimbraMail'>"
            + "<m l='10'>"
            + "<inv>"
            + "<comp name='"
            + subject
            + "' "
            + loc
            + " draft='0' status='CONF' class='PUB' transp='O' fb='B'>"
            + "<s d='"
            + beginning.toTimeZone(timezoneString).toYYYYMMDDTHHMMSS()
            + "' tz='"
            + timezoneString
            + "'/>"
            + "<e d='"
            + ending.toTimeZone(timezoneString).toYYYYMMDDTHHMMSS()
            + "' tz='"
            + timezoneString
            + "'/>"
            + "<or a='"
            + account.EmailAddress
            + "'/>"
            + "</comp>"
            + "</inv>"
            + "<su>"
            + subject
            + "</su>"
            + "<mp ct='text/plain'>"
            + "<content>"
            + content
            + "</content>"
            + "</mp>"
            + "</m>"
            + "</CreateAppointmentRequest>");

    AppointmentItem result =
        AppointmentItem.importFromSOAP(
            account, "subject:(" + subject + ")", beginning.addDays(-7), beginning.addDays(7));

    return (result);
  }