/*******************************************************************************

Copyright (c) 2010, Perforce Software, Inc.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1.  Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in the
	documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*******************************************************************************/

/*******************************************************************************
 * Name		: BridgeInterfaceClasses.cs
 *
 * Author	: dbb
 *
 * Description	: Utility classes used to interface with the P4Bridged DLL.
 *
 ******************************************************************************/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace Perforce.P4
{
	/// <summary>
	/// The StrDictListIterator represents the iterator functions exposed by 
	/// the P4 bridge dll to allow the client to read structured (tagged) data.
	/// It is not exposed outside the p4.net assembly as the P4Server class
	/// translates the raw data into a .NET array list of hash tables.
	/// </summary>
	internal class StrDictListIterator : IDisposable
	{
		IntPtr pIterator = IntPtr.Zero;
		P4Server p4Server;

		public StrDictListIterator( P4Server p4server, IntPtr pObj )
		{
			pIterator = pObj;
			p4Server = p4server;
		}

		public bool NextItem()
		{
			IntPtr p = P4Bridge.GetNextItem( pIterator );
			if( p == IntPtr.Zero )
			{
				return false;
			}
			return true;
		}

		public KeyValuePair NextEntry()
		{
			IntPtr p = P4Bridge.GetNextEntry( pIterator );
			if( p == IntPtr.Zero )
			{
				return null;
			}
			return new KeyValuePair( p4Server, p );
		}

		public void Dispose()
		{
            // don't clean up the P4Bridge pointer, that should be done elsewhere
		}
	}

	/// <summary>
	/// Simple calls used to read key:value pairs from a StrDict object 
	/// referenced by a pointer returned from the bridge
	/// </summary>
	internal class KeyValuePair
	{
		public string Key;
		public string Value;

		public KeyValuePair( P4Server p4Server, IntPtr pObj )
		{
            Key = p4Server.MarshalPtrToString(P4Bridge.GetKey(pObj));
			Value = p4Server.MarshalPtrToString( P4Bridge.GetValue( pObj ) );
		}
	}

	/// <summary>
	/// Error severity levels.
	/// </summary>
	public enum ErrorSeverity : int
	{
		/// <summary>
		/// Unknown
		/// </summary>
		E_UNKNOWN = -1, 
		/// <summary>
		/// nothing yet 
		/// </summary>
		E_EMPTY = 0,	
		/// <summary>
		/// something good happened
		/// </summary>
		E_INFO = 1,	    
		/// <summary>
		/// something not good happened 
		/// </summary>
		E_WARN = 2,	    
		/// <summary>
		/// user did something wrong
		/// </summary>
		E_FAILED = 3,	
		/// <summary>
		/// system broken -- nothing can continue
		/// </summary>
		E_FATAL = 4,    
		/// <summary>
		/// Used to turnoff exceptions
		/// </summary>
		E_NOEXC = 9999  
	};

    /// <summary>
    /// Error Categories
    /// </summary>
	public enum ErrorGeneric : int
	{
        /// <summary>
        /// No Error
        /// </summary>
		EV_NONE = 0,	// misc

		// The fault of the user

        /// <summary>
        /// request not consistent with dox
        /// </summary>
		EV_USAGE = 0x01,	
        /// <summary>
        /// using unknown entity
        /// </summary>
		EV_UNKNOWN = 0x02,
        /// <summary>
        /// using entity in wrong context
        /// </summary>
		EV_CONTEXT = 0x03,
        /// <summary>
        /// trying to do something you can't
        /// </summary>
		EV_ILLEGAL = 0x04,
        /// <summary>
        /// something must be corrected first
        /// </summary>
		EV_NOTYET = 0x05,
        /// <summary>
        /// protections prevented operation
        /// </summary>
		EV_PROTECT = 0x06,

		// No fault at all
        /// <summary>
        /// Action returned empty results
        /// </summary>
		EV_EMPTY = 0x11,

		// not the fault of the user

        /// <summary>
        /// Inexplicable program fault
        /// </summary>
		EV_FAULT = 0x21,
        /// <summary>
        /// Client side program errors
        /// </summary>
		EV_CLIENT = 0x22,
        /// <summary>
        /// Server administrative action required
        /// </summary>
		EV_ADMIN = 0x23,
        /// <summary>
        /// Client configuration inadequate
        /// </summary>
		EV_CONFIG = 0x24,
        /// <summary>
        /// Client or server too old to interact
        /// </summary>
		EV_UPGRADE = 0x25,
        /// <summary>
        /// Communications error
        /// </summary>
		EV_COMM = 0x26,
        /// <summary>
        /// Something was too big
        /// </summary>
		EV_TOOBIG = 0x27

	} ;

    /// <summary>
    /// Error Subsystem Categories
    /// </summary>
	public enum ErrorSubsystem : int
    {
        /// <summary>
        /// OS error
        /// </summary>
		ES_OS = 0,
        /// <summary>
        /// Misc support
        /// </summary>
		ES_SUPP = 1,
        /// <summary>
        /// Librarian
        /// </summary>
		ES_LBR = 2,
        /// <summary>
        /// Messaging
        /// </summary>
		ES_RPC = 3,
        /// <summary>
        /// Database
        /// </summary>
        ES_DB = 4,
        /// <summary>
        /// Database support
        /// </summary>
		ES_DBSUPP = 5,
        /// <summary>
        /// Data manager
        /// </summary>
		ES_DM = 6,
        /// <summary>
        /// Top level of server
        /// </summary>
		ES_SERVER = 7,
        /// <summary>
        /// Top level of client
        /// </summary>
		ES_CLIENT = 8,
        /// <summary>
        /// Pseudo subsystem for information messages
        /// </summary>
		ES_INFO = 9,
        /// <summary>
        /// Pseudo subsystem for help messages
        /// </summary>
		ES_HELP = 10,
        /// <summary>
        /// Pseudo subsystem for spec/comment messages
        /// </summary>
		ES_SPEC = 11,
        /// <summary>
        /// P4FTP server
        /// </summary>
		ES_FTPD = 12,
        /// <summary>
        /// Perforce Broker
        /// </summary>
		ES_BROKER = 13,
        /// <summary>
        /// P4V and other Qt based clients
        /// </summary>
        ES_P4QT = 14,
        /// <summary>
        /// P4X3 server
        /// </summary>
        ES_X3SERVER = 15,
        /// <summary>
        /// Graph depot messages
        /// </summary>
        ES_GRAPH = 16,
        /// <summary>
        /// Scripting
        /// </summary>
        ES_SCRIPT = 17,
        /// <summary>
        /// Server Overflow
        /// </summary>
        ES_SERVER2 = 18,
        /// <summary>
        /// DM overflow
        /// </summary>
        ES_DM2 = 19,
        /// <summary>
        /// Help for configurables
        /// </summary>
        ES_CONFIG = 20
    };

	/*******************************************************************************
	 *  P4ClientError
	 ******************************************************************************/

	/// <summary>
	/// Class used to return a single error or warning from the bridge dll.
	/// </summary>
	public partial class P4ClientError
	{
		/// <summary>
		/// How severe is the error
		/// </summary>
		public ErrorSeverity SeverityLevel;
		/// <summary>
		/// Generic code for the error
		/// </summary>
		public int ErrorCode;
		/// <summary>
		/// Descriptive error message
		/// </summary>
		public string ErrorMessage;

		/// <summary>
		/// Create a new ClientError
		/// </summary>
		/// <param name="severityLevel"></param>
		/// <param name="errorMessage"></param>
		public P4ClientError(ErrorSeverity severityLevel, string errorMessage)
		{
			SeverityLevel = severityLevel;
			ErrorMessage = errorMessage;
		}

        /// <summary>
        /// Create a new ClientError
        /// </summary>
        /// <param name="p4Server"></param>
        /// <param name="pErr"></param>
        public P4ClientError(P4Server p4Server, IntPtr pErr)
		{
			SeverityLevel = (ErrorSeverity)P4Bridge.Severity(pErr);
			ErrorCode = P4Bridge.ErrorCode(pErr);
			if (p4Server != null)
			{
				ErrorMessage = p4Server.MarshalPtrToString(P4Bridge.Message(pErr));
			}
			else
			{
				ErrorMessage = P4Server.MarshalPtrToStringUtf8_Int(P4Bridge.Message(pErr));
			}
		}

		int ErrorSubcode 
		{
			get 
			{
				return unchecked(eErrorSubcode(ErrorCode));
			}
		}

		ErrorSubsystem ErrorSubsystem 
		{
			get 
			{
				return (ErrorSubsystem)unchecked(eErrorSubsystem(ErrorCode));
			}
		}

		ErrorGeneric ErrorGeneric 
		{
			get 
			{
				return (ErrorGeneric)unchecked(eErrorGeneric(ErrorCode));
			}
		}

		int ArgCount 
		{
			get 
			{
				return (eArgCount(ErrorCode));
			}
		}

		ErrorSeverity ErrorSeverity 
		{
			get 
			{
				return (ErrorSeverity)unchecked(eErrorSeverity(ErrorCode));
			}
		}

		int UniqueCode 
		{
			get 
			{
				return (eUniqueCode(ErrorCode));
			}
		}

		static int eErrorSubcode(int ErrorCode)
		{
			return unchecked(ErrorCode & 0x3ff);
		}

		static ErrorSubsystem eErrorSubsystem(int ErrorCode)
		{
			return (ErrorSubsystem)unchecked((ErrorCode >> 10) & 0x3f);
		}

		static ErrorGeneric eErrorGeneric(int ErrorCode)
		{
			return (ErrorGeneric)unchecked((ErrorCode >> 16) & 0xff);
		}

		static int eArgCount(int ErrorCode)
		{
			return ((ErrorCode >> 24) & 0x0f);
		}

		static ErrorSeverity eErrorSeverity(int ErrorCode)
		{
			return (ErrorSeverity)unchecked((ErrorCode >> 28) & 0x0f);
		}

		static int eUniqueCode(int ErrorCode)
		{
			return (ErrorCode & 0xffff);
		}

		/// <summary>
		/// Format the error in the form [ErrorLevel] Message
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return String.Format( "[{0}] {1}", Enum.GetName( typeof( ErrorSeverity ), SeverityLevel ), ErrorMessage );
		}

        /// <summary>
        /// Generate an error code from components
        /// </summary>
        /// <param name="sub">Error Subsystem</param>
        /// <param name="cod">Error code</param>
        /// <param name="sev">Error Severity</param>
        /// <param name="gen">Error Category</param>
        /// <param name="arg">Error Code argument</param>
        /// <returns></returns>
		public static int ErrorOf(ErrorSubsystem sub, int cod, ErrorSeverity sev, ErrorGeneric gen, int arg)
		{
			return ((int)sev << 28) | (arg << 24) | ((int)gen << 16) | ((int)sub << 10) | (cod);
		}

		// P4 API errors are defined in various files in the API including msgrpc.cc, msgclient.cc, msgdm.cc,
		// msghelp.cc, msglbr.cc, msgrpc.cc, msgserver.cc, and msgsupp.cc, in the p4\msgs directory
		//public static int HostKeyMismatch		= ErrorOf( ErrorSubsystem.ES_RPC, 49, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 3 );//, "******* WARNING P4PORT IDENTIFICATION HAS CHANGED! *******\n"
		//public static int HostKeyUnknown		= ErrorOf( ErrorSubsystem.ES_RPC, 48, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 3 ); //"The authenticity of '%host%' can't be established,\n"
		//public static int MustSetPassword		= ErrorOf( ErrorSubsystem.ES_SERVER, 307, ErrorSeverity.E_FAILED, ErrorGeneric.EV_USAGE, 0); // "Password must be set before access can be granted." 
		//public static int TicketOnly			= ErrorOf( ErrorSubsystem.ES_SERVER, 322,  ErrorSeverity.E_FAILED, ErrorGeneric.EV_USAGE, 0); // "Password not allowed at this server security level, use 'p4 login'."
		//public static int LoggedOut				= ErrorOf( ErrorSubsystem.ES_SERVER, 318, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 0); // "Your session was logged out, please login again."
		//public static int LoginExpired			= ErrorOf( ErrorSubsystem.ES_SERVER, 312, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 0 ); // "Your session has expired, please login again."
		//public static int NoSuchDomain2			= ErrorOf( ErrorSubsystem.ES_DM, 35, ErrorSeverity.E_FAILED, ErrorGeneric.EV_UNKNOWN, 3);//"%type% '%name%' unknown - use '%command%' command to create it."
		//public static int NotUnderRoot			= ErrorOf( ErrorSubsystem.ES_DB, 39, ErrorSeverity.E_FAILED, ErrorGeneric.EV_CONTEXT, 2 );// "Path '%path%' is not under client's root '%root%'."
		//public static int NotUnderClient		= ErrorOf( ErrorSubsystem.ES_DB, 40, ErrorSeverity.E_FAILED, ErrorGeneric.EV_CONTEXT, 2 );// "Path '%path%' is not under client '%client%'."
		//public static int IntegMovedUnmapped	= ErrorOf( ErrorSubsystem.ES_DM, 551, ErrorSeverity.E_INFO, 0, 2 );// "%depotFile% - not in client view (remapped from %movedFrom%)" };
		//public static int ExVIEW                = ErrorOf( ErrorSubsystem.ES_DM, 367, ErrorSeverity.E_WARN, ErrorGeneric.EV_EMPTY, 1 );// "[%argc% - file(s)|File(s)] not in client view." } ;
		//public static int ExVIEW2				= ErrorOf( ErrorSubsystem.ES_DM, 477, ErrorSeverity.E_WARN, ErrorGeneric.EV_EMPTY, 2);// "%!%[%argc% - file(s)|File(s)] not in client view." };
		//public static int ActionResolve111		= ErrorOf( ErrorSubsystem.ES_SERVER, 498, ErrorSeverity.E_WARN,  ErrorGeneric.EV_UPGRADE, 2);//"%localFile% - upgrade to a 2011.1 or later client to perform an interactive %resolveType% resolve, or use resolve -a."

		//public static int WeakPassword			= ErrorOf( ErrorSubsystem.ES_SERVER, 321,  ErrorSeverity.E_FAILED, ErrorGeneric.EV_USAGE, 0 ); // "The security level of this server requires the password to be reset."
		//public static int BadPassword			= ErrorOf( ErrorSubsystem.ES_SERVER, 21, ErrorSeverity.E_FAILED, ErrorGeneric.EV_CONFIG, 0);// "Perforce password (P4PASSWD) invalid or unset." 
		//public static int BadPassword0			= ErrorOf( ErrorSubsystem.ES_SERVER, 38, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 0 ); // "Password invalid."
		//public static int BadPassword1			= ErrorOf( ErrorSubsystem.ES_SERVER, 39, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 0 ); // "Passwords don't match."
		//public static int PasswordTooShort		= ErrorOf( ErrorSubsystem.ES_SERVER, 308, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 0 ); // "Password should be at least 8 characters in length."
		//public static int PasswordTooLong		= ErrorOf( ErrorSubsystem.ES_SERVER, 527, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 1 ); // "Password should be no longer than %maxLength% bytes in length."
		//public static int PasswordTooSimple		= ErrorOf( ErrorSubsystem.ES_SERVER, 309, ErrorSeverity.E_FAILED, ErrorGeneric.EV_ILLEGAL, 0); // "Password should be mixed case or contain non alphabetic characters."

		//public static int TcpAccept             = ErrorOf( ErrorSubsystem.ES_RPC, 11, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "TCP connection accept failed." } ;
		//public static int TcpConnect            = ErrorOf( ErrorSubsystem.ES_RPC, 12, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "TCP connect to %host% failed." } ;
		//public static int TcpHost               = ErrorOf( ErrorSubsystem.ES_RPC, 13, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "%host%: host unknown." } ;
		//public static int TcpListen             = ErrorOf( ErrorSubsystem.ES_RPC, 14, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "TCP listen on %service% failed." } ;
		//public static int TcpPortInvalid        = ErrorOf( ErrorSubsystem.ES_RPC, 22, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "TCP port number %service% is out of range." } ;
		//public static int TcpRecv               = ErrorOf( ErrorSubsystem.ES_RPC, 15, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "TCP receive failed." } ;
		//public static int TcpSend               = ErrorOf( ErrorSubsystem.ES_RPC, 16, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "TCP send failed." } ;
		//public static int TcpService            = ErrorOf( ErrorSubsystem.ES_RPC, 17, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "%service%: service unknown." } ;
		//public static int TcpPeerSsl            = ErrorOf( ErrorSubsystem.ES_RPC, 31, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "Failed client SSL connection setup, server not using SSL." };

		//public static int SslAccept             = ErrorOf( ErrorSubsystem.ES_RPC, 38, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "SSL connection accept failed %error%.\n\tClient must add SSL protocol prefix to P4PORT." };
		//public static int SslConnect			= ErrorOf( ErrorSubsystem.ES_RPC, 23, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 2 ); // "SSL connect to %host% failed %error%.\n\tRemove SSL protocol prefix from P4PORT." };
		//public static int SslListen				= ErrorOf( ErrorSubsystem.ES_RPC, 24, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "SSL listen on %service% failed." };
		//public static int SslRecv				= ErrorOf( ErrorSubsystem.ES_RPC, 25, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "SSL receive failed." };
		//public static int SslSend				= ErrorOf( ErrorSubsystem.ES_RPC, 26, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "SSL send failed." };
		//public static int SslClose				= ErrorOf( ErrorSubsystem.ES_RPC, 27, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "SSL close failed." };
		//public static int SslInvalid			= ErrorOf( ErrorSubsystem.ES_RPC, 28, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "Invalid operation for SSL on %service%." };
		//public static int SslCtx				= ErrorOf( ErrorSubsystem.ES_RPC, 29, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 1 ); // "Fail create ctx on %service%." };
		//public static int SslShutdown			= ErrorOf( ErrorSubsystem.ES_RPC, 30, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "SSL read/write failed since in Shutdown." };
		//public static int SslInit				= ErrorOf( ErrorSubsystem.ES_RPC, 32, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "Failed to initialize SSL library." };
		//public static int SslCleartext			= ErrorOf( ErrorSubsystem.ES_RPC, 33, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "Failed client connect, server using SSL.\nClient must add SSL protocol prefix to P4PORT." };
		//public static int SslCertGen			= ErrorOf( ErrorSubsystem.ES_RPC, 34, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "Unable to generate certificate or private key for server." };
		//public static int SslNoSsl				= ErrorOf( ErrorSubsystem.ES_RPC, 35, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "Trying to use SSL when SSL library has not been compiled into program." };
		//public static int SslBadKeyFile			= ErrorOf( ErrorSubsystem.ES_RPC, 36, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "Either privatekey.txt or certificate.txt files do not exist." };
		//public static int SslGetPubKey          = ErrorOf( ErrorSubsystem.ES_RPC, 40, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "Unable to get public key for token generation." } ;
		//public static int SslBadDir             = ErrorOf( ErrorSubsystem.ES_RPC, 41, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "P4SSLDIR not defined or does not reference a valid directory." } ;
		//public static int SslBadFsSecurity      = ErrorOf( ErrorSubsystem.ES_RPC, 42, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "P4SSLDIR directory or key and certificate files not secure." } ;
		//public static int SslDirHasCreds        = ErrorOf( ErrorSubsystem.ES_RPC, 43, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "P4SSLDIR contains credentials, please remove key and certificate files." } ;
		//public static int SslCredsBadOwner      = ErrorOf( ErrorSubsystem.ES_RPC, 44, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "P4SSLDIR or credentials files not owned by Perforce process effective user." } ;
		//public static int SslCertBadDates       = ErrorOf( ErrorSubsystem.ES_RPC, 45, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "Certificate date range invalid." } ;
		//public static int SslNoCredentials      = ErrorOf( ErrorSubsystem.ES_RPC, 46, ErrorSeverity.E_FATAL, ErrorGeneric.EV_COMM, 0 ); // "SSL credentials do not exist." } ;
		//public static int SslFailGetExpire      = ErrorOf( ErrorSubsystem.ES_RPC, 47, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "Failed to get certificate's expiration date." } ;

		//public static int Connect				= ErrorOf( ErrorSubsystem.ES_CLIENT, 1, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "Connect to server failed; check $P4PORT." } ;
		//public static int ZCResolve				= ErrorOf( ErrorSubsystem.ES_CLIENT, 33, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 0 ); // "Zeroconf resolved '%name%' to '%port%'." } ;
		//public static int Fatal					= ErrorOf( ErrorSubsystem.ES_CLIENT, 2, ErrorSeverity.E_FATAL, ErrorGeneric.EV_CLIENT, 0); // "Fatal client error; disconnecting!" };

		//public static int MsgOs_Net				= ErrorOf( ErrorSubsystem.ES_OS, 3, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 3 );// "%operation%: %arg%: %errmsg%" } ;
		//public static int MsgOs_NetUn			= ErrorOf( ErrorSubsystem.ES_OS, 4, ErrorSeverity.E_FAILED, ErrorGeneric.EV_COMM, 3 );// "%operation%: %arg%: unknown network error %errno%" } ;

        /// <summary>
        /// Test if an error is a Login Error
        /// </summary>
        /// <param name="errorNum">Error Number</param>
        /// <returns>true if error is a Login Error</returns>
		public static bool IsLoginError(int errorNum)
		{
			return (errorNum == MsgServer_BadPassword) || (errorNum == MsgServer_MustSetPassword) || (errorNum == MsgServer_LoggedOut);
		}

        /// <summary>
        /// Test if an error is a Bad Password error
        /// </summary>
        /// <param name="errorNum">Error Number</param>
        /// <returns>true if error is a Bad Password Error</returns>
		public static bool IsBadPasswdError(int errorNum)
		{
			return (errorNum == MsgServer_WeakPassword) || (errorNum == MsgServer_BadPassword0) || (errorNum == MsgServer_BadPassword1) ||
				(errorNum == MsgServer_PasswordTooShort) || (errorNum == MsgServer_PasswordTooLong) || (errorNum == MsgServer_PasswordTooSimple);
		}

        /// <summary>
        /// Test if an error is a TCP error
        /// </summary>
        /// <param name="errorNum">Error Number</param>
        /// <returns>true if error is a TCP error</returns>
		public static bool IsTCPError(int errorNum)
		{
			return (errorNum == MsgRpc_TcpAccept) || (errorNum == MsgRpc_TcpConnect) || (errorNum == MsgRpc_TcpHost) ||
				(errorNum == MsgRpc_TcpListen) || (errorNum == MsgRpc_TcpPortInvalid) || (errorNum == MsgRpc_TcpRecv) ||
				(errorNum == MsgRpc_TcpSend) || (errorNum == MsgRpc_TcpService) || (errorNum == MsgRpc_TcpPeerSsl) ||
				(errorNum == MsgClient_Connect) || (errorNum == MsgClient_ZCResolve) || (errorNum == MsgClient_Fatal) ||
				(errorNum == MsgOs_Net) || (errorNum == MsgOs_NetUn);
		}

        /// <summary>
        /// Test if an error is an SSL error
        /// </summary>
        /// <param name="errorNum">Error Number</param>
        /// <returns>true if error is an SSL error</returns>
		public static bool IsSSLError(int errorNum)
		{
			return (errorNum == MsgRpc_SslAccept) || (errorNum == MsgRpc_SslConnect) || (errorNum == MsgRpc_SslListen) ||
				(errorNum == MsgRpc_SslRecv) || (errorNum == MsgRpc_SslSend) || (errorNum == MsgRpc_SslClose) ||
				(errorNum == MsgRpc_SslInvalid) || (errorNum == MsgRpc_SslCtx) || (errorNum == MsgRpc_SslShutdown) ||
				(errorNum == MsgRpc_SslInit) || (errorNum == MsgRpc_SslCleartext) || (errorNum == MsgRpc_SslCertGen) ||
				(errorNum == MsgRpc_SslNoSsl) || (errorNum == MsgRpc_SslBadKeyFile) || (errorNum == MsgRpc_SslGetPubKey) ||
				(errorNum == MsgRpc_SslBadDir) || (errorNum == MsgRpc_SslBadFsSecurity) || (errorNum == MsgRpc_SslDirHasCreds) ||
				(errorNum == MsgRpc_SslCredsBadOwner) || (errorNum == MsgRpc_SslCertBadDates) ||
				(errorNum == MsgRpc_SslNoCredentials) || (errorNum == MsgRpc_SslFailGetExpire);
		}
	}

	/*******************************************************************************
	 *  P4ClientErrorList
	 ******************************************************************************/

	/// <summary>
	/// Class used to return a list of errors and warnings returned by a command.
	/// </summary>
	public class P4ClientErrorList : List<P4ClientError>
	{
		/// <summary>
		/// Read an error list from the server by traversing the linked list.
		/// </summary>
		/// <param name="p4Server">Perforce server</param>
		/// <param name="pObj">First error in the list</param>
		private void Init( P4Server p4Server, IntPtr pObj )
		{
			IntPtr pNext = pObj;

			while (pNext != IntPtr.Zero)
			{
				IntPtr pCurrent = pNext; 

				pNext = P4Bridge.Next(pNext);
				this.Add(new P4ClientError(p4Server, pCurrent));
			}
		}

		/// <summary>
		/// Create a list of a single error
		/// </summary>
		/// <param name="Err">Error</param>
		internal P4ClientErrorList(P4ClientError Err)
		{
            if (Err != null)
			    this.Add(Err);
		}

		/// <summary>
		/// Create a list of a single error
		/// </summary>
		/// <param name="msg">Error message</param>
		/// <param name="severity">Severity level</param>
		internal P4ClientErrorList(string msg, ErrorSeverity severity)
		{
			this.Add(new P4ClientError(severity, msg));
		}

		/// <summary>
		/// Read the error list from the server
		/// </summary>
		/// <param name="p4Server"></param>
		/// <param name="pObj"></param>
		public P4ClientErrorList( P4Server p4Server, IntPtr pObj )
		{
			Init( p4Server, pObj );
		}

        /// <summary>
        /// Create a list from the current error results on the server
        /// </summary>
        /// <param name="p4Server">Perforce Server</param>
        /// <param name="cmdId">Command id</param>
        public P4ClientErrorList( P4Server p4Server, uint cmdId )
		{
			IntPtr pObj = P4Bridge.GetErrorResults( p4Server.pServer, cmdId );

			Init( p4Server, pObj );
		}

		/// <summary>
		/// Cast the errors to a String[]
		/// </summary>
		/// <param name="l">the list to cast </param>
		/// <returns></returns>
		public static implicit operator String[](P4ClientErrorList l)
		{
			String[] r = new String[l.Count];
			int idx = 0;
			foreach (P4ClientError e in l)
				r[idx++] = e.ToString();
			return r;
		}

		/// <summary>
        /// Cast the errors to a String. Each error is separated by NewLine
		/// </summary>
		/// <param name="l">the list to cast </param>
		/// <returns></returns>
		public static implicit operator String(P4ClientErrorList l)
		{
			StringBuilder r = new StringBuilder(l.Count * 80);
			foreach (P4ClientError e in l)
			{
				r.Append(e.ToString());
                r.Append($"{Environment.NewLine}");
			}
			return r.ToString();
		}
	}

	/*******************************************************************************
	 *  P4ClientInfoMessage
	 ******************************************************************************/

	/// <summary>
	/// Class used to return a info message from the bridge dll.
	/// </summary>
	public partial class P4ClientInfoMessage
	{
		/// <summary>
		/// Message level
		/// </summary>
		public int MessageLevel;
		/// <summary>
		/// Generic code for the message
		/// </summary>
		public int MessageCode;
		/// <summary>
		/// Message
		/// </summary>
		public string Message;

		/// <summary>
		/// Create a new ClientError
		/// </summary>
		/// <param name="msgCode"></param>
		/// <param name="level"></param>
		/// <param name="message"></param>
		public P4ClientInfoMessage(int msgCode, int level, string message)
		{
			MessageCode = msgCode;
			MessageLevel = level;
			Message = message;
		}

        /// <summary>
        /// Create a new P4ClientInfoMessage
        /// </summary>
        /// <param name="p4Server"></param>
        /// <param name="pMsg"></param>
        public P4ClientInfoMessage(P4Server p4Server, IntPtr pMsg)
		{
			MessageLevel = ((int) P4Bridge.MessageLevel(pMsg)) - ((int) '0');
			MessageCode = P4Bridge.InfoMsgCode(pMsg);
			// message is always in ASCII
			Message = p4Server.MarshalPtrToString(P4Bridge.InfoMessage(pMsg));
			if (p4Server != null)
			{
				Message = p4Server.MarshalPtrToString(P4Bridge.InfoMessage(pMsg));
			}
			else
			{
				Message = P4Server.MarshalPtrToStringUtf8_Int(P4Bridge.Message(pMsg));
			}
		}
        /// <summary>
        /// Convert message to string
        /// </summary>
        /// <returns></returns>
		public override string ToString()
		{
			return string.Format("{0}:{1}", MessageLevel, Message);
		}
	}

	/*******************************************************************************
	 *  P4ClientInfoMessageList
	 ******************************************************************************/

	/// <summary>
	/// Class used to return a list of info Messages returned by a command.
	/// </summary>
	public class P4ClientInfoMessageList : List<P4ClientInfoMessage>
	{
		/// <summary>
		/// Read an error list from the server by traversing the linked list.
		/// </summary>
		/// <param name="p4Server">Perforce server</param>
		/// <param name="pObj">First error in the list</param>
		private void Init(P4Server p4Server, IntPtr pObj)
		{
			IntPtr pNext = pObj;

			while (pNext != IntPtr.Zero)
			{
				IntPtr pCurrent = pNext;

				pNext = P4Bridge.NextInfoMsg(pNext);
				this.Add(new P4ClientInfoMessage(p4Server, pCurrent));
			}
		}

		/// <summary>
		/// Create an empty list
		/// </summary>
		internal P4ClientInfoMessageList()
		{
		}

		/// <summary>
		/// Create a list of a single error
		/// </summary>
		/// <param name="Msg">Error</param>
		internal P4ClientInfoMessageList(P4ClientInfoMessage Msg)
		{
			this.Add(Msg);
		}

		/// <summary>
		/// Create a list of a single error
		/// </summary>
		/// <param name="msg">Error message</param>
		/// <param name="level">Message level</param>
		internal P4ClientInfoMessageList(string msg, int level)
		{
			this.Add(new P4ClientInfoMessage(0, level, msg));
		}

        /// <summary>
        /// Read the error list from the server
        /// </summary>
        /// <param name="p4Server"></param>
        /// <param name="pObj"></param>
        /// <param name="Count"></param>
        public P4ClientInfoMessageList(P4Server p4Server, IntPtr pObj, int Count)
            : base(Count)
		{
			Init(p4Server, pObj);
		}

        /// <summary>
        /// Create a list from the current error results on the server
        /// </summary>
        /// <param name="p4Server">Perforce Server</param>
        /// <param name="cmdId">Unique ID</param>
        public P4ClientInfoMessageList(P4Server p4Server, uint cmdId)
		{
			IntPtr pObj = P4Bridge.GetErrorResults(p4Server.pServer, cmdId);

			Init(p4Server, pObj);
		}

		/// <summary>
		/// Cast the messages to a String[]
		/// </summary>
		/// <param name="l">the list to cast </param>
		/// <returns></returns>
		public static implicit operator String[](P4ClientInfoMessageList l)
		{
			if (l == null)
			{
				return null;
			}
			String[] r = new String[l.Count];
			int idx = 0;
			foreach (P4ClientInfoMessage m in l)
				r[idx++] = m.ToString();
			return r;
		}

		/// <summary>
        /// Cast the errors to a String. Each error is separated by NewLine
		/// </summary>
		/// <param name="l">the list to cast </param>
		/// <returns></returns>
		public static implicit operator String(P4ClientInfoMessageList l)
		{
			if (l == null)
			{
				return null;
			}
			StringBuilder r = new StringBuilder(l.Count * 80);
			foreach (P4ClientInfoMessage m in l)
			{
				r.Append(m.ToString());
                r.Append($"{Environment.NewLine}");
			}
			return r.ToString();
		}
	}

	/// <summary>
	/// Used to wrap a byte[] that is pinned in memory to pass down to the dll.
	/// In general, since .NET does not natively support marshaling Strings to
	/// UTF-8 for char * parameters, we must do our own encoding of the 
	/// strings into byte[], pin them, pass them to the dll, and then free the
	/// pinned memory on return. This class wraps that process with an 
	/// IDisposable object to ensure the memory is freed after the call.
	/// </summary>
	internal class PinnedByteArray :IDisposable
	{
		private byte[] Value;
		private GCHandle hValue;
		private IntPtr pValue;

		private bool isValid;

		public static PinnedByteArray nullv = new PinnedByteArray();

		private PinnedByteArray()
		{
			isValid = false;
		}

		~PinnedByteArray()
		{
			Free();
		}

		public PinnedByteArray( byte[] value )
		{
			Value = value;
			hValue = GCHandle.Alloc( Value, GCHandleType.Pinned );
			pValue = hValue.AddrOfPinnedObject();

			isValid = true;
		}

		public static implicit operator IntPtr( PinnedByteArray p )
		{
			if (p == null)
			{
				return IntPtr.Zero;
			}
			return p.pValue;
		}

		public static implicit operator GCHandle( PinnedByteArray p )
		{
			if (p == null)
			{
				throw new ArgumentNullException("p");
			}
			return p.hValue;
		}

		public static implicit operator byte[]( PinnedByteArray p )
		{
			if (p == null)
			{
				return null;
			}
			return p.Value;
		}

		public void Free()
		{
			if( !isValid )
				return;

			isValid = false;

			Value = null;

			hValue.Free();
			// hValue = null; value type so can set to null

			pValue = IntPtr.Zero;
		}

		public void Dispose()
		{
			Free();
		}
	}

	/**********************************************************************
	*
	*  P4ClientMerge
	*
	*  This simple class is a ClientMerge object.
	*
	*********************************************************************/
    /// <summary>
    /// ClientMerge object - retains state
    /// </summary>
	public class P4ClientMerge
	{
        /// <summary>
        /// Type of merge
        /// </summary>
		public enum MergeType
		{
            /// <summary>
            /// Binary merge
            /// </summary>
			CMT_BINARY = 0,
            /// <summary>
            /// 3-way text merge
            /// </summary>
			CMT_3WAY,
            /// <summary>
            /// 2-way text merge
            /// </summary>
			CMT_2WAY
		} ;

        /// <summary>
        /// Status of merge
        /// </summary>
		public enum MergeStatus
		{
            /// <summary>
            /// Not determined
            /// </summary>
			CMS_NONE = -1,
            /// <summary>
            /// User wants to quit
            /// </summary>
			CMS_QUIT = 0,
	        /// <summary>
	        /// Skip the integration record
	        /// </summary>
            CMS_SKIP,
            /// <summary>
            /// Accepted merged theirs and yours
            /// </summary>
			CMS_MERGED,
            /// <summary>
            /// Accepted edited merge
            /// </summary>
			CMS_EDIT,
            /// <summary>
            /// Accepted theirs
            /// </summary>
			CMS_THEIRS,
            /// <summary>
            /// Accepted yours
            /// </summary>
			CMS_YOURS
		} ;

        /// <summary>
        /// Options for Merge
        /// </summary>
		public enum MergeForce
		{
            /// <summary>
            /// Don't force "-am"
            /// </summary>
			CMF_AUTO = 0,
            /// <summary>
            /// Accept only non-conflicts  "-as"
            /// </summary>
			CMF_SAFE,
            /// <summary>
            /// Accept only non-conflicts "-af"
            /// </summary>
			CMF_FORCE
		} ;

        P4Server p4Server = null;
		IntPtr pObj = IntPtr.Zero;

        /// <summary>
        /// P4ClientMerge Constructor
        /// </summary>
        /// <param name="pserver"></param>
        /// <param name="p"></param>
		public P4ClientMerge(P4Server pserver,IntPtr p)
		{
			p4Server = pserver;
			pObj = p;
		}

        /// <summary>
        /// AutoResolve
        /// </summary>
        /// <param name="forceMerge">force the merge</param>
        /// <returns>MergeStatus</returns>
		public MergeStatus AutoResolve(MergeForce forceMerge)
		{
			return (MergeStatus)P4ClientMergeBridge.AutoResolve(pObj, (int)forceMerge);
		}

        /// <summary>
        /// Do a resolve
        /// </summary>
        /// <returns></returns>
		public MergeStatus Resolve()
		{
			return (MergeStatus)P4ClientMergeBridge.Resolve(pObj);
		}

        /// <summary>
        /// Detect if a resolve has been done
        /// </summary>
        /// <returns></returns>
		public MergeStatus DetectResolve()
		{
			return (MergeStatus)P4ClientMergeBridge.DetectResolve(pObj);
		}

        /// <summary>
        /// Check that the merge is acceptable
        /// </summary>
        /// <returns>bool Acceptable</returns>
		public bool IsAcceptable()
		{
			return (bool)(P4ClientMergeBridge.IsAcceptable(pObj) != 0);
		}

        /// <summary>
        /// GetBaseFile name
        /// </summary>
        /// <returns>string filename</returns>
		public string GetBaseFile()
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetBaseFile(pObj) );
		}

        /// <summary>
        /// GetYourFile name
        /// </summary>
        /// <returns>string filename</returns>
		public string GetYourFile()
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetYourFile(pObj) );
		}

        /// <summary>
        /// GetTheirFile name
        /// </summary>
        /// <returns>string filename</returns>
		public string GetTheirFile()
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetTheirFile(pObj) );
		}

        /// <summary>
        /// GetResultFile name
        /// </summary>
        /// <returns>string filename</returns>
		public string GetResultFile()
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetResultFile(pObj) );
		}

        /// <summary>
        /// GetYourChunks
        /// </summary>
        /// <returns>count of chunks</returns>
		public int GetYourChunks()
		{
			return P4ClientMergeBridge.GetYourChunks(pObj);
		}

        /// <summary>
        /// GetTheirChunks
        /// </summary>
        /// <returns>count of chunks</returns>
		public int GetTheirChunks()
		{
			return P4ClientMergeBridge.GetTheirChunks(pObj);
		}

        /// <summary>
        /// GetBothChunks
        /// </summary>
        /// <returns>count of chunks</returns>
		public int GetBothChunks()
		{
			return P4ClientMergeBridge.GetBothChunks(pObj);
		}

        /// <summary>
        /// GetConflictChunks
        /// </summary>
        /// <returns>count of chunks</returns>
		public int GetConflictChunks()
		{
			return P4ClientMergeBridge.GetConflictChunks(pObj);
		}

        /// <summary>
        /// GetMergeDigest
        /// </summary>
        /// <param name="pObj"></param>
        /// <returns>string digest</returns>
		public string GetMergeDigest(IntPtr pObj)
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetMergeDigest(pObj) );
		}

        /// <summary>
        /// GetYourDigest
        /// </summary>
        /// <returns>string digest</returns>
		public string GetYourDigest()
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetYourDigest(pObj) );
		}

        /// <summary>
        /// GetTheirDigest
        /// </summary>
        /// <returns>string digest</returns>
		public string GetTheirDigest()
		{
			return p4Server.MarshalPtrToString( P4ClientMergeBridge.GetTheirDigest(pObj) );
		}

        /// <summary>
        /// GetLastError
        /// </summary>
        /// <param name="pObj"></param>
        /// <returns>P4ClientError object</returns>

		public P4ClientError GetLastError(IntPtr pObj)
		{
			return new P4ClientError(null, P4ClientMergeBridge.GetLastError(pObj));
		}
	}
	/**********************************************************************
	*
	*  P4ClientResolve
	*
	*  This simple class is a ClientResolve object.
	*
	*********************************************************************/
	/// <summary>
	/// Class containing the DLL imports for the P4Bridge DLL.
	/// </summary>
	public class P4ClientResolve
	{
        P4Server p4Server = null;
		IntPtr pObj = IntPtr.Zero;

		public P4ClientResolve(P4Server pserver, IntPtr p)
		{
			p4Server = pserver;
			pObj = p;
		}

		public P4ClientMerge.MergeStatus AutoResolve(P4ClientMerge.MergeForce force)
		{
			return (P4ClientMerge.MergeStatus)P4ClientResolveBridge.AutoResolve(pObj, (int)force);
		}

		public P4ClientMerge.MergeStatus Resolve(bool preview)
		{
			return (P4ClientMerge.MergeStatus)P4ClientResolveBridge.Resolve(pObj, preview);
		}

		private string _resolveType = null;
		public string ResolveType
		{
			get
			{
				if (_resolveType == null)
				{
					_resolveType = GetResolveType();
				}
				return _resolveType;
			}
		}

	private string _mergeAction;
	public string MergeAction
	{
		get
		{
			if (_mergeAction == null)
			{
				_mergeAction = GetMergeAction();
			}
			return _mergeAction;
		}
	}
	private string _yoursAction;
	public string YoursAction
	{
		get
		{
			if (_yoursAction == null)
			{
				_yoursAction = GetYoursAction();
			}
			return _yoursAction;
		}
	}
	private string _theirAction;
	public string TheirAction
	{
		get
		{
			if (_theirAction == null)
			{
				_theirAction = GetTheirAction();
			}
			return _theirAction;
		}
	}

	// For the CLI interface, probably not of interest to others

	private string _mergePrompt;
	public string MergePrompt
	{
		get
		{
			if (_mergePrompt == null)
			{
				_mergePrompt = GetMergePrompt();
			}
			return _mergePrompt;
		}
	}
	private string _yoursPrompt;
	public string YoursPrompt
	{
		get
		{
			if (_yoursPrompt == null)
			{
				_yoursPrompt = GetYoursPrompt();
			}
			return _yoursPrompt;
		}
	}
	private string _theirPrompt;
	public string TheirPrompt
	{
		get
		{
			if (_theirPrompt == null)
			{
				_theirPrompt = GetTheirPrompt();
			}
			return _theirPrompt;
		}
	}

	private string _mergeOpt;
	public string MergeOpt
	{
		get
		{
			if (_mergeOpt == null)
			{
				_mergeOpt = GetMergeOpt();
			}
			return _mergeOpt;
		}
	}
	private string _yoursOpt;
	public string YoursOpt
	{
		get
		{
			if (_yoursOpt == null)
			{
				_yoursOpt = GetYoursOpt();
			}
			return _yoursOpt;
		}
	}
	private string _theirOpt;
	public string TheirOpt
	{
		get
		{
			if (_theirOpt == null)
			{
				_theirOpt = GetTheirOpt();
			}
			return _theirOpt;
		}
	}
	private string _skipOpt;
	public string SkipOpt
	{
		get
		{
			if (_skipOpt == null)
			{
				_skipOpt = GetSkipOpt();
			}
			return _skipOpt;
		}
	}
	private string _helpOpt;
	public string HelpOpt
	{
		get
		{
			if (_helpOpt == null)
			{
				_helpOpt = GetHelpOpt();
			}
			return _helpOpt;
		}
	}
	private string _autoOpt;
	public string AutoOpt
	{
		get
		{
			if (_autoOpt == null)
			{
				_autoOpt = GetAutoOpt();
			}
			return _autoOpt;
		}
	}

	private string _prompt;
	public string Prompt
	{
		get
		{
			if (_prompt == null)
			{
				_prompt = GetPrompt();
			}
			return _prompt;
		}
	}
	private string _typePrompt;
	public string TypePrompt
	{
		get
		{
			if (_typePrompt == null)
			{
				_typePrompt = GetTypePrompt();
			}
			return _typePrompt;
		}
	}
	private string _usageError;
	public string UsageError
	{
		get
		{
			if (_usageError == null)
			{
				_usageError = GetUsageError();
			}
			return _usageError;
		}
	}
	private string _help;
	public string Help
	{
		get
		{
			if (_help == null)
			{
				_help = GetHelp();
			}
			return _help;
		}
	}
        /// <summary>
        /// GetResolveType
        /// </summary>
        /// <returns>string resolveType</returns>
		private String GetResolveType()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetResolveType(pObj));
		}

        /// <summary>
        /// GetMergeAction()
        /// </summary>
        /// <returns>string MergeAction</returns>
		private String GetMergeAction()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetMergeAction(pObj));
		}

        /// <summary>
        /// GetYoursAction
        /// </summary>
        /// <returns>string action</returns>
		private String GetYoursAction()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetYoursAction(pObj));
		}

        /// <summary>
        /// GetTheirAction  
        /// </summary>
        /// <returns>string action</returns>
		private String GetTheirAction()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetTheirAction(pObj));
		}

        /// <summary>
        /// GetMergePrompt
        /// </summary>
        /// <returns>string merge prompt</returns>
		private String GetMergePrompt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetMergePrompt(pObj));
		}

        /// <summary>
        /// GetYoursPrompt
        /// </summary>
        /// <returns></returns>
		private String GetYoursPrompt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetYoursPrompt(pObj));
		}

        /// <summary>
        /// GetTheirPrompt
        /// </summary>
        /// <returns>string prompt</returns>
		private String GetTheirPrompt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetTheirPrompt(pObj));
		}

        /// <summary>
        /// GetMergeOpt
        /// </summary>
        /// <returns>string merge option</returns>
		private String GetMergeOpt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetMergeOpt(pObj));
		}

        /// <summary>
        /// GetYoursOpt()
        /// </summary>
        /// <returns>string yours opt</returns>
		private String GetYoursOpt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetYoursOpt(pObj));
		}

        /// <summary>
        /// GetTheirOpt
        /// </summary>
        /// <returns>string their opt</returns>
		private String GetTheirOpt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetTheirOpt(pObj));
		}

        /// <summary>
        /// Get Skip
        /// </summary>
        /// <returns></returns>
		private String GetSkipOpt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetSkipOpt(pObj));
		}

        /// <summary>
        /// GetHelpOpt
        /// </summary>
        /// <returns>string help options</returns>
		private String GetHelpOpt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetHelpOpt(pObj));
		}

        /// <summary>
        /// GetAutoOpt
        /// </summary>
        /// <returns>string auto opt</returns>
		private String GetAutoOpt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetAutoOpt(pObj));
		}

        /// <summary>
        /// Get
        /// </summary>
        /// <returns></returns>
		private String GetPrompt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetPrompt(pObj));
		}

        /// <summary>
        /// GetTypePrompt
        /// </summary>
        /// <returns>string type prompt</returns>
		private String GetTypePrompt()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetTypePrompt(pObj));
		}

        /// <summary>
        /// GetUsageError
        /// </summary>
        /// <returns>string usage error</returns>
		private String GetUsageError()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetUsageError(pObj));
		}

        /// <summary>
        /// GetHelp()
        /// </summary>
        /// <returns>string help</returns>
		private String GetHelp()
		{
			return p4Server.MarshalPtrToString(P4ClientResolveBridge.GetHelp(pObj));
		}

        /// <summary>
        /// GetLastError
        /// </summary>
        /// <returns>P4ClientError error</returns>
		public P4ClientError GetLastError()
		{
			return new P4ClientError(null, P4ClientResolveBridge.GetLastError(pObj));
		}
	}

	/// <summary>
	/// Wrapper for an array of PinnedByteArrays, used to wrap the arg list
	/// passed to a command to make sure the pinned memory is freed.
	/// </summary>
	internal class PinnedByteArrays : IDisposable
	{
		private PinnedByteArray[] Values;
		private IntPtr[] pValues = null;

		public PinnedByteArrays( PinnedByteArray[] values )
		{
			Values = values;
		}

		~PinnedByteArrays()
		{
			Free();
		}

		public static explicit operator IntPtr[]( PinnedByteArrays p )
		{
			if( p.pValues != null )
				return p.pValues;

			p.pValues = new IntPtr[ p.Values.Length ];
			for( int idx = 0; idx < p.Values.Length; idx++ )
			{
				p.pValues[ idx ] = (IntPtr)p.Values[ idx ];
			}
			return p.pValues;
		}

		public PinnedByteArray this[ int idx ]
		{
			get { return Values[ idx ]; }
		}

		public void Free()
		{
			if( Values == null )
				return;

			for( int idx = 0; idx < Values.Length; idx++ )
			{
				Values[ idx ].Free();
			}
		}

		public void Dispose()
		{
			Free();
		}
	}
}
