/// <summary>
        /// Searches for the given user using the given Midpoint model port.
        /// Returns the UserType object for the user if they exist.
        /// </summary>
        /// <param name="modelPort">The model port used to run search Midpoint.</param>
        /// <param name="username">The username to search for.</param>
        /// <returns>The UserType object for the requested user, or null if not found.</returns>

        public static MidpointModel3WebService.UserType searchUserByName(MidpointModel3WebService.modelPortType modelPort, String username)
        {
            MidpointModel3WebService.QueryType query = new MidpointModel3WebService.QueryType();
            query.filter = createNameFilter(username);

            MidpointModel3WebService.searchObjects         request  = new MidpointModel3WebService.searchObjects(USER_TYPE, query, null);
            MidpointModel3WebService.searchObjectsResponse response = modelPort.searchObjects(request);
            return((MidpointModel3WebService.UserType)getOneObject(response, username));
        }
/*
 *      public static void changeUserPassword2(MidpointModel3WebService.modelPortType modelPort, string oid, string newPassword)
 *      {
 *
 *          MidpointModel3WebService.ModelExecuteOptionsType meot = new MidpointModel3WebService.ModelExecuteOptionsType();
 *          MidpointModel3WebService.ObjectDeltaType[] odt = new MidpointModel3WebService.ObjectDeltaType[1];
 *
 *          MidpointModel3WebService.ObjectDeltaType objDeltaType = new MidpointModel3WebService.ObjectDeltaType();
 *          XmlQualifiedName xqn = new XmlQualifiedName(new MidpointModel3WebService.UserType().GetType().Name, NS_COMMON);
 *          objDeltaType.objectType = xqn;
 *          objDeltaType.oid = oid;
 *          objDeltaType.changeType = MidpointModel3WebService.ChangeTypeType.modify;
 *
 *
 *          MidpointModel3WebService.ItemDeltaType passwordDelta = new MidpointModel3WebService.ItemDeltaType();
 *          MidpointModel3WebService.ObjectModificationType userDelta = new MidpointModel3WebService.ObjectModificationType();
 *          passwordDelta.modificationType = MidpointModel3WebService.ModificationTypeType.replace;
 *          MidpointModel3WebService.ItemPathType ipt = new ItemPathType();
 *          ipt.Value = @"declare default namespace 'http://midpoint.evolveum.com/xml/ns/public/common/common-3'; credentials/password/value";
 *          passwordDelta.path = ipt;
 *          MidpointModel3WebService.ProtectedStringType pst = new MidpointModel3WebService.ProtectedStringType();
 *          pst.clearValue = "testPassword";
 *          Object[] valueArray = new Object[1];
 *          valueArray[0] = pst;
 *          passwordDelta.value = valueArray;
 *
 *          MidpointModel3WebService.ItemDeltaType[] itemDeltaList = new MidpointModel3WebService.ItemDeltaType[1];
 *          itemDeltaList[0] = passwordDelta;
 *          objDeltaType.itemDelta = itemDeltaList;
 *
 *          odt[0] = objDeltaType;
 *
 *          string test =  odt.ToString();
 *
 *          MidpointModel3WebService.executeChanges request = new executeChanges(odt, meot);
 *          executeChangesResponse response = modelPort.executeChanges(request);
 *          response.deltaOperationList[]
 *
 *          System.Console.WriteLine("Result: '" + response.deltaOperationList.Length.ToString() + "' for user oid: '" + oid + "'");
 *      }
 *
 */



