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

Public Enum ActionStatus
    asSuccess
    asInUse
    asTooSoon
    asInsufficentMembers
    asDeclined
    asDecline
    asAccept
    asUnauthorized
    asUnknownUser
    asClanFull
    asBadTag
    asBadName
    
    asPending = &HFF
End Enum

Public Enum InvitationStatus
    isUnanswered = asPending
    isAccepted = asSuccess
    isDeclined = asDeclined
    isFailed = asDecline
    isClanFull = asClanFull
End Enum

Public Enum ActionSource
    asCommand
    asManager
    asWindow
    asDDP
    asVoid
    asAuto
End Enum

Private Enum ActionType
    atInvitation
    atRemoval
    atPromotion
    atDemotion
    atChieftainPromotion
End Enum

Private Type Invitation
    Username As String
    Status As InvitationStatus
    Source As ActionSource
    Extra As String
End Type

Private Type Action
    aType As ActionType
    Username As String
    Status As ActionStatus
    Source As ActionSource
    Extra As String
End Type

Public Enum CandidateSearchResult
    csSuccess = 0
    csTagTaken = 1
    csInsufficientCandidates = 3
    csAlreadyInClan = 8
    csInvalidTag = 10
    csUnknown = &HFF
End Enum

Private m_Tag As String
Private m_Members As Collection
Private m_Active As Boolean
Private m_PendingInvitations() As Invitation
Private m_numPendingInvitations As Long
Private m_PendingActions() As Action
Private m_numPendingActions As Long
Private m_Me As ClanMember
Private m_MOTD As String

Private m_DDP As Boolean
Private m_DDPUser As String
Private m_DDPShamen() As String
Private WithEvents m_DDPTimer As Timer
Attribute m_DDPTimer.VB_VarHelpID = -1
Private m_DDPPromoting As Boolean
Private m_DDPActive As Long

Private Sub Class_Initialize()
    Set m_Members = New Collection
    Set m_Me = Nothing
End Sub

Public Property Get Tag() As String
    Tag = m_Tag
End Property

Public Property Let Tag(ByVal NewValue As String)
    m_Tag = NewValue
End Property

Public Property Get Active() As Boolean
    Active = m_Active
End Property

Public Property Let Active(ByVal NewValue As Boolean)
    m_Active = NewValue
End Property

Public Property Get Myself() As ClanMember
    Set Myself = m_Me
End Property

Public Property Get Rank() As ClanRanks
    Rank = m_Me.Rank
End Property

Public Property Get MOTD() As String
    MOTD = m_MOTD
End Property

Public Property Let MOTD(ByVal NewValue As String)
    m_MOTD = NewValue
End Property

Public Property Get DDPInProgress() As Boolean
    DDPInProgress = m_DDP
End Property

Public Function AddMember(Member As ClanMember) As Boolean
On Error GoTo Insert_OK
    Dim TestMember As ClanMember
    Set TestMember = m_Members(Member.Username)
    ' already a member with this name; remove to avoid conflict
    If (RemoveMember(Member.Username) = False) Then
        AddMember = False
        Exit Function
    End If
    
    'drop
Insert_OK:
On Error GoTo AM_Error
    m_Members.Add Member, Member.Username
    If LCase$(Member.Username) = LCase$(Bot.Username) Then _
        Set m_Me = Member
    AddMember = True
    Exit Function
AM_Error:
    AddMember = False
End Function

Public Function GetMember(Username As String) As ClanMember
On Error GoTo GM_Error
    Dim Member As ClanMember
    Set Member = m_Members(Username)
    Set GetMember = Member
    Exit Function
GM_Error:
    Set GetMember = Nothing
End Function

Public Function GetMembers(Members() As ClanMember) As Boolean
On Error GoTo GM_Error
    Dim i As Long
    GetMembers = False
    If m_Members.Count <= 0 Then Exit Function
    
    ReDim Members(m_Members.Count - 1) As ClanMember
    For i = 1 To m_Members.Count
        Set Members(i - 1) = m_Members(i)
    Next i
    GetMembers = True
    Exit Function
GM_Error:
End Function

Public Function RemoveMember(Username As String) As Boolean
On Error GoTo RM_Error
    RemoveMember = False
    m_Members.Remove Username
    RemoveMember = True
    Exit Function
RM_Error:
End Function

Public Sub RequestMOTD()
    Bot.RequestClanMOTD
End Sub

