import { ArgumentException, ArgumentNullException } from "../Exceptions/ArgumentException";
import { ArrayHelper, Convert, StringHelper } from "../ExtensionMethods";
import { ConversationQueryTraversal } from "../Enumerations/ConversationQueryTraversal";
import { DateTime, DateTimeKind } from "../DateTime";
import { DayOfTheWeek } from "../Enumerations/DayOfTheWeek";
import { DayOfWeek } from "../Enumerations/DayOfWeek";
import { Dictionary, DictionaryWithStringKey, DictionaryWithNumericKey } from "../AltDictionary";
import { DictionaryKeyType } from "../Enumerations/DictionaryKeyType";
import { EmailAddressKey } from "../Enumerations/EmailAddressKey";
import { EnumToSchemaMappingHelper } from "../Enumerations/EnumToSchemaMappingHelper";
import { EventType } from "../Enumerations/EventType";
import { EwsLogging } from "./EwsLogging";
import { ExchangeService } from "./ExchangeService";
import { ExchangeVersion } from "../Enumerations/ExchangeVersion";
import { FileAsMapping } from "../Enumerations/FileAsMapping";
import { ImAddressKey } from "../Enumerations/ImAddressKey";
import { ISelfValidate } from "../Interfaces/ISelfValidate";
import { Item } from "./ServiceObjects/Items/Item";
import { ItemAttachment } from "../ComplexProperties/ItemAttachment";
import { ItemTraversal } from "../Enumerations/ItemTraversal";
import { IXHROptions } from "../Interfaces";
import { LazyMember } from "./LazyMember";
import { MailboxType } from "../Enumerations/MailboxType";
import { MeetingRequestsDeliveryScope } from "../Enumerations/MeetingRequestsDeliveryScope";
import { PhoneNumberKey } from "../Enumerations/PhoneNumberKey";
import { PhysicalAddressKey } from "../Enumerations/PhysicalAddressKey";
import { ServiceObject } from "./ServiceObjects/ServiceObject";
import { ServiceObjectInfo } from "./ServiceObjects/ServiceObjectInfo";
import { ServiceVersionException } from "../Exceptions/ServiceVersionException";
import { Strings } from "../Strings";
import { TimeSpan } from "../TimeSpan";
import { TimeZoneConversionException } from "../Exceptions/TimeZoneConversionException";
import { TimeZoneInfo } from "../TimeZoneInfo";
import { TypeContainer } from "../TypeContainer";
import { TypeGuards } from "../Interfaces/TypeGuards";
import { ViewFilter } from "../Enumerations/ViewFilter";
import { WellKnownFolderName } from "../Enumerations/WellKnownFolderName";
import { XmlNamespace } from "../Enumerations/XmlNamespace";

export type RequiredServerVersionEnums = typeof ConversationQueryTraversal | typeof EventType | typeof FileAsMapping | typeof ItemTraversal | typeof MailboxType | typeof MeetingRequestsDeliveryScope | typeof ViewFilter | typeof WellKnownFolderName

/**
 * @internal EWS utilities
 *
 * @static
 */
export class EwsUtilities {

  //#region constants in c# - typescript static
  static XSFalse: string = "false";
  static XSTrue: string = "true";
  static EwsTypesNamespacePrefix: string = "t";
  static EwsMessagesNamespacePrefix: string = "m";
  static EwsErrorsNamespacePrefix: string = "e";
  static EwsSoapNamespacePrefix: string = "soap";
  static EwsXmlSchemaInstanceNamespacePrefix: string = "xsi";
  static PassportSoapFaultNamespacePrefix: string = "psf";
  static WSTrustFebruary2005NamespacePrefix: string = "wst";
  static WSAddressingNamespacePrefix: string = "wsa";
  static AutodiscoverSoapNamespacePrefix: string = "a";
  static WSSecurityUtilityNamespacePrefix: string = "wsu";
  static WSSecuritySecExtNamespacePrefix: string = "wsse";
  static EwsTypesNamespace: string = "http://schemas.microsoft.com/exchange/services/2006/types";
  static EwsMessagesNamespace: string = "http://schemas.microsoft.com/exchange/services/2006/messages";
  static EwsErrorsNamespace: string = "http://schemas.microsoft.com/exchange/services/2006/errors";
  static EwsSoapNamespace: string = "http://schemas.xmlsoap.org/soap/envelope/";
  static EwsSoap12Namespace: string = "http://www.w3.org/2003/05/soap-envelope";
  static EwsXmlSchemaInstanceNamespace: string = "http://www.w3.org/2001/XMLSchema-instance";
  static PassportSoapFaultNamespace: string = "http://schemas.microsoft.com/Passport/SoapServices/SOAPFault";
  static WSTrustFebruary2005Namespace: string = "http://schemas.xmlsoap.org/ws/2005/02/trust";
  static WSAddressingNamespace: string = "http://www.w3.org/2005/08/addressing";
  static AutodiscoverSoapNamespace: string = "http://schemas.microsoft.com/exchange/2010/Autodiscover";
  static WSSecurityUtilityNamespace: string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
  static WSSecuritySecExtNamespace: string = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

