[Runtime] Changed Gencode for WrapRawException

USING System
USING System.Collections.Generic
USING System.Text
USING System.Reflection
BEGIN NAMESPACE XSharp
  /// XSharp Runtime base Error class
  CLASS Error INHERIT Exception
    /// A string representing the name of the subsystem generating the error.
    VIRTUAL PROPERTY SubSystem AS STRING AUTO  := "BASE"
    /// An integer numeric value representing a Visual Objects generic error code.
    /// <Seealso cref="T:XSharp.Gencode"/>
    VIRTUAL PROPERTY Gencode AS DWORD AUTO    := EG_UNKNOWN
    /// An string containing the description of the Gencode.
    VIRTUAL PROPERTY GenCodeText AS STRING GET IIF (Gencode != 0, ErrString(Gencode), "Unknown GenCode")
    /// An integer numeric value representing a subsystem-specific error code.
    VIRTUAL PROPERTY SubCode AS DWORD AUTO    := 0
    /// An string containing the description of the SubCode.
    VIRTUAL PROPERTY SubCodeText AS STRING GET IIF (SubCode != 0, __CavoStr(SubCode), "Unknown SubCode")
    /// A string representing the name of the function or method in which the error occurred.
    VIRTUAL PROPERTY FuncSym AS STRING AUTO   := ""
    /// A string representing the name used to open the file associated with the error condition.
    VIRTUAL PROPERTY FileName AS STRING AUTO  := ""
    /// A constant indicating the severity of the error condition.
    /// <Seealso cref="T:XSharp.Severity"/>
    VIRTUAL PROPERTY Severity AS DWORD AUTO   := ES_ERROR
    /// A string that describes the error condition.
    VIRTUAL PROPERTY Description		AS STRING AUTO := ""
    /// A string representing the argument supplied to an operator or function when an argument error occurs.
    VIRTUAL PROPERTY Arg				AS STRING AUTO := ""
    PRIVATE  _ArgType			AS DWORD
    /// A numeric value representing the data type of the argument that raised the error.
    VIRTUAL PROPERTY ArgType			AS DWORD
      GET
        RETURN _ArgType
      END GET
      SET
        _ArgType := VALUE
        _ArgTypeType := UsualTypeToType(VALUE)
      END SET
    END PROPERTY
    PRIVATE  _ArgTypeType 		:= NULL AS System.Type
    /// The system type representing the data type of the argument that raised the error.
    VIRTUAL PROPERTY ArgTypeType		AS System.Type
      GET
        RETURN _ArgTypeType
      END GET
      SET
        _ArgTypeType := VALUE
        _ArgType 	 := TypeToUsualType(VALUE)
      END SET
    END PROPERTY

    PRIVATE _ArgTypeReq		AS DWORD
    /// A numeric value representing the expected type of the argument that raised the error.
    VIRTUAL PROPERTY ArgTypeReq			AS DWORD
      GET
        RETURN _ArgTypeReq
      END GET
      SET
        _ArgTypeReq := VALUE
        _ArgTypeReqType := UsualTypeToType(VALUE)
      END SET
    END PROPERTY
    PRIVATE  _ArgTypeReqType := NULL	AS System.Type
    /// The system type representing the expected type of the argument that raised the error.
    VIRTUAL PROPERTY ArgTypeReqType		AS System.Type
      GET
        RETURN _ArgTypeReqType
      END GET
      SET
        _ArgTypeReqType := VALUE
        _ArgTypeReq 	 := TypeToUsualType(VALUE)
      END SET
    END PROPERTY
    /// A numeric value representing the type of the new result that the error handler substitutes for the operation that produced the error condition.
    VIRTUAL PROPERTY SubstituteType     AS DWORD AUTO
    /// A numeric value representing the number of the argument supplied to an operator or function when an argument error occurs.
    VIRTUAL PROPERTY ArgNum				AS DWORD AUTO
    /// An object representing the SELF of the method in which the error occurred.
    VIRTUAL PROPERTY MethodSelf			AS OBJECT AUTO
    /// A symbol representing the calling function of the function in which the error occurred.
    VIRTUAL PROPERTY CallFuncSym		AS STRING AUTO
    /// An array of the arguments supplied to an operator or function when an argument error occurs.
    VIRTUAL PROPERTY Args				AS OBJECT[] AUTO
    /// An integer numeric value representing the number of times the failed operation has been attempted.
    VIRTUAL PROPERTY Tries				AS INT AUTO := 0
    /// A logical value indicating whether the subsystem can perform default error recovery for the error condition.
    VIRTUAL PROPERTY CanDefault         AS LOGIC AUTO
    /// A logical value indicating whether the subsystem can retry the operation that caused the error condition.
    VIRTUAL PROPERTY CanRetry           AS LOGIC AUTO
    /// A logical value indicating whether a new result can be substituted for the operation that produced the error condition.
    VIRTUAL PROPERTY CanSubstitute      AS LOGIC AUTO
    /// A string that describes the operation being attempted when the error occurred.
    VIRTUAL PROPERTY Operation          AS STRING AUTO := ""
    /// A value of 0 indicates that the error condition was not caused by an error from the operating system.  When Error:OsCode is set to a value other
    VIRTUAL PROPERTY OSCode				AS DWORD AUTO := 0
    /// Descripion of the OSCode
    VIRTUAL PROPERTY OSCodeText			AS STRING GET IIF(OSCode == 0, "", DosErrString(OSCode))
    /// A numeric value representing the file handle supplied to a function when an file error occurs.
    VIRTUAL PROPERTY FileHandle         AS DWORD AUTO
    /// A numeric value representing a boundary condition for an operation (such as string overflow or array bound error).
    VIRTUAL PROPERTY MaxSize			AS DWORD AUTO
    /// A pointer to the function in which the error occurred.
    /// <remarks>Note This property is for compatibility only. It is not being used in the X# runtime.</remarks>
    VIRTUAL PROPERTY FuncPtr            AS IntPtr AUTO := IntPtr.Zero

    /// A value of any data type unused by the Error system.  It is provided as a user-definable slot, allowing arbitrary information to be attached to an Error object and retrieved later
    VIRTUAL PROPERTY Cargo              AS OBJECT AUTO

	PRIVATE _StackTrace AS STRING
    VIRTUAL PROPERTY StackTrace         AS STRING
    	GET
    		IF String.IsNullOrEmpty(SELF:_StackTrace)
    			RETURN SUPER:StackTrace
    		END IF
    		RETURN SELF:_StackTrace
    	END GET
    	SET
    		SELF:_StackTrace := VALUE
    	END SET
    END PROPERTY

    PRIVATE METHOD setDefaultValues() AS VOID
        SELF:FuncSym := ProcName(2)
        SELF:Args    := <OBJECT>{}
        IF String.IsNullOrEmpty(SELF:StackTrace)
            SELF:_StackTrace  := System.Diagnostics.StackTrace{2,TRUE}:ToString()
        ENDIF
        RETURN

    /// Create an Error Object
    CONSTRUCTOR()
    SELF:setDefaultValues()
    RETURN

    /// Create an Error Object with the specified Description
    CONSTRUCTOR (msg AS STRING)
    SUPER(msg)
    SELF:setDefaultValues()
    SELF:Description := msg
    SELF:GenCode     := EG_EXCEPTION
    RETURN

    /// Create an Error Object with the Innner Exception
    CONSTRUCTOR (ex AS Exception)
    SUPER(ex.Message,ex)
    SELF:setDefaultValues()
    IF ex IS Error
        // Clone Error properties from Inner Exception
        LOCAL e := (Error) ex AS Error
        VAR props := typeof(error):GetProperties()
        FOREACH oProp AS PropertyInfo IN props
            IF oProp:CanWrite
                oProp:SetValue(SELF, oProp:GetValue(e))
            ENDIF
        NEXT
    ELSE
        SELF:Description := ex:Message
        SELF:GenCode     := EG_EXCEPTION
        SELF:_StackTrace := ex:StackTrace
    ENDIF
    IF String.IsNullOrEmpty(SELF:StackTrace)
        SELF:StackTrace  := System.Diagnostics.StackTrace{2,TRUE}:ToString()
    ENDIF

    /// Create an Error Object with the Innner Exception and other parameters
    CONSTRUCTOR (ex AS Exception, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[])
    SUPER(ex.Message,ex)
    SELF:setDefaultValues()
    SELF:GenCode     := EG_EXCEPTION
    SELF:FuncSym     := cFuncName
    SELF:Arg         := cArgName
    SELF:ArgNum      := iArgNum
    SELF:Args		 := aArgs

    /// Create an Error Object for a Gencode and Argument Name.
    CONSTRUCTOR (dwgencode AS DWORD, cArg AS STRING)
    SUPER(ErrString( dwGenCode ))
    SELF:setDefaultValues()
    SELF:Gencode := dwGenCode
    SELF:Arg	 := cArg

    /// Create an Error Object for a Gencode, Argument Name and Description.
    CONSTRUCTOR (dwgencode AS DWORD, cArg AS STRING, cDescription AS STRING)
    SUPER(cDescription)
    SELF:setDefaultValues()
    SELF:Gencode		:= dwGenCode
    SELF:Arg			:= cArg
    SELF:Description	:= cDescription

    /// Create an Error Object.
    CONSTRUCTOR (dwgencode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD)
    SUPER(ErrString( dwGenCode ))
    SELF:setDefaultValues()
    SELF:Gencode := dwgencode
    SELF:SubCode := dwSubcode
    SELF:FuncSym     := cFuncName
    SELF:Arg         := cArgName
    SELF:ArgNum      := iArgNum

    /// Create an Error Object.
    CONSTRUCTOR (dwgencode AS DWORD, dwSubCode := 0 AS DWORD)
    SELF:setDefaultValues()
    SELF:Gencode := dwgencode
    SELF:SubCode := dwSubcode

    PRIVATE METHOD LangString(e as VOErrors) AS STRING
        local cString := __CavoStr(e):Trim() as string
        if cString:Endswith(":")
            cString := cString:Substring(0, cString:Length-1):Trim()
        endif
        return cString+e" :\t"

    /// <inheritdoc />
    OVERRIDE METHOD ToString() AS STRING
      LOCAL sb AS StringBuilder
      LOCAL nGenCode AS GenCode
      nGenCode := (GenCode) SELF:GenCode
      sb := StringBuilder{}

      sb:AppendLine( LangString(VOErrors.ERROR_DESCRIPTION) + SELF:Description)
      sb:AppendLine( LangString(VOErrors.ERROR_SUBSYSTEM) + SELF:SubSystem )
      sb:AppendLine( LangString(VOErrors.ERROR_GENCODE) + nGenCode:ToString()  +" " +SELF:GenCodeText  )
      IF SELF:SubCode != 0
        sb:AppendLine( LangString(VOErrors.ERROR_SUBCODE) + SELF:SubCode:ToString() +" "+SELF:SubCodeText)
      ENDIF
      IF SELF:OsCode != 0
        sb:AppendLine( LangString(VOErrors.ERROR_OSCODE)  + SELF:OsCode:ToString() +" " +SELF:OsCodeText )
      ENDIF
      IF !String.IsNullOrEmpty(SELF:FuncSym)
        sb:AppendLine(LangString(VOErrors.ERROR_FUNCSYM) + SELF:FuncSym   )
      ENDIF
      LOCAL sev := (Severity) SELF:Severity AS Severity
      sb:AppendLine(LangString(VOErrors.ERROR_SEVERITY)     + sev:ToString() )
      sb:AppendLine(LangString(VOErrors.ERROR_CANDEFAULT)   + SELF:CanDefault:ToString())
      sb:AppendLine(LangString(VOErrors.ERROR_CANRETRY)     +SELF:CanRetry:ToString() )
      sb:AppendLine(LangString(VOErrors.ERROR_CANSUBSTITUTE) +SELF:CanSubstitute:ToString())
      IF ! String.IsNullOrEmpty(SELF:Operation)
        sb:AppendLine(LangString(VOErrors.ERROR_OPERATION) + SELF:Operation)
      ENDIF
      IF ! String.IsNullOrEmpty(SELF:FileName)
        sb:AppendLine(LangString(VOErrors.ERROR_FILENAME) + SELF:FileName )
      ENDIF
      IF SELF:Tries != 0
          sb:AppendLine(LangString(VOErrors.ERROR_TRIES) + SELF:Tries:ToString()    )
     ENDIF
      IF SELF:ArgType != 0
        sb:AppendLine(LangString(VOErrors.ERROR_ARGTYPE) 	+ TypeString(SELF:ArgType    ) )
      ENDIF
      IF SELF:ArgNum != 0
        sb:AppendLine(LangString(VOErrors.ERROR_ARGNUM) 	+ SELF:ArgNum:ToString()    )
      ENDIF
      IF ! String.IsNullOrEmpty(SELF:Arg)
        sb:AppendLine(LangString(VOErrors.ERROR_ARG)	+ SELF:Arg)
      ENDIF
      LOCAL cArgs AS STRING
      IF SELF:Args != NULL .AND. SELF:Args:Length > 0
            cArgs := "{"
            LOCAL lFirst := TRUE AS LOGIC
            FOREACH VAR oArg IN SELF:Args
                IF ! lFirst
                    cArgs += ","
                ENDIF
                IF oArg == NULL
                    cArgs += "(NULL)"
                ELSE
                    cArgs += oArg:ToString()
                ENDIF
                lFirst := FALSE
            NEXT
            cArgs += "}"
            sb:AppendLine(LangString(VOErrors.ERROR_ARGS)+ cArgs)
      ENDIF
      IF SELF:ArgTypeReqType != NULL
        sb:AppendLine(LangString(VOErrors.ERROR_ARGTYPE_REQ) + SELF:ArgTypeReqType:FullName)
      ENDIF
      IF ! String.IsNullOrEmpty(SELF:CallFuncSym)
        sb:AppendLine(LangString(VOErrors.ERROR_CALLEDFROM) + SELF:CallFuncSym  )
      ENDIF
      sb:AppendLine(LangString(VOErrors.ERROR_STACK))
      sb:AppendLine(SELF:StackTrace  )
      RETURN sb:ToString()

    /// Throw the error.
    VIRTUAL METHOD Throw AS VOID STRICT
        THROW SELF

    #region STATIC methods TO construct an error
    /// <exclude/>
    STATIC METHOD ArgumentError(cFuncName AS STRING, name AS STRING, description AS STRING) AS Error
    RETURN ArgumentError(cFuncName, name, description, 0)

    /// <exclude/>
    STATIC METHOD ArgumentError(cFuncName AS STRING, name AS STRING, iArgnum  AS DWORD, aArgs PARAMS OBJECT[]) AS Error
    VAR err			:= Error{XSharp.Gencode.EG_ARG, name, ErrString( EG_ARG )}
    err:FuncSym     := cFuncName
    err:Description := err:Message
    err:Argnum		:= iArgNum
    err:args		:= aArgs
    RETURN err

    /// <exclude/>
    STATIC METHOD ArgumentError(cFuncName AS STRING, name AS STRING, description AS STRING, iArgnum AS DWORD) AS Error
    VAR err			:= Error{XSharp.Gencode.EG_ARG, name, description}
    err:FuncSym     := cFuncName
    err:Description := err:Message
    err:Argnum		:= iArgNum
    RETURN err

    /// <exclude/>
    STATIC METHOD WrapRawException( ex AS Exception ) AS Error
    LOCAL e AS Error
    if ex != null
        e			  := Error{ ex }
        e:Description := ErrString( EG_EXCEPTION ) + ":" + ex:Message
    else
        e			  := Error{  }
        e:Description := ErrString( EG_SEQUENCE ) + ":" + ex:Message
    endif
    RETURN e

    /// <exclude/>
    STATIC METHOD VOError( dwGenCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs AS OBJECT[] ) AS Error
        LOCAL e AS Error
        e:= Error{dwGencode,cArgName}
        e:FuncSym := cFuncName
        e:ArgNum := iArgNum
        e:Args := aArgs
        RETURN e

    /// <exclude/>
    STATIC METHOD VOError( ex AS Exception, dwGenCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs AS OBJECT[]  ) AS Error
    LOCAL e AS Error
    e			  := Error{ ex, cFuncName, cArgName, iArgNum, aArgs }
    e:GenCode     := dwGenCode
    e:Description := ErrString( dwGenCode )
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING ) AS Error
    LOCAL e AS Error
    e := Error{dwGenCode, dwSubCode}
    e:SubSystem   := "DBCMD"
    e:FuncSym     := cFuncName
    e:Description := ErrString( dwGenCode )
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e := Error{dwGenCode, dwSubCode}
    e:SubSystem   := "DBCMD"
    e:FuncSym     := cFuncName
    e:Description := ErrString( dwGenCode )
    e:Args        := aArgs
    IF aArgs:Length > 0
        e:Arg := aArgs[0]:ToString()
    ENDIF
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e			  := Error{dwGenCode, dwSubCode}
    e:SubSystem   := "DBCMD"
    e:Description := __CavoStr( dwSubCode)
    e:Args        := aArgs
    IF aArgs:Length > 0
        e:Arg := aArgs[0]:ToString()
    ENDIF
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e := Error{dwGenCode, dwSubCode, cFuncName, cArgName, iArgNum}
    e:SubSystem   := "DBCMD"
    IF dwSubCode != 0
        e:Description := __CavoStr( dwSubCode)
    ELSE
        e:Description := ErrString(dwGenCode)
    ENDIF
    e:Args        := aArgs
    RETURN e

    /// <exclude/>
    STATIC METHOD DataTypeError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e			  := Error{ ArgumentException{} , cFuncName, cArgName, iArgNum, aArgs}
    e:GenCode     := EG_DATATYPE
    e:Description := __CavoStr( VOErrors.DATATYPEERROR )
    RETURN e

    /// <exclude/>
    STATIC METHOD ArgumentError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, cDescription AS STRING, aArgs PARAMS OBJECT[]) AS Error
    LOCAL e AS Error
    e				:= Error{ ArgumentException{} , cFuncName, cArgName, iArgNum, aArgs}
    e:GenCode     := EG_ARG
    e:Description := cDescription
    RETURN e

    /// <exclude/>
    STATIC METHOD NullArgumentError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD ) AS Error
    LOCAL e AS Error
    e := Error{ ArgumentNullException{} ,cFuncName, cArgName, iArgNum}
    e:GenCode     := EG_ARG
    e:Description := __CavoStr( VOErrors.ARGISNULL )
    RETURN e

    /// <exclude/>
    STATIC METHOD BoundError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e := Error{ ArgumentException{ErrString( EG_BOUND) } }
    e:Severity    := ES_ERROR
    e:GenCode     := EG_BOUND
    e:SubSystem   := "BASE"
    e:FuncSym     := cFuncName
    e:Arg         := cArgName
    e:ArgNum      := iArgNum
    e:Args        := aArgs
    RETURN e

    #endregion
    /// <exclude />
    STATIC METHOD TypeToUsualType(oType AS System.Type) AS DWORD
    SWITCH Type.GetTypeCode(oType)
    CASE TypeCode.Boolean
      RETURN __UsualType.logic
    CASE TypeCode.Byte
      RETURN __UsualType.byte
    CASE TypeCode.Char
      RETURN __UsualType.Char
    CASE TypeCode.DateTime
      RETURN __UsualType.DATETIME
    CASE TypeCode.DBNull
      RETURN __UsualType.object
    CASE TypeCode.Decimal
      RETURN __UsualType.DECIMAL
    CASE TypeCode.Double
      RETURN __UsualType.real8
    CASE TypeCode.Empty
      RETURN __UsualType.void
    CASE TypeCode.Int16
      RETURN __UsualType.shortint
    CASE TypeCode.Int32
      RETURN __UsualType.long
    CASE TypeCode.Int64
      RETURN __UsualType.int64
    CASE TypeCode.SByte
      RETURN __UsualType.byte
    CASE TypeCode.Single
      RETURN __UsualType.real4
    CASE TypeCode.UInt16
      RETURN __UsualType.word
    CASE TypeCode.UInt32
      RETURN __UsualType.dword
    CASE TypeCode.UInt64
      RETURN __UsualType.uint64
    OTHERWISE
        SWITCH oType:FullName:ToLower()
        CASE "xsharp.__array"
          RETURN __UsualType.array
        CASE "xsharp.__codeblock"
          RETURN __UsualType.codeblock
        CASE "xsharp.__date"
          RETURN __UsualType.date
        CASE "xsharp.__float"
          RETURN __UsualType.float
        CASE "xsharp.__psz"
          RETURN __UsualType.psz
        CASE "xsharp.__symbol"
          RETURN __UsualType.symbol
        CASE "xsharp.__usual"
          RETURN __UsualType.usual
        CASE "system.intptr"
          RETURN __UsualType.ptr

      END SWITCH
    END SWITCH
    RETURN __UsualType.void

    /// <exclude />
    STATIC METHOD UsualTypeTotype(dwType AS DWORD) AS System.Type
    LOCAL typename := NULL AS STRING
    SWITCH dwType
    CASE __UsualType.array
      typeName := "XSharp.__Array"
    CASE __UsualType.byte
      RETURN typeof(System.Byte)
    CASE __UsualType.char
      RETURN typeof(System.Char)
    CASE __UsualType.codeblock
      typeName := "XSharp.__CodeBlock"
    CASE __UsualType.date
      typeName := "XSharp.__Date"
    CASE __UsualType.dword
      RETURN typeof(System.UInt32)
    CASE __UsualType.int64
      RETURN typeof(System.Int64)
    CASE __UsualType.float
      typeName := "XSharp.__Float"
    CASE __UsualType.logic
      RETURN typeof(System.Boolean)
    CASE __UsualType.long
      RETURN typeof(System.Int32)
    CASE __UsualType.object
      RETURN typeof(System.Object)
    CASE __UsualType.psz
      typeName := "XSharp.__Psz"
    CASE __UsualType.ptr
      RETURN typeof(System.IntPtr)
    CASE __UsualType.real4
      RETURN typeof(System.Single)
    CASE __UsualType.real8
      RETURN typeof(System.Double)
    CASE __UsualType.shortint
      RETURN typeof(System.Int16)
    CASE __UsualType.string
      RETURN typeof(System.String)
    CASE __UsualType.symbol
      typeName := "XSharp.__Symbol"
    CASE __UsualType.uint64
      RETURN typeof(System.Int64)
    CASE __UsualType.usual
      typeName := "XSharp.__Usual"
    CASE __UsualType.void
      RETURN typeof(System.Void)
    CASE __UsualType.word
      RETURN typeof(System.Uint16)
    END SWITCH
    // lookup types in XSharp.VO
    IF typename != NULL
        FOREACH asm AS Assembly IN AppDomain.CurrentDomain:GetAssemblies()
          IF asm:Getname():Name:ToLower() == "xsharp.vo"
            VAR type := asm:GetType(typeName, FALSE, TRUE)
            IF type != NULL
              RETURN type
            ENDIF
          ENDIF
        NEXT
    ENDIF
    RETURN NULL
  END CLASS

  /// This enum is used to describe the type of USUAL values in the X# Runtime. It is based on the original USUAL type values in the VO runtime.
  ENUM __UsualType AS BYTE
        // These numbers must match with the types defined in the compiler
        // They also match with the USUAL types in VO (BaseType.h)
        /// The usual contains a NIL.
        MEMBER Void		:=0
        /// The usual contains a LONG value
        MEMBER Long		:=1
        /// The usual contains a DATE value
        MEMBER Date		:=2
        /// The usual contains a FLOAT value
        MEMBER Float	:=3
        /// This value is NEVER used for USUALs (this was also defined in VO but never used).
        MEMBER Fixed    := 4
        /// The usual contains an ARRAY value
        MEMBER Array	:=5
        /// The usual contains an OBJECT value
        MEMBER Object	:=6
        /// The usual contains an STRING value
        MEMBER String	:=7
        /// The usual contains an LOGIC value
        MEMBER Logic	:=8
        /// The usual contains an CODEBLOCK value
        MEMBER Codeblock:=9
        /// The usual contains an SYMBOL value
        MEMBER Symbol	:=10
        // see below for missing values
        // The follow numbers are defined but never stored inside a USUAL in VO and Vulcan
        /// This value is in the enum for completeness but never used inside a usual. Byte values are stored as LONG.
        MEMBER Byte		:=11
        /// This value is in the enum for completeness but never used inside a usual. Short values are stored as LONG.
        MEMBER ShortInt	:=12
        /// This value is in the enum for completeness but never used inside a usual. Word values are stored as LONG.
        MEMBER Word		:=13
        /// This value is in the enum for completeness but never used inside a usual. DWord values are stored as LONG or FLOAT.
        MEMBER DWord	:=14
        /// This value is in the enum for completeness but never used inside a usual. Real4 values are stored as FLOAT
        MEMBER Real4	:=15
        /// This value is in the enum for completeness but never used inside a usual. Real8 values are stored as FLOAT.
        MEMBER Real8	:=16
        /// This value is in the enum for completeness but never used inside a usual.
        /// The usual contains an PSZ value
        MEMBER Psz		:=17
        /// The usual contains an PTR value
        MEMBER Ptr		:=18
        /// <exclude/>
        MEMBER Usual	:=19	// USUAL by Ref, not implemented in Vulcan

        // 20 and 21 not used

        /// The usual contains an INT64 value (new in Vulcan and X#).
        MEMBER Int64		:=22
        /// The usual contains an UINT64 value (new in Vulcan and X#).
        MEMBER Uint64     :=23
        /// This value is in the enum for completeness but never used inside a usual. Char values are stored as LONG
        MEMBER Char		:=24    // not stored in a usual
        /// This value is in the enum for completeness but never used inside a usual. Dynamic values are stored as OBJECT
        MEMBER Dynamic    :=25
        /// The usual contains an DateTime value (new in X#).
        MEMBER DateTime	:=26
        /// The usual contains an Decimal value (new in X#).
        MEMBER Decimal	:=27
        /// The usual contains an Memo value. This value is there for compatibility with VO but never used.
        MEMBER Memo		:=32	// Used in RDD system in VO
        /// Invalid Usual Type.
        MEMBER Invalid    :=99
    END ENUM