Public Function Invite(Username As String, Source As ActionSource, Optional Key As String = vbNullString) As Boolean
    Dim Inv As Invitation
    Dim Cookie As Long
    
    If Rank < rankShaman Then
        Invite = False
        Exit Function
    End If
    
    Cookie = m_numPendingInvitations
    m_numPendingInvitations = m_numPendingInvitations + 1
    ReDim Preserve m_PendingInvitations(Cookie) As Invitation
    
    With m_PendingInvitations(Cookie)
        .Username = Username
        .Source = Source
        .Status = isUnanswered
        .Extra = Key
    End With
    
    Bot.InviteToClan Username, Cookie
    Invite = True
End Function

Public Function Remove(Username As String, Source As ActionSource, Optional Key As String = vbNullString) As Boolean
    Dim Cookie As Long
    
    If (Rank < rankShaman) Then
        Remove = False
        Exit Function
    End If
    
    Remove = True
    Cookie = AddAction(atRemoval, Username, Source, Key)
    
    Bot.RemoveClanMember Username, Cookie
End Function

'Can handle either standard promotions or chieftain changes.
Public Function Promote(Username As String, Source As ActionSource, Optional Key As String = vbNullString) As Boolean
    Dim Cookie As Long
    Dim Member As ClanMember
    Dim aType As ActionType
    
    If (Rank < rankShaman) Then
        Promote = False
        Exit Function
    End If
    
    Set Member = GetMember(Username)
    If (Member Is Nothing) Then
        Promote = False
        Exit Function
    End If
    
    If (Member.Rank = rankShaman) Then
        If (Rank < rankChieftain) Then
            Promote = False
            Exit Function
        End If
        
        aType = atChieftainPromotion
    ElseIf (Member.Rank = rankGrunt Or Member.Rank = rankPeon) Then
        aType = atPromotion
    Else
        'Cannot promote new peons or chieftains.
        Promote = False
        Exit Function
    End If
    
    Promote = True
    Cookie = AddAction(aType, Username, Source, Key)
    
    If (aType = atChieftainPromotion) Then
        Bot.ChangeChieftain Username, Cookie
    Else
        Bot.ChangeMemberRank Username, (Member.Rank + 1), Cookie
    End If
End Function

Public Function Demote(Username As String, Source As ActionSource, Optional Key As String = vbNullString) As Boolean
    Dim Cookie As Long
    Dim Member As ClanMember
    
    If (Rank < rankShaman) Then
        Demote = False
        Exit Function
    End If
    
    Set Member = GetMember(Username)
    If (Member Is Nothing) Then
        Demote = False
        Exit Function
    End If
    
    If (Member.Rank = rankShaman And Rank < rankChieftain) Then
        Demote = False
        Exit Function
    ElseIf (Member.Rank <= rankPeon Or Member.Rank = rankChieftain) Then
        Demote = False
        Exit Function
    End If
    
    Demote = True
    Cookie = AddAction(atDemotion, Username, Source, Key)
    
    Bot.ChangeMemberRank Username, (Member.Rank - 1), Cookie
End Function

Private Function AddAction(aType As ActionType, Username As String, Source As ActionSource, Extra As String) As Long
    Dim Cookie As Long
    Cookie = m_numPendingActions
    m_numPendingActions = m_numPendingActions + 1
    
    ReDim Preserve m_PendingActions(Cookie) As Action
    
    With m_PendingActions(Cookie)
        .aType = aType
        .Username = Username
        .Source = Source
        .Status = asPending
        .Extra = Extra
    End With
    
    AddAction = Cookie
End Function

Public Function SetMOTD(Message As String)
    SetMOTD = False
    
    If Rank < rankShaman Then _
        Exit Function
    
    Bot.SetClanMOTD Message
    SetMOTD = True
End Function

Public Function FindCandidates() As Boolean
    If (LenB(Tag) = 0) Then
        FindCandidates = False
        Exit Function
    End If
    
    FindCandidates = Bot.FindClanCandidates(Tag)
End Function

Public Sub CandidateResult(ByVal Result As CandidateSearchResult, ByVal Count As Long, Users() As String)
    If (Count < 9 And (Result = csSuccess Or Result = csUnknown)) Then
        frmCreateClan.CandidateResult csInsufficientCandidates, Users, Count
    Else
        frmCreateClan.CandidateResult Result, Users, Count
    End If
End Sub

Public Function CreateClan(ClanName As String, InitialMembers() As String, ByVal NumMembers As Long) As Boolean
    CreateClan = Bot.CreateClan(Tag, ClanName, InitialMembers, NumMembers)