  /// <summary>
  /// Regular expression for legal domain names.
  /// </summary>
  static DomainRegex: string = "^[-a-zA-Z0-9_.]+$";
  //#endregion


  static BuildVersion: string;
  // private static serviceObjectInfo: LazyMember<ServiceObjectInfo> = new LazyMember<ServiceObjectInfo>(
  //     () => {
  //         //return new ServiceObjectInfo();
  //     });
  //private static buildVersion: LazyMember<T>;
  // private static enumVersionDictionaries: LazyMember<EnumToExhcangeVersionDelegateDictionary> = new LazyMember<EnumToExhcangeVersionDelegateDictionary>(
  //     () => {
  //         var e2evmh = EnumToExchangeVersionMappingHelper;
  //         var dict: EnumToExhcangeVersionDelegateDictionary = {};
  //         dict[e2evmh[e2evmh.WellKnownFolderName]] = EwsUtilities.BuildEnumDict(e2evmh.WellKnownFolderName);
  //         dict[e2evmh[e2evmh.ItemTraversal]] = EwsUtilities.BuildEnumDict(e2evmh.ItemTraversal);
  //         dict[e2evmh[e2evmh.ConversationQueryTraversal]] = EwsUtilities.BuildEnumDict(e2evmh.ConversationQueryTraversal);
  //         dict[e2evmh[e2evmh.FileAsMapping]] = EwsUtilities.BuildEnumDict(e2evmh.FileAsMapping);
  //         dict[e2evmh[e2evmh.EventType]] = EwsUtilities.BuildEnumDict(e2evmh.EventType);
  //         dict[e2evmh[e2evmh.MeetingRequestsDeliveryScope]] = EwsUtilities.BuildEnumDict(e2evmh.MeetingRequestsDeliveryScope);
  //         dict[e2evmh[e2evmh.ViewFilter]] = EwsUtilities.BuildEnumDict(e2evmh.ViewFilter);
  //         dict[e2evmh[e2evmh.MailboxType]] = EwsUtilities.BuildEnumDict(e2evmh.MailboxType);
  //         return dict;
  //     });
  // private static schemaToEnumDictionaries: LazyMember<DictionaryWithNumericKey<DictionaryWithStringKey<number>>> = new LazyMember<DictionaryWithNumericKey<DictionaryWithStringKey<number>>>(
  //     () => {
  //         var dict = new DictionaryWithNumericKey<DictionaryWithStringKey<number>>();
  //         dict.addUpdate(EnumToSchemaMappingHelper.WellKnownFolderName, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.WellKnownFolderName));
  //         dict.addUpdate(EnumToSchemaMappingHelper.ItemTraversal, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.ItemTraversal));
  //         dict.addUpdate(EnumToSchemaMappingHelper.ConversationQueryTraversal, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.ConversationQueryTraversal));
  //         dict.addUpdate(EnumToSchemaMappingHelper.FileAsMapping, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.FileAsMapping));
  //         dict.addUpdate(EnumToSchemaMappingHelper.EventType, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.EventType));
  //         dict.addUpdate(EnumToSchemaMappingHelper.MeetingRequestsDeliveryScope, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.MeetingRequestsDeliveryScope));
  //         dict.addUpdate(EnumToSchemaMappingHelper.ViewFilter, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.ViewFilter));
  //         dict.addUpdate(EnumToSchemaMappingHelper.MailboxType, EwsUtilities.BuildSchemaToEnumDict(EnumToSchemaMappingHelper.EventType));
  //         return dict;
  //     });
  // private static enumToSchemaDictionaries: LazyMember<DictionaryWithNumericKey<DictionaryWithNumericKey<string>>>;
  //private static typeNameToShortNameMap: LazyMember<T>;

  static BoolToXSBool(value: boolean): string {
    var boolvalue = Convert.toBool(value)
    return boolvalue ? EwsUtilities.XSTrue : EwsUtilities.XSFalse;
    //throw new Error("EwsUtilities.ts - static BoolToXSBool : Not implemented.");
  }
  //static BuildEnumDict(enumType: System.Type): System.Collections.Generic.Dictionary<TKey, TValue>{ throw new Error("EwsUtilities.ts - static BuildEnumDict : Not implemented.");}
  //deviation - need to work with static data for enum to exchange version dict, there is no Attribute type system in javascript.
  // static BuildEnumDict(enumType: EnumToExchangeVersionMappingHelper): EnumVersionDelegate {
  //     var enumDelegate = (value: any) => { return ExchangeVersion.Exchange2007_SP1 };
  //     switch (enumType) {
  //         //TODO: fix numbering to named enum value if possible
  //         case EnumToExchangeVersionMappingHelper.WellKnownFolderName:
  //             enumDelegate = (value) => {
  //                 var enumVersion = ExchangeVersion.Exchange2007_SP1;
  //                 if (value <= 15) //<= WellKnownFolderName.VoiceMail
  //                     enumVersion = ExchangeVersion.Exchange2007_SP1;
  //                 else if (value >= 16 && value <= 26) //>= RecoverableItemsRoot && <= ArchiveRecoverableItemsPurges
  //                     enumVersion = ExchangeVersion.Exchange2010_SP1;
  //                 else if (value >= 27 && value <= 34) //>= SyncIssues && <= ToDoSearch
  //                     enumVersion = ExchangeVersion.Exchange2013;
  //                 else
  //                     enumVersion = ExchangeVersion.Exchange_Version_Not_Updated;

