VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Fstat"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

' Class for doing FStat on a file

Private m_OfficeApp As OfficeApp
Private m_DocPath As String

Public Enum LockType
    NoLock = 0
    OurLock = 1
    OtherLock = 2
    OurExclusiveLock = 3
    OtherExclusiveLock = 4
End Enum

' Class member variables - don't forget to initialise below...
Private m_DepotFile As String
Private m_ClientFile As String
Private m_HeadAction As String
Private m_HeadType As String
Private m_HeadRev As Integer
Private m_HeadChange As Integer
Private m_HeadTime As Long
Private m_HaveRev As Integer
Private m_Action As String
Private m_ActionOwner As String
Private m_Change As String
Private m_LockType As String
Private m_NumOtherOpen As Integer
Private m_WhoOtherOpen As String
Private m_Unresolved As String

Private m_FormattedInfo() As String
Private m_Error As Boolean

Private m_Version As String    ' optional version specifier
    
Private m_p4 As P4COM.p4

Private Sub InitVars()
    m_Error = False
    m_DepotFile = vbNullString
    m_ClientFile = vbNullString
    m_HeadAction = vbNullString
    m_HeadType = vbNullString
    m_Action = vbNullString
    m_ActionOwner = vbNullString
    m_Change = vbNullString
    m_HeadRev = 0
    m_HeadChange = 0
    m_HaveRev = 0
    m_NumOtherOpen = 0
    m_WhoOtherOpen = vbNullString
    m_LockType = vbNullString
    m_Unresolved = vbNullString
    
    Erase m_FormattedInfo
End Sub

Public Sub Initialize(App As OfficeApp, Optional Version As String = vbNullString, _
                      Optional DocPath As String = vbNullString)
    Set m_OfficeApp = App
    m_Version = Version
    m_DocPath = DocPath
    
End Sub

Property Get Error() As Boolean
    Error = m_Error
End Property


Property Get DepotFile() As String
    DepotFile = m_DepotFile
End Property

Property Get ClientFile() As String
    ClientFile = m_ClientFile
End Property

Property Get HeadAction() As String
    HeadAction = m_HeadAction
End Property

Property Get HeadType() As String
    HeadType = m_HeadType
End Property

Property Get HeadRev() As Integer
    HeadRev = m_HeadRev
End Property

Property Get HeadTime() As Long
    HeadTime = m_HeadTime
End Property

Property Get HeadTimeAsDate() As Date
    ' Convert to VB date
    HeadTimeAsDate = CDate(UnixLongToDate(m_HeadTime))
End Property

Property Get HeadChange() As Integer
    HeadChange = m_HeadChange
End Property

Property Get HaveRev() As Integer
    HaveRev = m_HaveRev
End Property

Property Get Action() As String
    Action = m_Action
End Property

Property Get ActionOwner() As String
    ActionOwner = m_ActionOwner
End Property

Property Get Change() As String
    Change = m_Change
End Property

Property Get NumOtherOpen() As Integer
    NumOtherOpen = m_NumOtherOpen
End Property

Property Get WhoOtherOpen() As String
    WhoOtherOpen = m_WhoOtherOpen
End Property

Property Get Unresolved() As Boolean
    Unresolved = Len(m_Unresolved) <> 0
End Property

Private Function ExclusiveLockFileType() As Boolean
    Dim i As Integer
    
    ExclusiveLockFileType = False
    i = InStr(1, m_HeadType, "+")
    If i > 0 Then
        If InStr(i, m_HeadType, "l") > 0 Then
            ExclusiveLockFileType = True
        End If
    End If
    
End Function

Property Get LockType() As LockType
    If Len(m_LockType) = 0 Then
        LockType = NoLock
        If ExclusiveLockFileType Then
            If m_Action <> "" And m_ActionOwner = m_p4.User Then
                LockType = OurExclusiveLock
            ElseIf m_WhoOtherOpen <> "" Then
                LockType = OtherExclusiveLock
            End If
        End If
    ElseIf m_LockType = "ourLock" Then
        LockType = OurLock
    Else
        LockType = OtherLock
    End If
End Property