END NAMESPACE

/// <exclude/>
FUNCTION ErrorBuild(pErrInfo AS exception) AS XSharp.ERROR
	RETURN  XSharp.Error{pErrInfo}

USING System
USING System.Collections.Generic
USING System.Text
USING System.Reflection
BEGIN NAMESPACE XSharp
  /// XSharp Runtime base Error class
  CLASS Error INHERIT Exception
    /// A string representing the name of the subsystem generating the error.
    VIRTUAL PROPERTY SubSystem AS STRING AUTO  := "BASE"
    /// An integer numeric value representing a Visual Objects generic error code.
    /// <Seealso cref="T:XSharp.Gencode"/>
    VIRTUAL PROPERTY Gencode AS DWORD AUTO    := EG_UNKNOWN
    /// An string containing the description of the Gencode.
    VIRTUAL PROPERTY GenCodeText AS STRING GET IIF (Gencode != 0, ErrString(Gencode), "Unknown GenCode")
    /// An integer numeric value representing a subsystem-specific error code.
    VIRTUAL PROPERTY SubCode AS DWORD AUTO    := 0
    /// An string containing the description of the SubCode.
    VIRTUAL PROPERTY SubCodeText AS STRING GET IIF (SubCode != 0, __CavoStr(SubCode), "Unknown SubCode")
    /// A string representing the name of the function or method in which the error occurred.
    VIRTUAL PROPERTY FuncSym AS STRING AUTO   := ""
    /// A string representing the name used to open the file associated with the error condition.
    VIRTUAL PROPERTY FileName AS STRING AUTO  := ""
    /// A constant indicating the severity of the error condition.
    /// <Seealso cref="T:XSharp.Severity"/>
    VIRTUAL PROPERTY Severity AS DWORD AUTO   := ES_ERROR
    /// A string that describes the error condition.
    VIRTUAL PROPERTY Description		AS STRING AUTO := ""
    /// A string representing the argument supplied to an operator or function when an argument error occurs.
    VIRTUAL PROPERTY Arg				AS STRING AUTO := ""
    PRIVATE  _ArgType			AS DWORD
    /// A numeric value representing the data type of the argument that raised the error.
    VIRTUAL PROPERTY ArgType			AS DWORD
      GET
        RETURN _ArgType
      END GET
      SET
        _ArgType := VALUE
        _ArgTypeType := UsualTypeToType(VALUE)
      END SET
    END PROPERTY
    PRIVATE  _ArgTypeType 		:= NULL AS System.Type
    /// The system type representing the data type of the argument that raised the error.
    VIRTUAL PROPERTY ArgTypeType		AS System.Type
      GET
        RETURN _ArgTypeType
      END GET
      SET
        _ArgTypeType := VALUE
        _ArgType 	 := TypeToUsualType(VALUE)
      END SET
    END PROPERTY

    PRIVATE _ArgTypeReq		AS DWORD
    /// A numeric value representing the expected type of the argument that raised the error.
    VIRTUAL PROPERTY ArgTypeReq			AS DWORD
      GET
        RETURN _ArgTypeReq
      END GET
      SET
        _ArgTypeReq := VALUE
        _ArgTypeReqType := UsualTypeToType(VALUE)
      END SET
    END PROPERTY
    PRIVATE  _ArgTypeReqType := NULL	AS System.Type
    /// The system type representing the expected type of the argument that raised the error.
    VIRTUAL PROPERTY ArgTypeReqType		AS System.Type
      GET
        RETURN _ArgTypeReqType
      END GET
      SET
        _ArgTypeReqType := VALUE
        _ArgTypeReq 	 := TypeToUsualType(VALUE)
      END SET
    END PROPERTY
    /// A numeric value representing the type of the new result that the error handler substitutes for the operation that produced the error condition.
    VIRTUAL PROPERTY SubstituteType     AS DWORD AUTO
    /// A numeric value representing the number of the argument supplied to an operator or function when an argument error occurs.
    VIRTUAL PROPERTY ArgNum				AS DWORD AUTO
    /// An object representing the SELF of the method in which the error occurred.
    VIRTUAL PROPERTY MethodSelf			AS OBJECT AUTO
    /// A symbol representing the calling function of the function in which the error occurred.
    VIRTUAL PROPERTY CallFuncSym		AS STRING AUTO
    /// An array of the arguments supplied to an operator or function when an argument error occurs.
    VIRTUAL PROPERTY Args				AS OBJECT[] AUTO
    /// An integer numeric value representing the number of times the failed operation has been attempted.
    VIRTUAL PROPERTY Tries				AS INT AUTO := 0
    /// A logical value indicating whether the subsystem can perform default error recovery for the error condition.
    VIRTUAL PROPERTY CanDefault         AS LOGIC AUTO
    /// A logical value indicating whether the subsystem can retry the operation that caused the error condition.
    VIRTUAL PROPERTY CanRetry           AS LOGIC AUTO
    /// A logical value indicating whether a new result can be substituted for the operation that produced the error condition.
    VIRTUAL PROPERTY CanSubstitute      AS LOGIC AUTO
    /// A string that describes the operation being attempted when the error occurred.
    VIRTUAL PROPERTY Operation          AS STRING AUTO := ""
    /// A value of 0 indicates that the error condition was not caused by an error from the operating system.  When Error:OsCode is set to a value other
    VIRTUAL PROPERTY OSCode				AS DWORD AUTO := 0
    /// Descripion of the OSCode
    VIRTUAL PROPERTY OSCodeText			AS STRING GET IIF(OSCode == 0, "", DosErrString(OSCode))
    /// A numeric value representing the file handle supplied to a function when an file error occurs.
    VIRTUAL PROPERTY FileHandle         AS DWORD AUTO
    /// A numeric value representing a boundary condition for an operation (such as string overflow or array bound error).
    VIRTUAL PROPERTY MaxSize			AS DWORD AUTO
    /// A pointer to the function in which the error occurred.
    /// <remarks>Note This property is for compatibility only. It is not being used in the X# runtime.</remarks>
    VIRTUAL PROPERTY FuncPtr            AS IntPtr AUTO := IntPtr.Zero

    /// A value of any data type unused by the Error system.  It is provided as a user-definable slot, allowing arbitrary information to be attached to an Error object and retrieved later
    VIRTUAL PROPERTY Cargo              AS OBJECT AUTO

	PRIVATE _StackTrace AS STRING
    VIRTUAL PROPERTY StackTrace         AS STRING
    	GET
    		IF String.IsNullOrEmpty(SELF:_StackTrace)
    			RETURN SUPER:StackTrace
    		END IF
    		RETURN SELF:_StackTrace
    	END GET
    	SET
    		SELF:_StackTrace := VALUE
    	END SET
    END PROPERTY

    PRIVATE METHOD setDefaultValues() AS VOID
        SELF:FuncSym := ProcName(2)
        SELF:Args    := <OBJECT>{}
        IF String.IsNullOrEmpty(SELF:StackTrace)
            SELF:_StackTrace  := System.Diagnostics.StackTrace{2,TRUE}:ToString()
        ENDIF
        RETURN

    /// Create an Error Object
    CONSTRUCTOR()
    SELF:setDefaultValues()
    RETURN

    /// Create an Error Object with the specified Description
    CONSTRUCTOR (msg AS STRING)
    SUPER(msg)
    SELF:setDefaultValues()
    SELF:Description := msg
    SELF:GenCode     := EG_EXCEPTION
    RETURN

    /// Create an Error Object with the Innner Exception
    CONSTRUCTOR (ex AS Exception)
    SUPER(ex.Message,ex)
    SELF:setDefaultValues()
    IF ex IS Error
        // Clone Error properties from Inner Exception
        LOCAL e := (Error) ex AS Error
        VAR props := typeof(error):GetProperties()
        FOREACH oProp AS PropertyInfo IN props
            IF oProp:CanWrite
                oProp:SetValue(SELF, oProp:GetValue(e))
            ENDIF
        NEXT
    ELSE
        SELF:Description := ex:Message
        SELF:GenCode     := EG_EXCEPTION
        SELF:_StackTrace := ex:StackTrace
    ENDIF
    IF String.IsNullOrEmpty(SELF:StackTrace)
        SELF:StackTrace  := System.Diagnostics.StackTrace{2,TRUE}:ToString()
    ENDIF

    /// Create an Error Object with the Innner Exception and other parameters
    CONSTRUCTOR (ex AS Exception, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[])
    SUPER(ex.Message,ex)
    SELF:setDefaultValues()
    SELF:GenCode     := EG_EXCEPTION
    SELF:FuncSym     := cFuncName
    SELF:Arg         := cArgName
    SELF:ArgNum      := iArgNum
    SELF:Args		 := aArgs

    /// Create an Error Object for a Gencode and Argument Name.
    CONSTRUCTOR (dwgencode AS DWORD, cArg AS STRING)
    SUPER(ErrString( dwGenCode ))
    SELF:setDefaultValues()
    SELF:Gencode := dwGenCode
    SELF:Arg	 := cArg

    /// Create an Error Object for a Gencode, Argument Name and Description.
    CONSTRUCTOR (dwgencode AS DWORD, cArg AS STRING, cDescription AS STRING)
    SUPER(cDescription)
    SELF:setDefaultValues()
    SELF:Gencode		:= dwGenCode
    SELF:Arg			:= cArg
    SELF:Description	:= cDescription

    /// Create an Error Object.
    CONSTRUCTOR (dwgencode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD)
    SUPER(ErrString( dwGenCode ))
    SELF:setDefaultValues()
    SELF:Gencode := dwgencode
    SELF:SubCode := dwSubcode
    SELF:FuncSym     := cFuncName
    SELF:Arg         := cArgName
    SELF:ArgNum      := iArgNum

    /// Create an Error Object.
    CONSTRUCTOR (dwgencode AS DWORD, dwSubCode := 0 AS DWORD)
    SELF:setDefaultValues()
    SELF:Gencode := dwgencode
    SELF:SubCode := dwSubcode

    PRIVATE METHOD LangString(e as VOErrors) AS STRING
        local cString := __CavoStr(e):Trim() as string
        if cString:Endswith(":")
            cString := cString:Substring(0, cString:Length-1):Trim()
        endif
        return cString+e" :\t"

    /// <inheritdoc />
    OVERRIDE METHOD ToString() AS STRING
      LOCAL sb AS StringBuilder
      LOCAL nGenCode AS GenCode
      nGenCode := (GenCode) SELF:GenCode
      sb := StringBuilder{}

      sb:AppendLine( LangString(VOErrors.ERROR_DESCRIPTION) + SELF:Description)
      sb:AppendLine( LangString(VOErrors.ERROR_SUBSYSTEM) + SELF:SubSystem )
      sb:AppendLine( LangString(VOErrors.ERROR_GENCODE) + nGenCode:ToString()  +" " +SELF:GenCodeText  )
      IF SELF:SubCode != 0
        sb:AppendLine( LangString(VOErrors.ERROR_SUBCODE) + SELF:SubCode:ToString() +" "+SELF:SubCodeText)
      ENDIF
      IF SELF:OsCode != 0
        sb:AppendLine( LangString(VOErrors.ERROR_OSCODE)  + SELF:OsCode:ToString() +" " +SELF:OsCodeText )
      ENDIF
      IF !String.IsNullOrEmpty(SELF:FuncSym)
        sb:AppendLine(LangString(VOErrors.ERROR_FUNCSYM) + SELF:FuncSym   )
      ENDIF
      LOCAL sev := (Severity) SELF:Severity AS Severity
      sb:AppendLine(LangString(VOErrors.ERROR_SEVERITY)     + sev:ToString() )
      sb:AppendLine(LangString(VOErrors.ERROR_CANDEFAULT)   + SELF:CanDefault:ToString())
      sb:AppendLine(LangString(VOErrors.ERROR_CANRETRY)     +SELF:CanRetry:ToString() )
      sb:AppendLine(LangString(VOErrors.ERROR_CANSUBSTITUTE) +SELF:CanSubstitute:ToString())
      IF ! String.IsNullOrEmpty(SELF:Operation)
        sb:AppendLine(LangString(VOErrors.ERROR_OPERATION) + SELF:Operation)
      ENDIF
      IF ! String.IsNullOrEmpty(SELF:FileName)
        sb:AppendLine(LangString(VOErrors.ERROR_FILENAME) + SELF:FileName )
      ENDIF
      IF SELF:Tries != 0
          sb:AppendLine(LangString(VOErrors.ERROR_TRIES) + SELF:Tries:ToString()    )
     ENDIF
      IF SELF:ArgType != 0
        sb:AppendLine(LangString(VOErrors.ERROR_ARGTYPE) 	+ TypeString(SELF:ArgType    ) )
      ENDIF
      IF SELF:ArgNum != 0
        sb:AppendLine(LangString(VOErrors.ERROR_ARGNUM) 	+ SELF:ArgNum:ToString()    )
      ENDIF
      IF ! String.IsNullOrEmpty(SELF:Arg)
        sb:AppendLine(LangString(VOErrors.ERROR_ARG)	+ SELF:Arg)
      ENDIF
      LOCAL cArgs AS STRING
      IF SELF:Args != NULL .AND. SELF:Args:Length > 0
            cArgs := "{"
            LOCAL lFirst := TRUE AS LOGIC
            FOREACH VAR oArg IN SELF:Args
                IF ! lFirst
                    cArgs += ","
                ENDIF
                IF oArg == NULL
                    cArgs += "(NULL)"
                ELSE
                    cArgs += oArg:ToString()
                ENDIF
                lFirst := FALSE
            NEXT
            cArgs += "}"
            sb:AppendLine(LangString(VOErrors.ERROR_ARGS)+ cArgs)
      ENDIF
      IF SELF:ArgTypeReqType != NULL
        sb:AppendLine(LangString(VOErrors.ERROR_ARGTYPE_REQ) + SELF:ArgTypeReqType:FullName)
      ENDIF
      IF ! String.IsNullOrEmpty(SELF:CallFuncSym)
        sb:AppendLine(LangString(VOErrors.ERROR_CALLEDFROM) + SELF:CallFuncSym  )
      ENDIF
      sb:AppendLine(LangString(VOErrors.ERROR_STACK))
      sb:AppendLine(SELF:StackTrace  )
      RETURN sb:ToString()

    /// Throw the error.
    VIRTUAL METHOD Throw AS VOID STRICT
        THROW SELF

    #region STATIC methods TO construct an error
    /// <exclude/>
    STATIC METHOD ArgumentError(cFuncName AS STRING, name AS STRING, description AS STRING) AS Error
    RETURN ArgumentError(cFuncName, name, description, 0)

    /// <exclude/>
    STATIC METHOD ArgumentError(cFuncName AS STRING, name AS STRING, iArgnum  AS DWORD, aArgs PARAMS OBJECT[]) AS Error
    VAR err			:= Error{XSharp.Gencode.EG_ARG, name, ErrString( EG_ARG )}
    err:FuncSym     := cFuncName
    err:Description := err:Message
    err:Argnum		:= iArgNum
    err:args		:= aArgs
    RETURN err

    /// <exclude/>
    STATIC METHOD ArgumentError(cFuncName AS STRING, name AS STRING, description AS STRING, iArgnum AS DWORD) AS Error
    VAR err			:= Error{XSharp.Gencode.EG_ARG, name, description}
    err:FuncSym     := cFuncName
    err:Description := err:Message
    err:Argnum		:= iArgNum
    RETURN err

    /// <exclude/>
    STATIC METHOD WrapRawException( ex AS Exception ) AS Error
    LOCAL e AS Error
    e			  := Error{ ex }
    e:Description := ErrString( EG_EXCEPTION ) + ":" + ex:Message
    RETURN e

    /// <exclude/>
    STATIC METHOD VOError( dwGenCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs AS OBJECT[] ) AS Error
        LOCAL e AS Error
        e:= Error{dwGencode,cArgName}
        e:FuncSym := cFuncName
        e:ArgNum := iArgNum
        e:Args := aArgs
        RETURN e

    /// <exclude/>
    STATIC METHOD VOError( ex AS Exception, dwGenCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs AS OBJECT[]  ) AS Error
    LOCAL e AS Error
    e			  := Error{ ex, cFuncName, cArgName, iArgNum, aArgs }
    e:GenCode     := dwGenCode
    e:Description := ErrString( dwGenCode )
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING ) AS Error
    LOCAL e AS Error
    e := Error{dwGenCode, dwSubCode}
    e:SubSystem   := "DBCMD"
    e:FuncSym     := cFuncName
    e:Description := ErrString( dwGenCode )
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e := Error{dwGenCode, dwSubCode}
    e:SubSystem   := "DBCMD"
    e:FuncSym     := cFuncName
    e:Description := ErrString( dwGenCode )
    e:Args        := aArgs
    IF aArgs:Length > 0
        e:Arg := aArgs[0]:ToString()
    ENDIF
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e			  := Error{dwGenCode, dwSubCode}
    e:SubSystem   := "DBCMD"
    e:Description := __CavoStr( dwSubCode)
    e:Args        := aArgs
    IF aArgs:Length > 0
        e:Arg := aArgs[0]:ToString()
    ENDIF
    RETURN e

    /// <exclude/>
    STATIC METHOD VODBError( dwGenCode AS DWORD, dwSubCode AS DWORD, cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e := Error{dwGenCode, dwSubCode, cFuncName, cArgName, iArgNum}
    e:SubSystem   := "DBCMD"
    IF dwSubCode != 0
        e:Description := __CavoStr( dwSubCode)
    ELSE
        e:Description := ErrString(dwGenCode)
    ENDIF
    e:Args        := aArgs
    RETURN e

    /// <exclude/>
    STATIC METHOD DataTypeError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e			  := Error{ ArgumentException{} , cFuncName, cArgName, iArgNum, aArgs}
    e:GenCode     := EG_DATATYPE
    e:Description := __CavoStr( VOErrors.DATATYPEERROR )
    RETURN e

    /// <exclude/>
    STATIC METHOD ArgumentError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, cDescription AS STRING, aArgs PARAMS OBJECT[]) AS Error
    LOCAL e AS Error
    e				:= Error{ ArgumentException{} , cFuncName, cArgName, iArgNum, aArgs}
    e:GenCode     := EG_ARG
    e:Description := cDescription
    RETURN e

    /// <exclude/>
    STATIC METHOD NullArgumentError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD ) AS Error
    LOCAL e AS Error
    e := Error{ ArgumentNullException{} ,cFuncName, cArgName, iArgNum}
    e:GenCode     := EG_ARG
    e:Description := __CavoStr( VOErrors.ARGISNULL )
    RETURN e

    /// <exclude/>
    STATIC METHOD BoundError( cFuncName AS STRING, cArgName AS STRING, iArgNum AS DWORD, aArgs PARAMS OBJECT[] ) AS Error
    LOCAL e AS Error
    e := Error{ ArgumentException{ErrString( EG_BOUND) } }
    e:Severity    := ES_ERROR
    e:GenCode     := EG_BOUND
    e:SubSystem   := "BASE"
    e:FuncSym     := cFuncName
    e:Arg         := cArgName
    e:ArgNum      := iArgNum
    e:Args        := aArgs
    RETURN e

    #endregion
    /// <exclude />
    STATIC METHOD TypeToUsualType(oType AS System.Type) AS DWORD
    SWITCH Type.GetTypeCode(oType)
    CASE TypeCode.Boolean
      RETURN __UsualType.logic
    CASE TypeCode.Byte
      RETURN __UsualType.byte
    CASE TypeCode.Char
      RETURN __UsualType.Char
    CASE TypeCode.DateTime
      RETURN __UsualType.DATETIME
    CASE TypeCode.DBNull
      RETURN __UsualType.object
    CASE TypeCode.Decimal
      RETURN __UsualType.DECIMAL
    CASE TypeCode.Double
      RETURN __UsualType.real8
    CASE TypeCode.Empty
      RETURN __UsualType.void
    CASE TypeCode.Int16
      RETURN __UsualType.shortint
    CASE TypeCode.Int32
      RETURN __UsualType.long
    CASE TypeCode.Int64
      RETURN __UsualType.int64
    CASE TypeCode.SByte
      RETURN __UsualType.byte
    CASE TypeCode.Single
      RETURN __UsualType.real4
    CASE TypeCode.UInt16
      RETURN __UsualType.word
    CASE TypeCode.UInt32
      RETURN __UsualType.dword
    CASE TypeCode.UInt64
      RETURN __UsualType.uint64
    OTHERWISE
        SWITCH oType:FullName:ToLower()
        CASE "xsharp.__array"
          RETURN __UsualType.array
        CASE "xsharp.__codeblock"
          RETURN __UsualType.codeblock
        CASE "xsharp.__date"
          RETURN __UsualType.date
        CASE "xsharp.__float"
          RETURN __UsualType.float
        CASE "xsharp.__psz"
          RETURN __UsualType.psz
        CASE "xsharp.__symbol"
          RETURN __UsualType.symbol
        CASE "xsharp.__usual"
          RETURN __UsualType.usual
        CASE "system.intptr"
          RETURN __UsualType.ptr

      END SWITCH
    END SWITCH
    RETURN __UsualType.void

    /// <exclude />
    STATIC METHOD UsualTypeTotype(dwType AS DWORD) AS System.Type
    LOCAL typename := NULL AS STRING
    SWITCH dwType
    CASE __UsualType.array
      typeName := "XSharp.__Array"
    CASE __UsualType.byte
      RETURN typeof(System.Byte)
    CASE __UsualType.char
      RETURN typeof(System.Char)
    CASE __UsualType.codeblock
      typeName := "XSharp.__CodeBlock"
    CASE __UsualType.date
      typeName := "XSharp.__Date"
    CASE __UsualType.dword
      RETURN typeof(System.UInt32)
    CASE __UsualType.int64
      RETURN typeof(System.Int64)
    CASE __UsualType.float
      typeName := "XSharp.__Float"
    CASE __UsualType.logic
      RETURN typeof(System.Boolean)
    CASE __UsualType.long
      RETURN typeof(System.Int32)
    CASE __UsualType.object
      RETURN typeof(System.Object)
    CASE __UsualType.psz
      typeName := "XSharp.__Psz"
    CASE __UsualType.ptr
      RETURN typeof(System.IntPtr)
    CASE __UsualType.real4
      RETURN typeof(System.Single)
    CASE __UsualType.real8
      RETURN typeof(System.Double)
    CASE __UsualType.shortint
      RETURN typeof(System.Int16)
    CASE __UsualType.string
      RETURN typeof(System.String)
    CASE __UsualType.symbol
      typeName := "XSharp.__Symbol"
    CASE __UsualType.uint64
      RETURN typeof(System.Int64)
    CASE __UsualType.usual
      typeName := "XSharp.__Usual"
    CASE __UsualType.void
      RETURN typeof(System.Void)
    CASE __UsualType.word
      RETURN typeof(System.Uint16)
    END SWITCH
    // lookup types in XSharp.VO
    IF typename != NULL
        FOREACH asm AS Assembly IN AppDomain.CurrentDomain:GetAssemblies()
          IF asm:Getname():Name:ToLower() == "xsharp.vo"
            VAR type := asm:GetType(typeName, FALSE, TRUE)
            IF type != NULL
              RETURN type
            ENDIF
          ENDIF
        NEXT
    ENDIF
    RETURN NULL
  END CLASS

  /// This enum is used to describe the type of USUAL values in the X# Runtime. It is based on the original USUAL type values in the VO runtime.
  ENUM __UsualType AS BYTE
        // These numbers must match with the types defined in the compiler
        // They also match with the USUAL types in VO (BaseType.h)
        /// The usual contains a NIL.
        MEMBER Void		:=0
        /// The usual contains a LONG value
        MEMBER Long		:=1
        /// The usual contains a DATE value
        MEMBER Date		:=2
        /// The usual contains a FLOAT value
        MEMBER Float	:=3
        /// This value is NEVER used for USUALs (this was also defined in VO but never used).
        MEMBER Fixed    := 4
        /// The usual contains an ARRAY value
        MEMBER Array	:=5
        /// The usual contains an OBJECT value
        MEMBER Object	:=6
        /// The usual contains an STRING value
        MEMBER String	:=7
        /// The usual contains an LOGIC value
        MEMBER Logic	:=8
        /// The usual contains an CODEBLOCK value
        MEMBER Codeblock:=9
        /// The usual contains an SYMBOL value
        MEMBER Symbol	:=10
        // see below for missing values
        // The follow numbers are defined but never stored inside a USUAL in VO and Vulcan
        /// This value is in the enum for completeness but never used inside a usual. Byte values are stored as LONG.
        MEMBER Byte		:=11
        /// This value is in the enum for completeness but never used inside a usual. Short values are stored as LONG.
        MEMBER ShortInt	:=12
        /// This value is in the enum for completeness but never used inside a usual. Word values are stored as LONG.
        MEMBER Word		:=13
        /// This value is in the enum for completeness but never used inside a usual. DWord values are stored as LONG or FLOAT.
        MEMBER DWord	:=14
        /// This value is in the enum for completeness but never used inside a usual. Real4 values are stored as FLOAT
        MEMBER Real4	:=15
        /// This value is in the enum for completeness but never used inside a usual. Real8 values are stored as FLOAT.
        MEMBER Real8	:=16
        /// This value is in the enum for completeness but never used inside a usual.
        /// The usual contains an PSZ value
        MEMBER Psz		:=17
        /// The usual contains an PTR value
        MEMBER Ptr		:=18
        /// <exclude/>
        MEMBER Usual	:=19	// USUAL by Ref, not implemented in Vulcan

        // 20 and 21 not used

        /// The usual contains an INT64 value (new in Vulcan and X#).
        MEMBER Int64		:=22
        /// The usual contains an UINT64 value (new in Vulcan and X#).
        MEMBER Uint64     :=23
        /// This value is in the enum for completeness but never used inside a usual. Char values are stored as LONG
        MEMBER Char		:=24    // not stored in a usual
        /// This value is in the enum for completeness but never used inside a usual. Dynamic values are stored as OBJECT
        MEMBER Dynamic    :=25
        /// The usual contains an DateTime value (new in X#).
        MEMBER DateTime	:=26
        /// The usual contains an Decimal value (new in X#).
        MEMBER Decimal	:=27
        /// The usual contains an Memo value. This value is there for compatibility with VO but never used.
        MEMBER Memo		:=32	// Used in RDD system in VO
        /// Invalid Usual Type.
        MEMBER Invalid    :=99
    END ENUM