  //                 return enumVersion;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.ItemTraversal:
  //             enumDelegate = (value) => {
  //                 if (value <= 1) //<= ItemTraversal.SoftDeleted
  //                     return ExchangeVersion.Exchange2007_SP1;
  //                 else if (value == 2) // === Associated
  //                     return ExchangeVersion.Exchange2010;

  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.ConversationQueryTraversal:
  //             enumDelegate = (value) => {
  //                 if (value <= 1) //<= ConversationQueryTraversal.Deep
  //                     return ExchangeVersion.Exchange2013;
  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.FileAsMapping:
  //             enumDelegate = (value) => {
  //                 if (value <= 12) //<= FileAsMapping.SurnameSpaceGivenName
  //                     return ExchangeVersion.Exchange2007_SP1;
  //                 else if (value >= 13 && value <= 17) // >= DisplayName && <=Empty
  //                     return ExchangeVersion.Exchange2010;

  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.EventType:
  //             enumDelegate = (value) => {
  //                 if (value <= 6) //<= EventType.Created
  //                     return ExchangeVersion.Exchange2007_SP1;
  //                 else if (value == 7) // == FreeBusyChanged
  //                     return ExchangeVersion.Exchange2010_SP1;

  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.MeetingRequestsDeliveryScope:
  //             enumDelegate = (value) => {
  //                 if (value <= 2) //<= MeetingRequestsDeliveryScope.DelegatesAndSendInformationToMe
  //                     return ExchangeVersion.Exchange2007_SP1;
  //                 else if (value == 3) // == NoForward
  //                     return ExchangeVersion.Exchange2010_SP1;

  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.ViewFilter:
  //             enumDelegate = (value) => {
  //                 if (value <= 10) //<=ViewFilter.SuggestionsDelete
  //                     return ExchangeVersion.Exchange2013;

  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         case EnumToExchangeVersionMappingHelper.MailboxType:
  //             enumDelegate = (value) => {
  //                 if (value <= 1) //<=MailboxType.OneOff
  //                     return ExchangeVersion.Exchange2010;
  //                 if (value <= 6) //<=MailboxType.Contact
  //                     return ExchangeVersion.Exchange2007_SP1;
  //                 if (value <= 7) //<=MailboxType.GroupMailbox
  //                     return ExchangeVersion.Exchange2015;

  //                 return ExchangeVersion.Exchange_Version_Not_Updated;
  //             };
  //             break;
  //         default:
  //             throw new Error("EwsUtilities.ts - BuildEnumDict - no mapping available for this enumtype" + EnumToExchangeVersionMappingHelper[<number>enumType]);
  //     }

  //     return enumDelegate;
  // }
  /**@internal */
  //deviation - need to work with static data for enum to exchange version dict, there is no Attribute type system in javascript.
  static BuildEnumToSchemaDict(enumType: EnumToSchemaMappingHelper): Dictionary<number, string> { throw new Error("EwsUtilities.ts - static BuildEnumToSchemaDict : Not implemented."); }
  /**@internal */
  //deviation - need to work with static data for enum to exchange version dict, there is no Attribute type system in javascript.
  static BuildSchemaToEnumDict(enumType: EnumToSchemaMappingHelper): Dictionary<string, number> { throw new Error("EwsUtilities.ts - static BuildSchemaToEnumDict : Not implemented."); }

  static GetDictionaryKeyTypeEnum(dictionaryKeyType: DictionaryKeyType): any {
    switch (dictionaryKeyType) {
      case DictionaryKeyType.EmailAddressKey:
        return EmailAddressKey;
      case DictionaryKeyType.ImAddressKey:
        return ImAddressKey;
      case DictionaryKeyType.PhoneNumberKey:
        return PhoneNumberKey;
      case DictionaryKeyType.PhysicalAddressKey:
        return PhysicalAddressKey;
      default:
        throw new Error("EwsUtilities.ts - GetDictionaryKeyTypeEnum - invalid value: " + dictionaryKeyType);
    }
  }
  // private static GetExchangeVersionFromEnumDelegate(enumType: EnumToExchangeVersionMappingHelper, enumValue: number): ExchangeVersion {
  //     var delegate = this.enumVersionDictionaries.Member[EnumToExchangeVersionMappingHelper[enumType]];
  //     if (delegate && typeof delegate === 'function') {
  //         try {
  //             return delegate(enumValue);
  //         }
  //         catch (ex) { }
  //     }