End Function
' Demote/Designate/Promote
Public Sub DDP(FromUser As String, ByVal Source As CommandSources, ByVal User As String)
    Dim i As Long
    Dim j As Long
    Dim Members() As ClanMember
    Dim U As User
    
    If (Not UserClan.GetMembers(Members)) Then
        cmdOut FromUser, Source, "Error: Failed to get clan members."
    End If
    
On Error GoTo Loop_Trap
    For i = 0 To UBound(Members)
        If (Members(i).Rank = rankShaman) Then
            Set U = Nothing
            Set U = Bot.ChannelUsers(Members(i).Username)
            If Not (U Is Nothing) Then
                ReDim Preserve m_DDPShamen(j) As String
                m_DDPShamen(j) = Members(i).Username
                j = j + 1
            End If
        End If
    Next i
On Error GoTo 0
    If (j = 0) Then
        qAdd "/designate " & User, 1
        qAdd "/resign", 1
        Exit Sub
    End If
    
    'Start it up
    m_DDP = True
    m_DDPPromoting = False
    m_DDPActive = 0
    m_DDPUser = User
On Error GoTo Remove_Trap
    frmMain.Controls.Remove "m_DDPTimer"
On Error GoTo 0
    Set m_DDPTimer = frmMain.Controls.Add("VB.Timer", "m_DDPTimer")
    m_DDPTimer.Interval = CLng(GetConfig("DDPInterval", "Tweaks", "370"))
    m_DDPTimer.Enabled = True
    
    Exit Sub
Loop_Trap:
    Resume Next
Remove_Trap:
    Resume Next
End Sub

Public Sub DDPRestore()
    If (Not m_DDP) Then Exit Sub
    m_DDPActive = 0
    m_DDPPromoting = True
    m_DDPTimer.Enabled = True
End Sub

Private Sub ContinueDDP()
    m_DDPTimer.Enabled = True
End Sub

Private Sub m_DDPTimer_Timer()
    Dim i As Long
    i = m_DDPActive

    m_DDPTimer.Enabled = False
    
    If (i > UBound(m_DDPShamen)) Then
        Exit Sub
    End If
    
    If (Not m_DDPPromoting) Then
        Demote m_DDPShamen(i), asDDP
    Else
        Promote m_DDPShamen(i), asDDP
    End If
    
    If (i = UBound(m_DDPShamen)) Then
        If (Not m_DDPPromoting) Then
            qAdd "/designate " & m_DDPUser, 1
            qAdd "/resign", 1
        Else
            m_DDP = False
        End If
    End If
    
    m_DDPActive = m_DDPActive + 1
End Sub

Public Sub ClanCreated()
    frmCreateClan.ClanCreated
End Sub

Public Sub MemberChanged(Username As String, ByVal Rank As ClanRanks, ByVal Online As Boolean)
    Dim Member As ClanMember
    
    Set Member = GetMember(Username)
    If Member Is Nothing Then Exit Sub
    
    Member.Rank = Rank
    If (Rank = rankShaman And Online = False And Member.Online = True And Not (AutoClan Is Nothing)) Then
        AutoClan.ShamanDisconnected Username
    End If
    Member.Online = Online
    
    If (clanWindowOpen) Then _
        frmClan.ListChanged
End Sub

Public Sub MemberRemoved(Username As String)
    RemoveMember Username
    If (clanWindowOpen) Then _
        frmClan.ListChanged
End Sub

Public Sub InvitationResponse(ByVal Cookie As Long, ByVal Status As InvitationStatus)
    Dim Inv As Invitation, Message As String
    
    If Cookie >= m_numPendingInvitations Then Exit Sub
    
    Inv = m_PendingInvitations(Cookie)
    Inv.Status = Status
    
    Select Case Inv.Source
        Case asManager
            frmClan.InvitationResponse Inv.Username, Status
        Case asCommand
            Select Case Status
                Case isAccepted: Message = "User " & Inv.Username & " accepted the invitation."
                Case InvitationStatus.isClanFull: Message = "Failed to invite user " & Inv.Username & " to the clan: clan is full."
                Case InvitationStatus.isDeclined: Message = "User " & Inv.Username & " declined the invitation."
                Case InvitationStatus.isFailed: Message = "Failed to invite user " & Inv.Username & " to the clan."
                Case InvitationStatus.isUnanswered: Message = "User " & Inv.Username & " did not respond to the invitation."
            End Select
            
            If (LenB(Inv.Extra) = 0) Then
                cmdOut "", csNormal, Message
            ElseIf (Inv.Extra = "***WINDOW***") Then
                cmdOut "", csWindow, Message
            Else
                cmdOut Inv.Extra, csWhispered, Message
            End If
    End Select