END NAMESPACE

/// <exclude/>
FUNCTION ErrorBuild(pErrInfo AS exception) AS XSharp.ERROR
	RETURN  XSharp.Error{pErrInfo}


(Dbf RDD) Correction to handle Empty Memo

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Text
USING XSharp.RDD.Enums
USING XSharp.RDD.Support

BEGIN NAMESPACE XSharp.RDD
    /// DBFDBT RDD. For DBF/DBT. No index support at this level
    CLASS DBFDBT INHERIT DBF
        CONSTRUCTOR
            SUPER()
            SELF:_oMemo := DBTMemo{SELF}

        VIRTUAL PROPERTY SysName AS STRING GET TYPEOF(DbfDbt):ToString()

        // Return the memo content as STRING
        METHOD GetValue(nFldPos AS LONG) AS OBJECT
            LOCAL buffer AS BYTE[]
            // not a memo ?
            IF SELF:_isMemoField( nFldPos )
                // At this level, the return value is the raw Data, in BYTE[]
                buffer := (BYTE[])SUPER:GetValue(nFldPos)
				IF ( buffer != NULL )
					LOCAL encoding AS Encoding //ASCIIEncoding
	                LOCAL str AS STRING
					encoding := SELF:_Encoding // ASCIIEncoding{}
					str :=  encoding:GetString(buffer)
					// Convert to String and return
					RETURN str
				ELSE
					// No Memo ?!, Empty String
					RETURN String.Empty
				ENDIF
            ENDIF
            RETURN SUPER:GetValue(nFldPos)

            END CLASS

        /// DBT Memo class. Implements the DBT support.
        // See https://www.clicketyclick.dk/databases/xbase/format/dbt.html#DBT_STRUCT/
    //
    INTERNAL CLASS DBTMemo INHERIT BaseMemo IMPLEMENTS IMemo
        INTERNAL _hFile	    AS IntPtr
        INTERNAL _FileName  AS STRING
        PROTECT _Shared     AS LOGIC
        PROTECT _ReadOnly   AS LOGIC
        PROTECT _oRDD       AS DBF
        PROTECT _blockSize  AS SHORT
        PROTECT _defExt     AS STRING
        PROTECT _lockScheme AS DbfLocking

        PROPERTY DefExt AS STRING
            GET
                RETURN SELF:_defExt
            END GET
            SET
                SELF:_defExt := VALUE
            END SET
        END PROPERTY

        VIRTUAL PROPERTY BlockSize 	 AS SHORT
            GET
                RETURN SELF:_blockSize
            END GET

            SET
                SELF:_blockSize := VALUE
            END SET

        END PROPERTY

        INTERNAL CONST HeaderSize := 512 AS LONG

        CONSTRUCTOR (oRDD AS DBF)
            SUPER(oRDD)
            SELF:_oRdd := oRDD
            SELF:_hFile := F_ERROR
            SELF:_Shared := SELF:_oRDD:_Shared
            SELF:_ReadOnly := SELF:_oRdd:_ReadOnly
			SELF:_Encoding := SELF:_oRdd:_Encoding
            SELF:DefExt := DBT_MEMOEXT
            // We should read the MEMOBLOCK setting here : 512 Per default
            SELF:_blockSize := 0

        VIRTUAL PROTECTED METHOD _initContext() AS VOID
            SELF:_blockSize := DBT_DEFBLOCKSIZE
            SELF:_lockScheme:Initialize( DbfLockingModel.Clipper52 )

            /// <inheritdoc />
        METHOD Flush() 			AS LOGIC
            LOCAL isOk AS LOGIC
            isOk := ( SELF:_hFile != F_ERROR )
            IF isOk
                isOk := FFlush( SELF:_hFile )
            ENDIF
            RETURN isOk

            /// <inheritdoc />
        METHOD GetValue(nFldPos AS INT) AS OBJECT
            LOCAL blockNbr AS LONG
            LOCAL blockLen := 0 AS LONG
            LOCAL memoBlock := NULL AS BYTE[]
            //
            blockNbr := SELF:_oRDD:_getMemoBlockNumber( nFldPos )
            IF ( blockNbr > 0 )
                // Get the raw Length of the Memo
                blockLen := SELF:_getValueLength( nFldPos )
                IF blockLen > 0
                    memoBlock := BYTE[]{ blockLen }
                    // Where do we start ?
                    LOCAL iOffset := blockNbr * SELF:BlockSize AS LONG
                    //
                    FSeek3( SELF:_hFile, iOffset, FS_SET )
                    // Max 512 Blocks
                    LOCAL isOk AS LOGIC
                    isOk := ( FRead3( SELF:_hFile, memoBlock, (DWORD)blockLen ) == blockLen )
                    IF ( !isOk )
                        memoBlock := NULL
                    ENDIF
                ENDIF
            ENDIF
            // At this level, the return value is the raw Data, in BYTE[]
            RETURN memoBlock

            /// <inheritdoc />
        METHOD GetValueFile(nFldPos AS INT, fileName AS STRING) AS LOGIC
            THROW NotImplementedException{}

            /// <inheritdoc />
        METHOD GetValueLength(nFldPos AS INT) AS INT
            RETURN SELF:_getValueLength( nFldPos )

            // Do the calculation for the MemoLength
        VIRTUAL PROTECTED METHOD _getValueLength(nFldPos AS INT) AS INT
            LOCAL blockNbr AS LONG
            LOCAL blockLen := 0 AS LONG
            // Where does the block start ?
            blockNbr := SELF:_oRDD:_getMemoBlockNumber( nFldPos )
            IF ( blockNbr > 0 )
                // File Open ?
                LOCAL isOk := ( SELF:_hFile != F_ERROR ) AS LOGIC
                IF isOk
                    // We don't need to store all the data, just read block by block
                    // in a temporary buffer, to count the needed size
                    LOCAL memoBlock AS BYTE[]
                    memoBlock := BYTE[]{SELF:BlockSize}
                    // Where do we start ?
                    LOCAL iOffset := blockNbr * SELF:BlockSize AS LONG
                    // Go to the blockNbr position
                    FSeek3( SELF:_hFile, iOffset, FS_SET )
                    //
                    LOCAL sizeRead := 1 AS LONG
                    LOCAL endPos := -1 AS LONG
                    WHILE ( sizeRead > 0 ) .AND. ( endPos == - 1 )
                        sizeRead := (LONG)FRead3( SELF:_hFile, memoBlock, (DWORD)SELF:BlockSize )
                        IF ( sizeRead > 0 )
                            // Search for one field terminator (1Ah)
                            endPos := Array.FindIndex<BYTE>( memoBlock, { x => x == 0x1A } )
                            IF ( endPos == - 1 )
                                blockLen += sizeRead
                            ELSE
                                // Some docs says that Memo ends with two field teminator ???
                                // https://www.clicketyclick.dk/databases/xbase/format/dbt.html
                                // but ok, let say only one.
                                blockLen += endPos
                            ENDIF
                        ENDIF
                    ENDDO
                ENDIF
            ENDIF
            RETURN blockLen

            /// <inheritdoc />
        VIRTUAL METHOD PutValue(nFldPos AS INT, oValue AS OBJECT) AS LOGIC
            LOCAL objType AS System.Type
            LOCAL objTypeCode AS System.TypeCode
            LOCAL str := NULL AS STRING
            LOCAL memoBlock AS BYTE[]
            LOCAL isOk := FALSE AS LOGIC
            LOCAL locked := FALSE AS LOGIC
            //
            IF ( SELF:_hFile == F_ERROR )
                RETURN FALSE
            ENDIF
            //
            objType := oValue:GetType()
            objTypeCode := Type.GetTypeCode( objType )
            //
            IF ( objTypeCode == TypeCode.Char )
                str := STRING{ (CHAR)oValue, 1 }
            ELSEIF ( objTypeCode == TypeCode.String )
                str := oValue ASTYPE STRING
            ENDIF
            // Not a Char, Not a String
            IF ( str == NULL )
                memoBlock := (BYTE[])oValue
            ELSE
                LOCAL encoding AS Encoding //ASCIIEncoding
                memoBlock := BYTE[]{str:Length}
                encoding := SELF:_Encoding //ASCIIEncoding{}
                encoding:GetBytes( str, 0, str:Length, memoBlock, 0 )
            ENDIF
            // Now, calculate where we will write the Datas
            LOCAL blockNbr AS LONG
            LOCAL blockLen := 0 AS LONG
            LOCAL newBlock AS LOGIC
            // Suppose we will have a new start of block
            newBlock := TRUE
            // Where are currently stored the datas ?
            blockNbr := SELF:_oRDD:_getMemoBlockNumber( nFldPos )
            IF ( blockNbr > 0 )
                // Existing block ? What is it's current raw length ?
                blockLen := SELF:_getValueLength( nFldPos )
                IF blockLen > 0
                    // Compare the number of Blocks : Do we need more blocks ?
                    newBlock := ( memoBlock:Length / SELF:_blockSize ) > ( blockLen / SELF:BlockSize )
                ENDIF
            ENDIF
            IF newBlock
                IF ( SELF:_Shared )
                    locked := SELF:_tryLock( (UINT64)SELF:_lockScheme:Offset, 1, (LONG)XSharp.RuntimeState.LockTries )
                ENDIF
                // Go to the end of end, where we will add the new data
                FSeek3( SELF:_hFile, 0, FS_END )
                LOCAL fileSize := (LONG)FTell( SELF:_hFile ) AS LONG
                // But first, check that the Size of Memo file is multiple of BlockSize
                LOCAL toAdd := fileSize % SELF:BlockSize AS LONG
                // No ! Fill the gap
                IF ( toAdd > 0 )
                    toAdd := SELF:BlockSize - toAdd
                    LOCAL dummy AS BYTE[]
                    dummy := BYTE[]{ toAdd }
                    FWrite3( SELF:_hfile, dummy, (DWORD)toAdd )
                    // so, new size is
                    fileSize := (LONG)FTell( SELF:_hFile )
                ENDIF
                // The new block Number to write to is (I supposed we should use NextAvailableBlock, no ?)
                blockNbr := fileSize / SELF:BlockSize
            ELSE
                // Go to the position of the blockNbr
                FSeek3( SELF:_hFile, blockNbr * SELF:BlockSize, FS_SET )
            ENDIF
            // Ok, now write the Datas
            isok := ( FWrite3( SELF:_hfile, memoBlock, (DWORD)memoBlock:Length ) == memoBlock:Length )
            IF isOk
                // Don't forget to write an End Of Block terminator (1Ah) (Should it be two ??)
                LOCAL eob := <BYTE>{0x1A} AS BYTE[]
                isOk := ( FWrite3( SELF:_hFile, eob, 1 ) == 1 )
                IF isOk .AND. newBlock
                    // We have to update the next block info
                    LOCAL newPos AS LONG // FTell might do the job ?
                    newPos := blockNbr * SELF:BlockSize + memoBlock:Length + 1
                    newPos += SELF:BlockSize - newPos % SELF:BlockSize
                    SELF:NextAvailableBlock := newPos / SELF:BlockSize
                ENDIF
                IF isOk
                    isOk := SELF:Flush()
                ENDIF
            ENDIF
            IF ( locked )
                SELF:_unlock( (UINT64)SELF:_lockScheme:Offset, 1 )
            ENDIF
            //
            IF !isOk
                SELF:_oRDD:_DbfError( ERDD.WRITE, XSharp.Gencode.EG_WRITE )
            ENDIF
            // Now, update the information in the DBF, look at PutValue() in the DbfDbt Class
            SELF:LastWrittenBlockNumber := blockNbr
            RETURN isOk

            /// <inheritdoc />
        VIRTUAL METHOD PutValueFile(nFldPos AS INT, fileName AS STRING) AS LOGIC
            THROW NotImplementedException{}

            /// <inheritdoc />
        VIRTUAL METHOD CloseMemFile( ) AS LOGIC
            LOCAL isOk := FALSE AS LOGIC
            IF ( SELF:_hFile != F_ERROR )
                //
                TRY
                    isOk := FClose( SELF:_hFile )
                CATCH
                    isOk := FALSE
                END TRY
                SELF:_hFile := F_ERROR
            ENDIF
            RETURN isOk

            /// <inheritdoc />
        VIRTUAL METHOD CreateMemFile(info AS DbOpenInfo) AS LOGIC
            LOCAL isOk AS LOGIC
            SELF:_FileName  := info:FileName
            SELF:_FileName  := System.IO.Path.ChangeExtension( SELF:_FileName, SELF:DefExt )
            SELF:_Shared    := info:Shared
            SELF:_ReadOnly  := info:ReadOnly
            //
            SELF:_hFile     := FCreate( SELF:_FileName)
            isOk := ( SELF:_hFile != F_ERROR )
            IF isOk
                // Per default, Header Block Size if 512
                LOCAL memoHeader AS BYTE[]
                LOCAL nextBlock AS LONG
                //
                memoHeader := BYTE[]{ DBTMemo.HeaderSize }
                nextBlock := 1
                Array.Copy(BitConverter.GetBytes(nextBlock),0, memoHeader, 0, SIZEOF(LONG))
                //
                FWrite3( SELF:_hFile, memoHeader, DBTMemo.HeaderSize )
                //
                SELF:_initContext()
            ELSE
                SELF:_oRDD:_DbfError( ERDD.CREATE_MEMO, XSharp.Gencode.EG_CREATE )
            ENDIF
            //
            RETURN isOk

            /// <inheritdoc />
        VIRTUAL METHOD OpenMemFile(info AS DbOpenInfo ) AS LOGIC
            LOCAL isOk AS LOGIC
            SELF:_FileName  := info:FileName
            SELF:_FileName  := System.IO.Path.ChangeExtension( SELF:_FileName, SELF:DefExt )
            SELF:_Shared    := info:Shared
            SELF:_ReadOnly  := info:ReadOnly
            //
            SELF:_hFile     := Fopen(SELF:_FileName, info:FileMode)
            isOk := ( SELF:_hFile != F_ERROR )
            IF isOk
                // Per default, Block Size if 512
                SELF:_initContext()
            ELSE
                SELF:_oRDD:_DbfError( ERDD.OPEN_MEMO, XSharp.Gencode.EG_OPEN )
                isOk := FALSE
            ENDIF
            //
            RETURN isOk

        PROPERTY NextAvailableBlock 	 AS LONG
            GET
                LOCAL nBlock := 0 AS LONG
                LOCAL isOk AS LOGIC
                isOk := ( SELF:_hFile != F_ERROR )
                IF isOk
                    LOCAL _memoBlock AS BYTE[]
                    // NextBlock is at the beginning of MemoFile
                    _memoBlock := BYTE[]{4}
                    LOCAL savedPos := FSeek3(SELF:_hFile, 0, FS_RELATIVE ) AS LONG
                    FSeek3( SELF:_hFile, 0, FS_SET )
                    IF ( FRead3( SELF:_hFile, _memoBlock, 4 ) == 4 )
                        nBlock := BitConverter.ToInt32( _memoBlock, 0)
                    ENDIF
                    FSeek3(SELF:_hFile, savedPos, FS_SET )
                ENDIF
                RETURN nBlock
            END GET

            SET
                LOCAL isOk AS LOGIC
                isOk := ( SELF:_hFile != F_ERROR )
                IF isOk
                    LOCAL _memoBlock AS BYTE[]
                    _memoBlock := BYTE[]{4}
                    Array.Copy(BitConverter.GetBytes(VALUE),0, _memoBlock, 0, SIZEOF(LONG))
                    LOCAL savedPos := FSeek3(SELF:_hFile, 0, FS_RELATIVE ) AS LONG
                    FSeek3( SELF:_hFile, 0, FS_SET )
                    FWrite3( SELF:_hFile, _memoBlock, 4 )
                    FSeek3(SELF:_hFile, savedPos, FS_SET )
                ENDIF
            END SET

        END PROPERTY

        // Place a lock : <nOffset> indicate where the lock should be; <nLong> indicate the number bytes to lock
        // If it fails, the operation is tried <nTries> times, waiting 1ms between each operation.
        // Return the result of the operation
        PROTECTED METHOD _tryLock( nOffset AS UINT64, nLong AS LONG, nTries AS LONG  ) AS LOGIC
            LOCAL locked AS LOGIC
            //
            REPEAT
                TRY
                    locked := FFLock( SELF:_hFile, (DWORD)nOffset, (DWORD)nLong )
                CATCH
                    locked := FALSE
                END TRY
                IF ( !locked )
                    nTries --
                    IF ( nTries > 0 )
                        System.Threading.Thread.Sleep( 1 )
                    ENDIF
                ENDIF
            UNTIL ( locked .OR. (nTries==0) )
            //
            RETURN locked

        PROTECTED METHOD _unlock( nOffset AS UINT64, nLong AS LONG ) AS LOGIC
            LOCAL unlocked AS LOGIC
            //
            TRY
                unlocked := FFUnLock( SELF:_hFile, (DWORD)nOffset, (DWORD)nLong )
            CATCH
                unlocked := FALSE
            END TRY
            RETURN unlocked

        VIRTUAL METHOD Zap() AS LOGIC
            THROW NotImplementedException{}

    END CLASS