  //     return ExchangeVersion.Exchange2007_SP1;
  // }
  static ConvertTime(dateTime: DateTime, sourceTimeZone: TimeZoneInfo, destinationTimeZone: TimeZoneInfo): DateTime {
    try {
      return TimeZoneInfo.ConvertTime(
        dateTime,
        sourceTimeZone,
        destinationTimeZone);
    }
    catch (ex)//ArgumentException
    {
      throw new TimeZoneConversionException(
        StringHelper.Format(
          Strings.CannotConvertBetweenTimeZones,
          EwsUtilities.DateTimeToXSDateTime(dateTime),
          sourceTimeZone.DisplayName,
          destinationTimeZone.DisplayName),
        ex);
    }
  }
  //static CopyStream(source: System.IO.Stream, target: System.IO.Stream): any{ throw new Error("EwsUtilities.ts - static CopyStream : Not implemented.");}
  static CountMatchingChars(str: string, charPredicate: any): number { throw new Error("EwsUtilities.ts - static CountMatchingChars : Not implemented."); }
  static CreateEwsObjectFromXmlElementName<TServiceObject extends ServiceObject>(service: ExchangeService, xmlElementName: string): TServiceObject {
    throw new Error("EwsUtilities - CreateEwsObjectFromXmlElementName: - this is moved in folderinfo/iteminfo classes to avoid circular loop caused by serviceobjectinfo class");
    //     //var itemClass = TypeSystem.GetObjectByClassName("Microsoft.Exchange.WebServices.Data." + xmlElementName
    //     debugger;

    //     //        var creationDelegate = EwsUtilities.serviceObjectInfo.Member.ServiceObjectConstructorsWithServiceParam[xmlElementName];
    //     //
    //     //        if (creationDelegate) {
    //     //            return creationDelegate(service);
    //     //        }
    //     //        else return null;

    //     //var itemClass = EwsUtilities.serviceObjectInfo.Member.XmlElementNameToServiceObjectClassMap[xmlElementName];
    //     //if (itemClass) {
    //     //    //return new itemClass(service);

    //     //    creationDelegate: CreateServiceObjectWithServiceParam;


    //     //    //if (EwsUtilities.serviceObjectInfo.Member.ServiceObjectConstructorsWithServiceParam.TryGetValue(itemClass, out creationDelegate)) {
    //     //    //    return (TServiceObject)creationDelegate(service);
    //     //    //}
    //     //    //else {
    //     //    //    throw new ArgumentException(Strings.NoAppropriateConstructorForItemClass);
    //     //    //}
    //     //}
    //     //else {
    //     //    return null; //default(TServiceObject);
    //     //}
  }
  //static CreateItemFromItemClass(itemAttachment: ItemAttachment, itemClass: System.Type, isNew: boolean): Item{ throw new Error("EwsUtilities.ts - static CreateItemFromItemClass : Not implemented.");}
  static CreateItemFromXmlElementName(itemAttachment: ItemAttachment, xmlElementName: string): Item { throw new Error("EwsUtilities.ts - static CreateItemFromXmlElementName : Not implemented."); }
  public static DateTimeToXSDate(date: DateTime): string { return DateTime.DateTimeToXSDate(date); }
  public static DateTimeToXSDateTime(dateTime: DateTime): string { return DateTime.DateTimeToXSDateTime(dateTime); }
  static DomainFromEmailAddress(emailAddress: string): string {
    var emailAddressParts: string[] = emailAddress.split('@');

    if (emailAddressParts.length != 2 || StringHelper.IsNullOrEmpty(emailAddressParts[1])) {
      throw new Error(Strings.InvalidEmailAddress);
    }

    return emailAddressParts[1];
  }
  static EwsToSystemDayOfWeek(dayOfTheWeek: DayOfTheWeek): DayOfWeek {
    if (dayOfTheWeek == DayOfTheWeek.Day ||
      dayOfTheWeek == DayOfTheWeek.Weekday ||
      dayOfTheWeek == DayOfTheWeek.WeekendDay) {
      throw new ArgumentException(
        StringHelper.Format("Cannot convert {0} to System.DayOfWeek enum value", dayOfTheWeek),
        "dayOfTheWeek");
    }
    else {
      return <DayOfWeek><any>dayOfTheWeek;
    }
  }

  static FindFirstItemOfType<T extends Item>(items: Item[], type: any): T {
    for (var item of items) {
      if (item instanceof type) {
        return <T>item;
      }
    }
  }

  // static ForEach(collection: System.Collections.Generic.IEnumerable<T>, action: any): any { throw new Error("EwsUtilities.ts - static ForEach : Not implemented."); }