' Extract out of line who has file opened.
' Possible formats:
'   OtherOpen0 bruno@cl
'   OtherOpen 1
Private Sub GetOtherOpened(line As String, WhoElse As String, NumOpened As Integer)
    Dim T As Tracker: Set T = GStackTrace.Enter(TypeName(Me), "GetOtherOpened")
    Dim i As Integer
    Dim n As Integer
    Const Key = "otherOpen"
    Dim NextCh As String
    
    i = InStr(1, line, Key)
    If i > 0 Then
        n = i + Len(Key)
        NextCh = Mid$(line, n, 1)
        If NextCh = " " Then
            NumOpened = Mid$(line, n + 1)
        Else
            If Len(WhoElse) <> 0 Then
                WhoElse = WhoElse & vbCrLf
            End If
            WhoElse = WhoElse & Mid$(line, n + 2)
            SaveFormatted "Also open by", Mid$(line, n + 2)
        End If
    End If

End Sub

Private Sub ProcessOtherAction(line As String)
    Dim T As Tracker: Set T = GStackTrace.Enter(TypeName(Me), "ProcessOtherAction")
    Dim i As Integer
    Dim n As Integer
    Const Key = "otherAction"
    Dim NextCh As String
    
    i = InStr(1, line, Key)
    If i > 0 Then
        n = i + Len(Key)
        NextCh = Mid$(line, n, 1)
        If NextCh <> " " Then
            SaveFormatted "Opened by them for", Mid$(line, n + 2)
        End If
    End If

End Sub

' Extract OtherOpen formatted information
Private Sub GetOtherOpen(line As String, VarName As String, Count As Integer, Var As String)
    Dim T As Tracker: Set T = GStackTrace.Enter(TypeName(Me), "GetOtherOpen")
    Dim i As Integer
    
    '   Format will be:
    '   depotFile //depot/.../<filename>
    i = InStr(1, line, VarName)
    If i <> 0 Then
        GetOtherOpened line, Var, Count
    End If

End Sub

Private Function IntValue(Value As String) As Integer
    
    On Error Resume Next
    IntValue = CInt(Value)
    err.Clear

End Function

Private Function LongValue(Value As String) As Long
    
    On Error Resume Next
    LongValue = CLng(Value)
    err.Clear

End Function

Public Sub Run()
    Dim T As Tracker: Set T = GStackTrace.Enter(TypeName(Me), "Run")
    Const CmdTitle = "fstat"
    Dim p4cmd As String
    Dim Result As Long
    Dim Msg As String
    Dim InfoArr() As String
    Dim ErrorArr() As String
    Dim FileName As String
    Dim DocPathname As String
    Dim i As Integer

    InitVars
    Set m_p4 = NewP4
    m_p4.ExceptionLevel = 0

    If Len(m_DocPath) = 0 Then
        If m_OfficeApp.NoCurrDoc() Then
            GoTo Exit_Block
        End If
        If Len(m_OfficeApp.GetCurrDocPath()) = 0 Then
            GoTo Exit_Block
        End If
            
        ' Make sure we change to appropriate directory (to pick up P4CONFIG file if there)
        m_OfficeApp.ChangeCurrDocDir
        m_p4.Cwd = m_OfficeApp.GetCurrDocPath
        DocPathname = m_OfficeApp.GetCurrDocPathname()
    Else
        DocPathname = m_DocPath
    End If
    
    If g_p4interface.HideConnectionWarning(m_p4.port) Then
        GoTo Exit_Block
    End If
    
    If Len(m_Version) = 0 Then
        p4cmd = "fstat " & Chr(34) & DocPathname & Chr(34)
    Else
        p4cmd = "fstat " & Chr(34) & DocPathname & "#" & m_Version & Chr(34)
    End If
    
    Result = p4runcmd(p4cmd, InfoArr, ErrorArr, FileName, m_p4)
    CheckResult Result, InfoArr, ErrorArr
    If LoginRequired(m_p4) Then
        If LoggedIn(m_p4) Then
            ' Retry command
            Result = p4runcmd(p4cmd, InfoArr, ErrorArr, FileName, m_p4)
            CheckResult Result, InfoArr, ErrorArr
        End If
    End If
    
    ' For debug purposes
    'DisplayResult CmdTitle, DocPathname, Result, InfoArr, ErrorArr

Exit_Block:

End Sub