END NAMESPACE

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Text
USING XSharp.RDD.Enums
USING XSharp.RDD.Support

BEGIN NAMESPACE XSharp.RDD
    /// DBFDBT RDD. For DBF/DBT. No index support at this level
    CLASS DBFDBT INHERIT DBF
        CONSTRUCTOR
            SUPER()
            SELF:_oMemo := DBTMemo{SELF}

        VIRTUAL PROPERTY SysName AS STRING GET TYPEOF(DbfDbt):ToString()

        // Return the memo content as STRING
        METHOD GetValue(nFldPos AS LONG) AS OBJECT
            LOCAL buffer AS BYTE[]
            // not a memo ?
            IF SELF:_isMemoField( nFldPos )
                // At this level, the return value is the raw Data, in BYTE[]
                buffer := (BYTE[])SUPER:GetValue(nFldPos)
                LOCAL encoding AS Encoding //ASCIIEncoding
                LOCAL str AS STRING
                encoding := SELF:_Encoding // ASCIIEncoding{}
                str :=  encoding:GetString(buffer)
                // Convert to String and return
                RETURN str
            ENDIF
            RETURN SUPER:GetValue(nFldPos)

            END CLASS

        /// DBT Memo class. Implements the DBT support.
        // See https://www.clicketyclick.dk/databases/xbase/format/dbt.html#DBT_STRUCT/
    //
    INTERNAL CLASS DBTMemo INHERIT BaseMemo IMPLEMENTS IMemo
        INTERNAL _hFile	    AS IntPtr
        INTERNAL _FileName  AS STRING
        PROTECT _Shared     AS LOGIC
        PROTECT _ReadOnly   AS LOGIC
        PROTECT _oRDD       AS DBF
        PROTECT _blockSize  AS SHORT
        PROTECT _defExt     AS STRING
        PROTECT _lockScheme AS DbfLocking

        PROPERTY DefExt AS STRING
            GET
                RETURN SELF:_defExt
            END GET
            SET
                SELF:_defExt := VALUE
            END SET
        END PROPERTY

        VIRTUAL PROPERTY BlockSize 	 AS SHORT
            GET
                RETURN SELF:_blockSize
            END GET

            SET
                SELF:_blockSize := VALUE
            END SET

        END PROPERTY

        INTERNAL CONST HeaderSize := 512 AS LONG

        CONSTRUCTOR (oRDD AS DBF)
            SUPER(oRDD)
            SELF:_oRdd := oRDD
            SELF:_hFile := F_ERROR
            SELF:_Shared := SELF:_oRDD:_Shared
            SELF:_ReadOnly := SELF:_oRdd:_ReadOnly
			SELF:_Encoding := SELF:_oRdd:_Encoding
            SELF:DefExt := DBT_MEMOEXT
            // We should read the MEMOBLOCK setting here : 512 Per default
            SELF:_blockSize := 0

        VIRTUAL PROTECTED METHOD _initContext() AS VOID
            SELF:_blockSize := DBT_DEFBLOCKSIZE
            SELF:_lockScheme:Initialize( DbfLockingModel.Clipper52 )

            /// <inheritdoc />
        METHOD Flush() 			AS LOGIC
            LOCAL isOk AS LOGIC
            isOk := ( SELF:_hFile != F_ERROR )
            IF isOk
                isOk := FFlush( SELF:_hFile )
            ENDIF
            RETURN isOk

            /// <inheritdoc />
        METHOD GetValue(nFldPos AS INT) AS OBJECT
            LOCAL blockNbr AS LONG
            LOCAL blockLen := 0 AS LONG
            LOCAL memoBlock := NULL AS BYTE[]
            //
            blockNbr := SELF:_oRDD:_getMemoBlockNumber( nFldPos )
            IF ( blockNbr > 0 )
                // Get the raw Length of the Memo
                blockLen := SELF:_getValueLength( nFldPos )
                IF blockLen > 0
                    memoBlock := BYTE[]{ blockLen }
                    // Where do we start ?
                    LOCAL iOffset := blockNbr * SELF:BlockSize AS LONG
                    //
                    FSeek3( SELF:_hFile, iOffset, FS_SET )
                    // Max 512 Blocks
                    LOCAL isOk AS LOGIC
                    isOk := ( FRead3( SELF:_hFile, memoBlock, (DWORD)blockLen ) == blockLen )
                    IF ( !isOk )
                        memoBlock := NULL
                    ENDIF
                ENDIF
            ENDIF
            // At this level, the return value is the raw Data, in BYTE[]
            RETURN memoBlock

            /// <inheritdoc />
        METHOD GetValueFile(nFldPos AS INT, fileName AS STRING) AS LOGIC
            THROW NotImplementedException{}

            /// <inheritdoc />
        METHOD GetValueLength(nFldPos AS INT) AS INT
            RETURN SELF:_getValueLength( nFldPos )

            // Do the calculation for the MemoLength
        VIRTUAL PROTECTED METHOD _getValueLength(nFldPos AS INT) AS INT
            LOCAL blockNbr AS LONG
            LOCAL blockLen := 0 AS LONG
            // Where does the block start ?
            blockNbr := SELF:_oRDD:_getMemoBlockNumber( nFldPos )
            IF ( blockNbr > 0 )
                // File Open ?
                LOCAL isOk := ( SELF:_hFile != F_ERROR ) AS LOGIC
                IF isOk
                    // We don't need to store all the data, just read block by block
                    // in a temporary buffer, to count the needed size
                    LOCAL memoBlock AS BYTE[]
                    memoBlock := BYTE[]{SELF:BlockSize}
                    // Where do we start ?
                    LOCAL iOffset := blockNbr * SELF:BlockSize AS LONG
                    // Go to the blockNbr position
                    FSeek3( SELF:_hFile, iOffset, FS_SET )
                    //
                    LOCAL sizeRead := 1 AS LONG
                    LOCAL endPos := -1 AS LONG
                    WHILE ( sizeRead > 0 ) .AND. ( endPos == - 1 )
                        sizeRead := (LONG)FRead3( SELF:_hFile, memoBlock, (DWORD)SELF:BlockSize )
                        IF ( sizeRead > 0 )
                            // Search for one field terminator (1Ah)
                            endPos := Array.FindIndex<BYTE>( memoBlock, { x => x == 0x1A } )
                            IF ( endPos == - 1 )
                                blockLen += sizeRead
                            ELSE
                                // Some docs says that Memo ends with two field teminator ???
                                // https://www.clicketyclick.dk/databases/xbase/format/dbt.html
                                // but ok, let say only one.
                                blockLen += endPos
                            ENDIF
                        ENDIF
                    ENDDO
                ENDIF
            ENDIF
            RETURN blockLen

            /// <inheritdoc />
        VIRTUAL METHOD PutValue(nFldPos AS INT, oValue AS OBJECT) AS LOGIC
            LOCAL objType AS System.Type
            LOCAL objTypeCode AS System.TypeCode
            LOCAL str := NULL AS STRING
            LOCAL memoBlock AS BYTE[]
            LOCAL isOk := FALSE AS LOGIC
            LOCAL locked := FALSE AS LOGIC
            //
            IF ( SELF:_hFile == F_ERROR )
                RETURN FALSE
            ENDIF
            //
            objType := oValue:GetType()
            objTypeCode := Type.GetTypeCode( objType )
            //
            IF ( objTypeCode == TypeCode.Char )
                str := STRING{ (CHAR)oValue, 1 }
            ELSEIF ( objTypeCode == TypeCode.String )
                str := oValue ASTYPE STRING
            ENDIF
            // Not a Char, Not a String
            IF ( str == NULL )
                memoBlock := (BYTE[])oValue
            ELSE
                LOCAL encoding AS Encoding //ASCIIEncoding
                memoBlock := BYTE[]{str:Length}
                encoding := SELF:_Encoding //ASCIIEncoding{}
                encoding:GetBytes( str, 0, str:Length, memoBlock, 0 )
            ENDIF
            // Now, calculate where we will write the Datas
            LOCAL blockNbr AS LONG
            LOCAL blockLen := 0 AS LONG
            LOCAL newBlock AS LOGIC
            // Suppose we will have a new start of block
            newBlock := TRUE
            // Where are currently stored the datas ?
            blockNbr := SELF:_oRDD:_getMemoBlockNumber( nFldPos )
            IF ( blockNbr > 0 )
                // Existing block ? What is it's current raw length ?
                blockLen := SELF:_getValueLength( nFldPos )
                IF blockLen > 0
                    // Compare the number of Blocks : Do we need more blocks ?
                    newBlock := ( memoBlock:Length / SELF:_blockSize ) > ( blockLen / SELF:BlockSize )
                ENDIF
            ENDIF
            IF newBlock
                IF ( SELF:_Shared )
                    locked := SELF:_tryLock( (UINT64)SELF:_lockScheme:Offset, 1, (LONG)XSharp.RuntimeState.LockTries )
                ENDIF
                // Go to the end of end, where we will add the new data
                FSeek3( SELF:_hFile, 0, FS_END )
                LOCAL fileSize := (LONG)FTell( SELF:_hFile ) AS LONG
                // But first, check that the Size of Memo file is multiple of BlockSize
                LOCAL toAdd := fileSize % SELF:BlockSize AS LONG
                // No ! Fill the gap
                IF ( toAdd > 0 )
                    toAdd := SELF:BlockSize - toAdd
                    LOCAL dummy AS BYTE[]
                    dummy := BYTE[]{ toAdd }
                    FWrite3( SELF:_hfile, dummy, (DWORD)toAdd )
                    // so, new size is
                    fileSize := (LONG)FTell( SELF:_hFile )
                ENDIF
                // The new block Number to write to is (I supposed we should use NextAvailableBlock, no ?)
                blockNbr := fileSize / SELF:BlockSize
            ELSE
                // Go to the position of the blockNbr
                FSeek3( SELF:_hFile, blockNbr * SELF:BlockSize, FS_SET )
            ENDIF
            // Ok, now write the Datas
            isok := ( FWrite3( SELF:_hfile, memoBlock, (DWORD)memoBlock:Length ) == memoBlock:Length )
            IF isOk
                // Don't forget to write an End Of Block terminator (1Ah) (Should it be two ??)
                LOCAL eob := <BYTE>{0x1A} AS BYTE[]
                isOk := ( FWrite3( SELF:_hFile, eob, 1 ) == 1 )
                IF isOk .AND. newBlock
                    // We have to update the next block info
                    LOCAL newPos AS LONG // FTell might do the job ?
                    newPos := blockNbr * SELF:BlockSize + memoBlock:Length + 1
                    newPos += SELF:BlockSize - newPos % SELF:BlockSize
                    SELF:NextAvailableBlock := newPos / SELF:BlockSize
                ENDIF
                IF isOk
                    isOk := SELF:Flush()
                ENDIF
            ENDIF
            IF ( locked )
                SELF:_unlock( (UINT64)SELF:_lockScheme:Offset, 1 )
            ENDIF
            //
            IF !isOk
                SELF:_oRDD:_DbfError( ERDD.WRITE, XSharp.Gencode.EG_WRITE )
            ENDIF
            // Now, update the information in the DBF, look at PutValue() in the DbfDbt Class
            SELF:LastWrittenBlockNumber := blockNbr
            RETURN isOk

            /// <inheritdoc />
        VIRTUAL METHOD PutValueFile(nFldPos AS INT, fileName AS STRING) AS LOGIC
            THROW NotImplementedException{}

            /// <inheritdoc />
        VIRTUAL METHOD CloseMemFile( ) AS LOGIC
            LOCAL isOk := FALSE AS LOGIC
            IF ( SELF:_hFile != F_ERROR )
                //
                TRY
                    isOk := FClose( SELF:_hFile )
                CATCH
                    isOk := FALSE
                END TRY
                SELF:_hFile := F_ERROR
            ENDIF
            RETURN isOk

            /// <inheritdoc />
        VIRTUAL METHOD CreateMemFile(info AS DbOpenInfo) AS LOGIC
            LOCAL isOk AS LOGIC
            SELF:_FileName  := info:FileName
            SELF:_FileName  := System.IO.Path.ChangeExtension( SELF:_FileName, SELF:DefExt )
            SELF:_Shared    := info:Shared
            SELF:_ReadOnly  := info:ReadOnly
            //
            SELF:_hFile     := FCreate( SELF:_FileName)
            isOk := ( SELF:_hFile != F_ERROR )
            IF isOk
                // Per default, Header Block Size if 512
                LOCAL memoHeader AS BYTE[]
                LOCAL nextBlock AS LONG
                //
                memoHeader := BYTE[]{ DBTMemo.HeaderSize }
                nextBlock := 1
                Array.Copy(BitConverter.GetBytes(nextBlock),0, memoHeader, 0, SIZEOF(LONG))
                //
                FWrite3( SELF:_hFile, memoHeader, DBTMemo.HeaderSize )
                //
                SELF:_initContext()
            ELSE
                SELF:_oRDD:_DbfError( ERDD.CREATE_MEMO, XSharp.Gencode.EG_CREATE )
            ENDIF
            //
            RETURN isOk

            /// <inheritdoc />
        VIRTUAL METHOD OpenMemFile(info AS DbOpenInfo ) AS LOGIC
            LOCAL isOk AS LOGIC
            SELF:_FileName  := info:FileName
            SELF:_FileName  := System.IO.Path.ChangeExtension( SELF:_FileName, SELF:DefExt )
            SELF:_Shared    := info:Shared
            SELF:_ReadOnly  := info:ReadOnly
            //
            SELF:_hFile     := Fopen(SELF:_FileName, info:FileMode)
            isOk := ( SELF:_hFile != F_ERROR )
            IF isOk
                // Per default, Block Size if 512
                SELF:_initContext()
            ELSE
                SELF:_oRDD:_DbfError( ERDD.OPEN_MEMO, XSharp.Gencode.EG_OPEN )
                isOk := FALSE
            ENDIF
            //
            RETURN isOk

        PROPERTY NextAvailableBlock 	 AS LONG
            GET
                LOCAL nBlock := 0 AS LONG
                LOCAL isOk AS LOGIC
                isOk := ( SELF:_hFile != F_ERROR )
                IF isOk
                    LOCAL _memoBlock AS BYTE[]
                    // NextBlock is at the beginning of MemoFile
                    _memoBlock := BYTE[]{4}
                    LOCAL savedPos := FSeek3(SELF:_hFile, 0, FS_RELATIVE ) AS LONG
                    FSeek3( SELF:_hFile, 0, FS_SET )
                    IF ( FRead3( SELF:_hFile, _memoBlock, 4 ) == 4 )
                        nBlock := BitConverter.ToInt32( _memoBlock, 0)
                    ENDIF
                    FSeek3(SELF:_hFile, savedPos, FS_SET )
                ENDIF
                RETURN nBlock
            END GET

            SET
                LOCAL isOk AS LOGIC
                isOk := ( SELF:_hFile != F_ERROR )
                IF isOk
                    LOCAL _memoBlock AS BYTE[]
                    _memoBlock := BYTE[]{4}
                    Array.Copy(BitConverter.GetBytes(VALUE),0, _memoBlock, 0, SIZEOF(LONG))
                    LOCAL savedPos := FSeek3(SELF:_hFile, 0, FS_RELATIVE ) AS LONG
                    FSeek3( SELF:_hFile, 0, FS_SET )
                    FWrite3( SELF:_hFile, _memoBlock, 4 )
                    FSeek3(SELF:_hFile, savedPos, FS_SET )
                ENDIF
            END SET

        END PROPERTY

        // Place a lock : <nOffset> indicate where the lock should be; <nLong> indicate the number bytes to lock
        // If it fails, the operation is tried <nTries> times, waiting 1ms between each operation.
        // Return the result of the operation
        PROTECTED METHOD _tryLock( nOffset AS UINT64, nLong AS LONG, nTries AS LONG  ) AS LOGIC
            LOCAL locked AS LOGIC
            //
            REPEAT
                TRY
                    locked := FFLock( SELF:_hFile, (DWORD)nOffset, (DWORD)nLong )
                CATCH
                    locked := FALSE
                END TRY
                IF ( !locked )
                    nTries --
                    IF ( nTries > 0 )
                        System.Threading.Thread.Sleep( 1 )
                    ENDIF
                ENDIF
            UNTIL ( locked .OR. (nTries==0) )
            //
            RETURN locked

        PROTECTED METHOD _unlock( nOffset AS UINT64, nLong AS LONG ) AS LOGIC
            LOCAL unlocked AS LOGIC
            //
            TRY
                unlocked := FFUnLock( SELF:_hFile, (DWORD)nOffset, (DWORD)nLong )
            CATCH
                unlocked := FALSE
            END TRY
            RETURN unlocked

        VIRTUAL METHOD Zap() AS LOGIC
            THROW NotImplementedException{}

    END CLASS

END NAMESPACE