  // static FormatHttpHeaders(sb: any, headers: System.Net.WebHeaderCollection): any { throw new Error("EwsUtilities.ts - static FormatHttpHeaders : Not implemented."); }

  private static FormatHttpHeaders(headers: object = {}): string {
    let header = "";
    for (var key in headers) {
      header += key + " : " + headers[key] + "\r\n";
    }
    return header;
  }

  // static FormatHttpRequestHeaders(request: IEwsHttpWebRequest): string { throw new Error("EwsUtilities.ts - static FormatHttpRequestHeaders : Not implemented."); }
  /**
   * @internal Format request HTTP headers.
   *
   * @param   {IXHROptions}   request   The HTTP request.
   */
  static FormatHttpRequestHeaders(request: IXHROptions): string {
    let sb = StringHelper.Format("{0} {1} HTTP/1.1\n", request.type, request.url);
    sb += EwsUtilities.FormatHttpHeaders(request.headers);
    sb += "\n";
    return sb;
  }

  /**
   * @internal Format response HTTP headers.
   *
   * @param   {XMLHttpRequest}   response   The HTTP response.
   */
  static FormatHttpResponseHeaders(response: XMLHttpRequest): string {
    let sb = StringHelper.Format("HTTP/{0} {1} {2}\n", 1 /*response.ProtocolVersion*/, response.status, response.statusText);
    sb += response.getAllResponseHeaders(); // EwsUtilities.FormatHttpHeaders(response.getAllResponseHeaders());
    sb += "\n";
    return sb;
  }

  /**
   * @internal Format log message.
   *
   * @param   {string}   entryKind   Kind of the entry.
   * @param   {string}   logEntry    The log entry.
   * @return  {string}               XML log entry as a string.
   */
  static FormatLogMessage(entryKind: string, logEntry: string): string {
    return `
      <${entryKind}>
        ${logEntry}
      </${entryKind}>
    `
  }

  /**
   * @internal Format XML content in a response for message.
   *
   * @param   {string}          entryKind      Kind of the entry.
   * @param   {XMLHttpRequest}  memoryStream   The XMLHttpRequest.
   * @return  {string}          XML log entry as a string.
   */
  static FormatLogMessageWithXmlContent(entryKind: string, memoryStream: XMLHttpRequest): string {
    return `
      <${entryKind}>
        ${memoryStream.responseText || memoryStream.response}
      </${entryKind}>
    `
  }

  static GetEnumeratedObjectAt(objects: any, index: number): any { throw new Error("EwsUtilities.ts - static GetEnumeratedObjectAt : Not implemented."); }
  static GetEnumeratedObjectCount(objects: any): number { throw new Error("EwsUtilities.ts - static GetEnumeratedObjectCount : Not implemented."); }


  //static GetEnumSchemaName(enumType: System.Type, enumName: string): string{ throw new Error("EwsUtilities.ts - static GetEnumSchemaName : Not implemented.");}
  //static GetEnumVersion(enumType: System.Type, enumName: string): ExchangeVersion{ throw new Error("EwsUtilities.ts - static GetEnumVersion : Not implemented.");}
  //static GetItemTypeFromXmlElementName(xmlElementName: string): System.Type{ throw new Error("EwsUtilities.ts - static GetItemTypeFromXmlElementName : Not implemented.");}