Private Sub CheckResult(Result As Long, InfoArr() As String, ErrorArr() As String)
    Dim i As Integer
    
    If Not IsEmptyArray(ErrorArr) Then
        If GStacker.Tracing Then
            GStacker.TraceMsg "Error: " & Join(ErrorArr, "; ")
        End If
    End If
    If Result = 0 Then
        ' Check what is in the info array
        '   Format will be:
        '   depotFile //depot/.../<filename>
        ' and similar
        If Not IsEmptyArray(InfoArr) Then
            For i = LBound(InfoArr) To UBound(InfoArr)
                ProcessInfo InfoArr(i)
            Next
            If LockType = OurExclusiveLock Then
                SaveFormatted "Exclusively locked by you", ""
            ElseIf LockType = OtherExclusiveLock Then
                SaveFormatted "Exclusively locked by them", ""
            End If
        End If
    Else
        ' Empty Info/Error arrays indicates we failed to talk to server so
        ' set error flag (otherwise we would at least expect to get an error msg)
        If IsEmptyArray(InfoArr) And IsEmptyArray(ErrorArr) Then
            m_Error = True
        ElseIf UBound(InfoArr) < 0 And UBound(ErrorArr) < 0 Then
            m_Error = True
        End If
    End If

End Sub

Public Sub ProcessInfo(line As String)
    ' Parse returned result
    Dim Key As String
    Dim val As String
    Dim i As Integer
    
    i = InStr(1, line, " ")
    If i > 0 Then
        Key = Left(line, i - 1)
        val = Mid(line, i + 1)
    Else
        Key = line
        val = vbNullString
    End If
    
    Select Case Key
    
    Case "depotFile"
        m_DepotFile = val
        SaveFormatted "Depot file", val
    
    Case "clientFile"
        m_ClientFile = val
        SaveFormatted "Your document", val
        
    Case "headAction"
        m_HeadAction = val
        SaveFormatted "Latest (head) action in depot", val

    Case "headType"
        m_HeadType = val
        SaveFormatted "File type", val
        
    Case "headTime"
        m_HeadTime = LongValue(val)
        SaveFormatted "Last modified in depot", _
                    UnixLongToDate(LongValue(val))
            
    Case "action"
        m_Action = val
        SaveFormatted "Open for", val
        
    Case "actionOwner"
        m_ActionOwner = val
        SaveFormatted "Open by Perforce user", val
        
    Case "change"
        m_Change = val
        SaveFormatted "In changelist", val
            
    Case "headRev"
        m_HeadRev = IntValue(val)
        SaveFormatted "Latest (head) revision in depot", val
        
    Case "headChange"
        m_HeadChange = IntValue(val)
        SaveFormatted "Latest (head) changelist in depot", val
        
    Case "haveRev"
        m_HaveRev = IntValue(val)
        SaveFormatted "Revision in your workspace", val
            
    Case "ourLock"
        m_LockType = Key
        SaveFormatted "Locked by you", ""
        
    Case "otherLock"
        m_LockType = Key
        SaveFormatted "Locked by someone else", ""
            
    Case "unresolved"
        m_Unresolved = Key
        SaveFormatted "Needs to be resolved", ""
            
    Case Else
        ' Note that we can get "otherOpen" or "otherOpen1" etc
        If InStr(1, line, "otherOpen") <> 0 Then
            GetOtherOpened line, m_WhoOtherOpen, m_NumOtherOpen
        ElseIf InStr(1, line, "otherAction") <> 0 Then
            ProcessOtherAction line
        ElseIf Not WhiteSpace(line) Then
            If Key = "type" Then
                ' ignore
            Else
                Debug.Print "Not processing: " & line
            End If
        End If
        
    End Select

End Sub

Private Function WhiteSpace(s As String) As Boolean
    Dim i As Integer
    
    WhiteSpace = True
    If Len(s) <> 0 Then
        For i = 1 To Len(s)
            If Mid(s, i, 1) <> " " Then
                WhiteSpace = False
                Exit For
            End If
        Next
    End If

End Function

Private Sub SaveFormatted(ByVal Desc As String, ByVal val As String)
    Dim i As Integer
    Dim Separator As String
    
    On Error Resume Next
    i = UBound(m_FormattedInfo) + 1
    If err.Number <> 0 Then
        i = 1
        err.Clear
    End If
    ReDim Preserve m_FormattedInfo(i) As String
    
    If Len(val) <> 0 Then Separator = ": "
    m_FormattedInfo(i) = Desc & Separator & val

End Sub

Public Sub FormattedInfo(FI() As String)
    FI = m_FormattedInfo
End Sub