End Sub

Public Sub ActionResponse(ByVal Cookie As Long, ByVal Status As ActionStatus)
    If Cookie < 0 Or Cookie >= m_numPendingActions Then Exit Sub
    
    With m_PendingActions(Cookie)
        Select Case .aType
            Case atRemoval
                Select Case .Source
                    Case asManager
                        frmClan.RemovalResponse .Username, Status
                    Case asCommand
                        RespondToCommand m_PendingActions(Cookie), Status
                End Select
            Case atPromotion, atChieftainPromotion
                Select Case .Source
                    Case asManager
                        frmClan.PromotionResponse .Username, Status
                    Case asDDP
                        ContinueDDP
                    Case asCommand
                        RespondToCommand m_PendingActions(Cookie), Status
                    Case asAuto
                        AutoClan.UserWasPromoted .Username, .Extra
                End Select
            Case atDemotion
                Select Case .Source
                    Case asManager
                        frmClan.DemotionResponse .Username, Status
                    Case asDDP
                        ContinueDDP
                    Case asCommand
                        RespondToCommand m_PendingActions(Cookie), Status
                    Case asAuto
                        AutoClan.UserWasDemoted .Username, .Extra
                End Select
        End Select
    End With
End Sub

Private Sub RespondToCommand(Action As Action, Status As ActionStatus)
    Dim Message As String, Verb As String
    
    Select Case Action.aType
        Case atInvitation: Verb = "invite"
        Case atDemotion: Verb = "demote"
        Case atChieftainPromotion, atPromotion: Verb = "promote"
        Case atRemoval: Verb = "remove"
    End Select
    
    Select Case Status
        Case ActionStatus.asSuccess, asAccept: Message = Action.Username & " was " & Verb & "d successfully."
        Case ActionStatus.asBadName: Message = "Failed to " & Verb & " " & Action.Username & ": Bad username."
        Case ActionStatus.asBadTag: Message = "Failed to " & Verb & " " & Action.Username & ": Bad tag."
        Case ActionStatus.asClanFull: Message = "Failed to " & Verb & " " & Action.Username & ": Clan is full."
        Case ActionStatus.asDecline, asDeclined: Message = "Failed to " & Verb & " " & Action.Username & ": Declined."
        Case ActionStatus.asInsufficentMembers: Message = "Failed to " & Verb & " " & Action.Username & ": Insufficient members."
        Case ActionStatus.asInUse: Message = "Failed to " & Verb & " " & Action.Username & ": In use."
        Case ActionStatus.asPending: Message = "Failed to " & Verb & " " & Action.Username & ": Pending."
        Case ActionStatus.asTooSoon: Message = "Failed to " & Verb & " " & Action.Username & ": Too early to do this."
        Case ActionStatus.asUnauthorized: Message = "Failed to " & Verb & " " & Action.Username & ": Unauthorized."
        Case ActionStatus.asUnknownUser: Message = "Failed to " & Verb & " " & Action.Username & ": Unknown member."
        Case Else:  Message = "Failed to " & Verb & " " & Action.Username & ": Unknown reason."
    End Select
    
    If (LenB(Action.Extra) = 0) Then
        cmdOut vbNullString, csNormal, Message
    ElseIf Action.Extra = "***WINDOW***" Then
        cmdOut vbNullString, csWindow, Message
    Else
        cmdOut Action.Extra, csWhispered, Message
    End If
End Sub

Public Function StatusString(ByVal Status As ActionStatus) As String
    Select Case Status
        Case asSuccess: StatusString = "Success"
        Case asInUse: StatusString = "In use"
        Case asTooSoon: StatusString = "Not enough time has passed"
        Case asInsufficentMembers: StatusString = "Not enough members"
        Case asDeclined: StatusString = "Invitation declined"
        Case asDecline: StatusString = "Decline"
        Case asAccept: StatusString = "Accept"
        Case asUnauthorized: StatusString = "Unauthorized action"
        Case asUnknownUser: StatusString = "Unknown user"
        Case asClanFull: StatusString = "Clan is full"
        Case asBadTag: StatusString = "Invalid tag"
        Case asBadName: StatusString = "Invalid name"
        Case asPending: StatusString = "Operation pending"
        Case Else: StatusString = "[Unknown]"
    End Select
End Function