  static GetNamespaceFromUri(namespaceUri: string): XmlNamespace {
    switch (namespaceUri) {
      case this.EwsErrorsNamespace:
        return XmlNamespace.Errors;
      case this.EwsTypesNamespace:
        return XmlNamespace.Types;
      case this.EwsMessagesNamespace:
        return XmlNamespace.Messages;
      case this.EwsSoapNamespace:
        return XmlNamespace.Soap;
      case this.EwsSoap12Namespace:
        return XmlNamespace.Soap12;
      case this.EwsXmlSchemaInstanceNamespace:
        return XmlNamespace.XmlSchemaInstance;
      case this.PassportSoapFaultNamespace:
        return XmlNamespace.PassportSoapFault;
      case this.WSTrustFebruary2005Namespace:
        return XmlNamespace.WSTrustFebruary2005;
      case this.WSAddressingNamespace:
        return XmlNamespace.WSAddressing;
      default:
        return XmlNamespace.NotSpecified;
    }
  }
  static GetNamespacePrefix(xmlNamespace: XmlNamespace): string {
    switch (xmlNamespace) {
      case XmlNamespace.Types:
        return EwsUtilities.EwsTypesNamespacePrefix;
      case XmlNamespace.Messages:
        return EwsUtilities.EwsMessagesNamespacePrefix;
      case XmlNamespace.Errors:
        return EwsUtilities.EwsErrorsNamespacePrefix;
      case XmlNamespace.Soap:
      case XmlNamespace.Soap12:
        return EwsUtilities.EwsSoapNamespacePrefix;
      case XmlNamespace.XmlSchemaInstance:
        return EwsUtilities.EwsXmlSchemaInstanceNamespacePrefix;
      case XmlNamespace.PassportSoapFault:
        return EwsUtilities.PassportSoapFaultNamespacePrefix;
      case XmlNamespace.WSTrustFebruary2005:
        return EwsUtilities.WSTrustFebruary2005NamespacePrefix;
      case XmlNamespace.WSAddressing:
        return EwsUtilities.WSAddressingNamespacePrefix;
      case XmlNamespace.Autodiscover:
        return EwsUtilities.AutodiscoverSoapNamespacePrefix;
      default:
        return "";
    }
  }
  static GetNamespaceUri(xmlNamespace: XmlNamespace): string {
    switch (xmlNamespace) {
      case XmlNamespace.Types:
        return EwsUtilities.EwsTypesNamespace;
      case XmlNamespace.Messages:
        return EwsUtilities.EwsMessagesNamespace;
      case XmlNamespace.Errors:
        return EwsUtilities.EwsErrorsNamespace;
      case XmlNamespace.Soap:
        return EwsUtilities.EwsSoapNamespace;
      case XmlNamespace.Soap12:
        return EwsUtilities.EwsSoap12Namespace;
      case XmlNamespace.XmlSchemaInstance:
        return EwsUtilities.EwsXmlSchemaInstanceNamespace;
      case XmlNamespace.PassportSoapFault:
        return EwsUtilities.PassportSoapFaultNamespace;
      case XmlNamespace.WSTrustFebruary2005:
        return EwsUtilities.WSTrustFebruary2005Namespace;
      case XmlNamespace.WSAddressing:
        return EwsUtilities.WSAddressingNamespace;
      case XmlNamespace.Autodiscover:
        return EwsUtilities.AutodiscoverSoapNamespace;
      default:
        return "";
    }
  }


  public static GetPrintableTypeName(type: any /*instance */): string {
    var typename: string = typeof type;
    if (typename.indexOf("object") >= 0) {
      try {
        typename = type.__proto__.constructor.name;

      } catch (error) {
        typename += " - Error getting name";
      }
    }

    return typename;
    //         if (type.IsGenericType)
    //             {
    //                 // Convert generic type to printable form (e.g. List<Item>)
    //                 string genericPrefix = type.Name.Substring(0, type.Name.IndexOf('`'));
    //                 StringBuilder nameBuilder = new StringBuilder(genericPrefix);
    //
    //                 // Note: building array of generic parameters is done recursively. Each parameter could be any type.
    //                 string[] genericArgs = type.GetGenericArguments().ToList<Type>().ConvertAll<string>(t => GetPrintableTypeName(t)).ToArray<string>();
    //
    //                 nameBuilder.Append("<");
    //                 nameBuilder.Append(string.Join(",", genericArgs));
    //                 nameBuilder.Append(">");
    //                 return nameBuilder.ToString();
    //             }
    //             else if (type.IsArray)
    //             {
    //                 // Convert array type to printable form.
    //                 string arrayPrefix = type.Name.Substring(0, type.Name.IndexOf('['));
    //                 StringBuilder nameBuilder = new StringBuilder(EwsUtilities.GetSimplifiedTypeName(arrayPrefix));
    //                 for (int rank = 0; rank < type.GetArrayRank(); rank++)
    //                 {
    //                     nameBuilder.Append("[]");
    //                 }
    //                 return nameBuilder.ToString();
    //             }
    //             else
    //             {
    //                 return EwsUtilities.GetSimplifiedTypeName(type.Name);
    //             }
  }
  //static GetSimplifiedTypeName(typeName: string): string{ throw new Error("EwsUtilities.ts - static GetSimplifiedTypeName : Not implemented.");}
  static IsLocalTimeZone(timeZone: TimeZoneInfo): boolean {
    return (TimeZoneInfo.Local == timeZone) || (TimeZoneInfo.Local.Id == timeZone.Id && TimeZoneInfo.Local.HasSameRules(timeZone));
  }
  //static Parse(value: string): any{ throw new Error("EwsUtilities.ts - static Parse : Not implemented.");}
  static ParseEnum(value: string, ewsenum): any { throw new Error("EwsUtilities.ts - static Parse : Not implemented."); }
  static ParseAsUnbiasedDatetimescopedToServicetimeZone(dateString: string, service: ExchangeService): DateTime {
    // Convert the element's value to a DateTime with no adjustment.
    //var tempDate: DateTime = DateTime.Parse(dateString + "Z");

    // Set the kind according to the service's time zone
    if (service.TimeZone == TimeZoneInfo.Utc) {
      return DateTime.Parse(dateString, DateTimeKind.Utc);
    }
    else if (EwsUtilities.IsLocalTimeZone(service.TimeZone)) {
      return DateTime.Parse(dateString, DateTimeKind.Local);
    }
    else {
      return DateTime.DateimeStringToTimeZone(dateString, service.TimeZone.IanaId);
      //return DateTime.Parse(dateString, DateTimeKind.Unspecified);
    }
  }
  static ParseEnumValueList<T>(list: any[], value: string, separators: string, enumType: any): void {
    // EwsLogging.Assert(
    //         typeof(T).IsEnum,
    //         "EwsUtilities.ParseEnumValueList",
    //         "T is not an enum type.");

    if (!value) {
      return;
    }

    var enumValues: string[] = value.split(separators);

    for (var enumValue of enumValues) {
      var enumValueParsed = enumType[enumValue];
      if (typeof enumValueParsed !== 'undefined')
        list.push(enumValueParsed);
    }
  }
  //static SerializeEnum(value: any): string{ throw new Error("EwsUtilities.ts - static SerializeEnum : Not implemented.");}