/*
 *              /// <summary>
 *              /// Creates a new userDelta with the new password for the given user, then uses
 *              /// the given model port to transmit userDelta to Midpoint.
 *              /// </summary>
 *              /// <param name="modelPort">The model port to transmit new userDelta back to Midpoint.</param>
 *              /// <param name="oid">The user ID.</param>
 *              /// <param name="newPassword">The new password value.</param>
 *
 *
 *              public static void changeUserPassword(modelPortType modelPort, string oid, string newPassword)
 *              {
 *                  XmlDocument doc = new XmlDocument();
 *
 *                  ObjectModificationType userDelta = new ObjectModificationType();
 *                  userDelta.oid = oid;
 *
 *                  ItemDeltaType passwordDelta = new ItemDeltaType();
 *                  passwordDelta.modificationType = ModificationTypeType.replace;
 *                  // Set path value - webservices name is apparently 'Any'?
 *                  passwordDelta.Any = createPathElement("credentials/password", doc);
 *                  ItemDeltaTypeValue passwordValue = new ItemDeltaTypeValue();
 *                  // New passwordValue object so add at first index?
 *                  passwordValue.Any = new XmlElement[1];
 *                  passwordValue.Any.SetValue(toPasswordElement(NS_COMMON, createProtectedString(newPassword), doc), 0);
 *                  passwordDelta.value = passwordValue;
 *                  // New userDelta object so add at first index?
 *                  userDelta.modification = new ItemDeltaType[1];
 *                  userDelta.modification.SetValue(passwordDelta, 0);
 *
 *                  modifyObject request = new modifyObject(getTypeUri(new UserType()), userDelta);
 *                  modifyObjectResponse response = modelPort.modifyObject(request);
 *
 *                  System.Console.WriteLine("Result: '" + response.result.status.ToString() + "' for user oid: '" + oid + "'");
 *              }
 *
 *              /// <summary>
 *              /// Searches for the given user using the given Midpoint model port.
 *              /// Returns the UserType object for the user if they exist.
 *              /// </summary>
 *              /// <param name="modelPort">The model port used to run search Midpoint.</param>
 *              /// <param name="username">The username to search for.</param>
 *              /// <returns>The UserType object for the requested user, or null if not found.</returns>
 *              public static UserType searchUserByName(modelPortType modelPort, string username)
 *              {
 *                  // WARNING: in a real case make sure that the username is properly escaped before putting it in XML
 *                  XmlElement filter = parseElement(
 *                                  "<equal xmlns='" + SEARCHUSER_NS + "' xmlns:c='" + NS_COMMON + "' >" +
 *                                      "<path>c:name</path>" +
 *                                      "<value>" + username + "</value>" +
 *                                  "</equal>"
 *                  );
 *                  QueryType query = new QueryType();
 *                  // Set filter value - webservices name is apparently 'Any'?
 *                  query.Any = filter;
 *                  // Create an empty array since it can't be uninitialised
 *                  ObjectOperationOptionsType[] options = new ObjectOperationOptionsType[0];
 *
 *                  searchObjects request = new searchObjects(getTypeUri(new UserType()), query, options);
 *                  searchObjectsResponse response = modelPort.searchObjects(request);
 *
 *                  ObjectListType objectList = response.objectList;
 *                  ObjectType[] objects = objectList.@object;
 *
 *                  if (objects != null)
 *                  {
 *                      switch (objects.Length)
 *                      {
 *                          case 0:
 *                              return null;
 *                              break;
 *                          case 1:
 *                              return (UserType)objects[0];
 *                              break;
 *                          default:
 *                              throw new ArgumentException("Expected to find a single user with username '" + username + "' but found " + objects.Length + " users instead");
 *                      }
 *                  }
 *                  else
 *                  {
 *                      return null;
 *                  }
 *              }
 */

        /// <summary>
        /// Creates a new model port for with the administrator credentials.
        /// Has a default endpoint URL but this can be overridden by passing in a value for the first element in the args parameter.
        /// </summary>
        /// <param name="args">If the first argument is defined it overrides the default endpoint URL. Any other args are ignored.</param>
        /// <returns>The new model port.</returns>
        public static MidpointModel3WebService.modelPortType createModelPort(String[] args)
        {
            string endpointUrl = DEFAULT_ENDPOINT_URL;

            if (args.Length > 0)
            {
                endpointUrl = args[0];
            }

            MidpointModel3WebService.modelPortTypeClient modelService = new MidpointModel3WebService.modelPortTypeClient();
            modelService.ClientCredentials.UserName.UserName = ADM_USERNAME;
            modelService.ClientCredentials.UserName.Password = ADM_PASSWORD;

            modelService.Endpoint.Behaviors.Add(new InspectorBehavior(new ClientInspector(new SecurityHeader(ADM_USERNAME, ADM_PASSWORD))));
            MidpointModel3WebService.modelPortType modelPort = modelService.ChannelFactory.CreateChannel(new EndpointAddress(endpointUrl));

            return(modelPort);
        }
/*
 *      static void Main(string[] args)
 *      {
 *
 *          string UserName = args[1];
 *          string Password = args[2];
 *
 *
 *          string newPassword = Password;
 *          MidpointModel3WebService.modelPortType modelPort = ChangePassword.createModelPort(args);
 *          MidpointModel3WebService.UserType user = ChangePassword.searchUserByName(modelPort, UserName);
 *          ChangePassword.changeUserPassword(modelPort, user.oid, newPassword);
 *      }
 */


        static void Main(string[] args)
        {
            try
            {
                List <UpdateDetails> updateDetails = new List <UpdateDetails> {
                };

                // Parse each file in folder (except the log file)
                string[] files = Directory.GetFiles(passwordFilePath, searchPattern, SearchOption.TopDirectoryOnly);
                foreach (string file in files)
                {
                    try
                    {
                        UpdateDetails newUpdate = ParseUpdateDetailsFile(file);
                        // Mark any stale updates as processed so they are deleted and not processed again
                        foreach (UpdateDetails update in updateDetails)
                        {
                            if (update.UserName == newUpdate.UserName)
                            {
                                if (update.TimeStamp > newUpdate.TimeStamp)
                                {
                                    // This is a stale update
                                    newUpdate.IsProcessed = true;
                                    break;
                                }
                                else if (update.TimeStamp < newUpdate.TimeStamp)
                                {
                                    // The previous update precedes new update
                                    update.IsProcessed = true;
                                }
                                else
                                {
                                    // Timestamps are exactly equal - big problem - shouldn't happen
                                    // Don't know which is newer
                                    update.IsProcessed    = true;
                                    newUpdate.IsProcessed = true;
                                }
                            }
                        }

                        // Add update to list
                        updateDetails.Add(newUpdate);
                    }
                    catch (Exception ex)
                    {
                        // If the file cannot be correctly processed, log the exception and delete it.
                        // Users should not have permissions to the files so it is important to clean them up.
                        DeleteFile(file, ex);
                    }
                }

                // Process the updates
                foreach (UpdateDetails update in updateDetails)
                {
                    try
                    {
                        // Only processed unprocessed items - if already processed then they are stale changes
                        if (!update.IsProcessed)
                        {
                            string newPassword = Encryptor.Decrypt(update.Password);
                            MidpointModel3WebService.modelPortType modelPort = ChangePassword.createModelPort(args);
                            UserType user = ChangePassword.searchUserByName(modelPort, update.UserName);
                            ChangePassword.changeUserPassword(modelPort, user.oid, newPassword);
                        }
                    }
                    catch (Exception ex)
                    {
                        LogError("Error processing file: " + ex.Message);
                    }
                    finally
                    {
                        // Mark file as processed so it can be deleted
                        update.IsProcessed = true;
                    }
                }

                // Delete any files that were successfully processed
                foreach (UpdateDetails update in updateDetails)
                {
                    if (update.IsProcessed)
                    {
                        DeleteFile(update.FileName, null);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError(ex.Message);
            }
        }