  static SystemToEwsDayOfTheWeek(dayOfWeek: DayOfWeek): DayOfTheWeek {
    return <DayOfTheWeek><any>dayOfWeek;
  }

  static TimeSpanToXSDuration(timeSpan: TimeSpan): string {
    // Optional '-' offset
    var offsetStr: string = (timeSpan.TotalSeconds < 0) ? "-" : StringHelper.Empty;

    // The TimeSpan structure does not have a Year or Month
    // property, therefore we wouldn't be able to return an xs:duration
    // string from a TimeSpan that included the nY or nM components.
    return StringHelper.Format(
      "{0}P{1}DT{2}H{3}M{4}S",
      offsetStr,
      Math.abs(timeSpan.Days),
      Math.abs(timeSpan.Hours),
      Math.abs(timeSpan.Minutes),
      Math.abs(timeSpan.Seconds) + "." + Math.abs(timeSpan.Milliseconds));
  }
  private static numPad(num: number, length: number) {
    var str = num.toString();
    while (str.length < length)
      str = "0" + str;
    return str;
  }
  static TimeSpanToXSTime(timeSpan: TimeSpan): string {
    return StringHelper.Format(
      "{0}:{1}:{2}",
      this.numPad(timeSpan.Hours, 2),
      this.numPad(timeSpan.Minutes, 2),
      this.numPad(timeSpan.Seconds, 2));
  }
  static XSDurationToTimeSpan(xsDuration: string): TimeSpan {
    var regex: RegExp = /(-)?P(([0-9]+)Y)?(([0-9]+)M)?(([0-9]+)D)?(T(([0-9]+)H)?(([0-9]+)M)?(([0-9]+)(\.([0-9]+))?S)?)?/; //ref: info: not using \\, may be a bug in EWS managed api. does not match "-P2Y6M5DT12H35M30.4S" with \\ //old /(-)?P([0-9]+)Y?([0-9]+)M?([0-9]+)D?T([0-9]+)H?([0-9]+)M?([0-9]+\.[0-9]+)?S?/;

    if (xsDuration.match(regex) === null) {
      throw new ArgumentException(Strings.XsDurationCouldNotBeParsed);
    }
    return new TimeSpan(xsDuration);//using moment, it recognize the format.

  }
  //static TrueForAll(collection: System.Collections.Generic.IEnumerable<T>, predicate: any): boolean{ throw new Error("EwsUtilities.ts - static TrueForAll : Not implemented.");}

  static ValidateClassVersion(service: ExchangeService, minimumServerVersion: ExchangeVersion, className: string): void {
    if (service.RequestedServerVersion < minimumServerVersion) {
      throw new ServiceVersionException(
        StringHelper.Format(
          Strings.ClassIncompatibleWithRequestVersion,
          className,
          ExchangeVersion[minimumServerVersion]));
    }
  }

  static ValidateDomainNameAllowNull(domainName: string, paramName: string): void {

    //todo: validate domain names per ews managed api

    //if (domainName != null) {
    //    Regex regex = new Regex(DomainRegex);

    //    if (!regex.IsMatch(domainName)) {
    //        throw new ArgumentException(string.Format(Strings.InvalidDomainName, domainName), paramName);
    //    }
    //}
  }

  /**
   * Validates the enum value against the request version.
   *
   * @param   {RequiredServerVersionEnums}   enumType        one of Enum type which has RequiredServerVersionAttrubute
   * @param   {number}            enumValue        The enum value.
   * @param   {ExchangeVersion}   requestVersion   The request version.
   * @param   {string}            name   The request version.
   */
  static ValidateEnumVersionValue(enumType: RequiredServerVersionEnums, enumValue: number, requestVersion: ExchangeVersion, name: string): void {
    //default is 2007_SP1
    var enumVersion = ExchangeVersion.Exchange2007_SP1;
    //if it has RequiredServerVersionAttrubute (ews-javascript-api, those enums has static function named 'RequiredServerVersion' )
    if (TypeGuards.hasRequiredServerVersionAttribute(enumType)) {
      enumVersion = enumType.RequiredServerVersion(enumValue);
    }

    if (requestVersion < enumVersion) {
      throw new ServiceVersionException(
        StringHelper.Format(
          Strings.EnumValueIncompatibleWithRequestVersion,
          enumType[enumValue],
          name,
          ExchangeVersion[enumVersion]));
    }

  }
  static ValidateMethodVersion(service: ExchangeService, minimumServerVersion: ExchangeVersion, methodName: string): void {
    if (service.RequestedServerVersion < minimumServerVersion) {
      throw new ServiceVersionException(
        StringHelper.Format(
          Strings.MethodIncompatibleWithRequestVersion,
          methodName,
          ExchangeVersion[minimumServerVersion]));
    }
  }

  /**
   * Validates string parameter to be non-empty string (null value not allowed).
   *
   * @param   {string}   param       The string parameter.
   * @param   {string}   paramName   Name of the parameter.
   */
  static ValidateNonBlankStringParam(param: string, paramName: string): void {
    if (param == null) {
      throw new ArgumentNullException(paramName);
    }

    this.ValidateNonBlankStringParamAllowNull(param, paramName);
  }

  /**
   * Validates string parameter to be non-empty string (null value allowed).
   *
   * @param   {string}   param       The string parameter.
   * @param   {string}   paramName   Name of the parameter.
   */
  static ValidateNonBlankStringParamAllowNull(param: string, paramName: string): void {
    if (param) {
      // Non-empty string has at least one character which is *not* a whitespace character
      if (param.replace(/\s*/g, '').length === 0) {
        throw new ArgumentException(Strings.ArgumentIsBlankString, paramName);
      }
    }
  }

  /**
   * Validates parameter (null value not allowed).
   *
   * @param   {any}       param       The param.
   * @param   {string}    paramName   Name of the param.
   */
  static ValidateParam(param: any, paramName: string): void {
    var isValid = false;

    if (typeof (param) == "string") {
      isValid = !StringHelper.IsNullOrEmpty(param);
    }
    else {
      isValid = param != null && typeof (param) !== 'undefined';
    }

    if (!isValid) {
      throw new ArgumentNullException(paramName);
    }

    this.ValidateParamAllowNull(param, paramName);
  }

  /**
   * Validates parameter (and allows null value).
   *
   * @param   {any}       param       The param.
   * @param   {string}    paramName   Name of the param.
   */
  static ValidateParamAllowNull(param: any, paramName: string): void {
    var selfValidate: ISelfValidate = param;
    // look for null/undefined
    if (TypeGuards.isISelfValidate(selfValidate)) {//todo: interface detection for ISelfValidate
      try {
        selfValidate.Validate();
      }
      catch (e) {
        throw new ArgumentException(
          Strings.ValidationFailed,
          paramName,
          e);
      }
    }

    let ewsObject: ServiceObject = param;

    if (ewsObject instanceof TypeContainer.ServiceObject) {
      if (ewsObject.IsNew) {
        throw new ArgumentException(Strings.ObjectDoesNotHaveId, paramName);
      }
    }
  }

  /**
   * Validates parameter collection.
   *
   * @param   {any[]}     collection   The collection.
   * @param   {string}    paramName    Name of the param.
   */
  static ValidateParamCollection(collection: any[], paramName: string): void {
    this.ValidateParam(collection, paramName);

    let count: number = 0;

    for (let obj of collection) {
      try {
        this.ValidateParam(obj, StringHelper.Format("collection[{0}]", count));
      }
      catch (e) {
        throw new ArgumentException(
          StringHelper.Format("The element at position {0} is invalid", count),
          paramName,
          e);
      }

      count++;
    }

    if (count == 0) {
      throw new ArgumentException(Strings.CollectionIsEmpty, paramName);
    }
  }

  /**
   * Validates property version against the request version.
   *
   * @param   {ExchangeService}   service                The Exchange service.
   * @param   {ExchangeVersion}   minimumServerVersion   The minimum server version that supports the property.
   * @param   {string}            propertyName           Name of the property.
   */
  static ValidatePropertyVersion(service: ExchangeService, minimumServerVersion: ExchangeVersion, propertyName: string): void {
    if (service.RequestedServerVersion < minimumServerVersion) {
      throw new ServiceVersionException(
        StringHelper.Format(
          Strings.PropertyIncompatibleWithRequestVersion,
          propertyName,
          ExchangeVersion[minimumServerVersion]));
    }
  }
  static ValidateServiceObjectVersion(serviceObject: ServiceObject, requestVersion: ExchangeVersion): any { throw new Error("EwsUtilities.ts - static ValidateServiceObjectVersion : Not implemented."); }
  //static WriteTraceStartElement(writer: System.Xml.XmlWriter, traceTag: string, includeVersion: boolean): any{ throw new Error("EwsUtilities.ts - static WriteTraceStartElement : Not implemented.");}
}


export interface EnumToExhcangeVersionDelegateDictionary {
  [index: string]: EnumVersionDelegate;
}

export interface EnumVersionDelegate {
  (value: number): ExchangeVersion;
}


