' ****************************************************************
' This is free software licensed under the NUnit license. You
' may obtain a copy of the license as well as information regarding
' copyright ownership at http://nunit.org
' ****************************************************************

Option Explicit On 

Namespace NUnit.Samples

    Public Class MoneyBag
        Implements IMoney

        Private fmonies As ArrayList = New ArrayList(5)

        Private Sub New()

        End Sub

        Public Sub New(ByVal bag As Money())
            For Each m As Money In bag
                If Not m.IsZero Then
                    AppendMoney(m)
                End If
            Next
        End Sub

        Public Sub New(ByVal m1 As Money, ByVal m2 As Money)

            AppendMoney(m1)
            AppendMoney(m2)

        End Sub

        Public Sub New(ByVal m As Money, ByVal bag As MoneyBag)
            AppendMoney(m)
            AppendBag(bag)
        End Sub

        Public Sub New(ByVal m1 As MoneyBag, ByVal m2 As MoneyBag)
            AppendBag(m1)
            AppendBag(m2)
        End Sub

        Public Function Add(ByVal m As IMoney) As IMoney Implements IMoney.Add
            Return m.AddMoneyBag(Me)
        End Function

        Public Function AddMoney(ByVal m As Money) As IMoney Implements IMoney.AddMoney
            Return New MoneyBag(m, Me).Simplify
        End Function

        Public Function AddMoneyBag(ByVal s As MoneyBag) As IMoney Implements IMoney.AddMoneyBag
            Return New MoneyBag(s, Me).Simplify()
        End Function

        Private Sub AppendBag(ByVal aBag As MoneyBag)
            For Each m As Money In aBag.fmonies
                AppendMoney(m)
            Next
        End Sub

        Private Sub AppendMoney(ByVal aMoney As Money)

            Dim old As Money = FindMoney(aMoney.Currency)
            If old Is Nothing Then
                fmonies.Add(aMoney)
                Return
            End If
            fmonies.Remove(old)
            Dim sum As IMoney = old.Add(aMoney)
            If (sum.IsZero) Then
                Return
            End If
            fmonies.Add(sum)
        End Sub

        Private Function Contains(ByVal aMoney As Money) As Boolean
            Dim m As Money = FindMoney(aMoney.Currency)
            Return m.Amount.Equals(aMoney.Amount)
        End Function

        Public Overloads Overrides Function Equals(ByVal anObject As Object) As Boolean
            If IsZero Then
                If TypeOf anObject Is IMoney Then
                    Dim aMoney As IMoney = anObject
                    Return aMoney.IsZero
                End If
            End If

            If TypeOf anObject Is MoneyBag Then
                Dim aMoneyBag As MoneyBag = anObject
                If Not aMoneyBag.fmonies.Count.Equals(fmonies.Count) Then
                    Return False
                End If

                For Each m As Money In fmonies
                    If Not aMoneyBag.Contains(m) Then
                        Return False
                    End If

                    Return True
                Next
            End If

            Return False
        End Function

        Private Function FindMoney(ByVal currency As String) As Money
            For Each m As Money In fmonies
                If m.Currency.Equals(currency) Then
                    Return m
                End If
            Next

            Return Nothing
        End Function

        Public Overrides Function GetHashCode() As Int32
            Dim hash As Int32 = 0
            For Each m As Money In fmonies
                hash += m.GetHashCode()
            Next
            Return hash
        End Function

        Public ReadOnly Property IsZero() As Boolean Implements IMoney.IsZero
            Get
                Return fmonies.Count.Equals(0)
            End Get
        End Property

        Public Function Multiply(ByVal factor As Integer) As IMoney Implements IMoney.Multiply
            Dim result As New MoneyBag
            If Not factor.Equals(0) Then
                For Each m As Money In fmonies
                    result.AppendMoney(m.Multiply(factor))
                Next
            End If
            Return result
        End Function

        Public Function Negate() As IMoney Implements IMoney.Negate
            Dim result As New MoneyBag
            For Each m As Money In fmonies
                result.AppendMoney(m.Negate())
            Next
            Return result
        End Function

        Private Function Simplify() As IMoney
            If fmonies.Count.Equals(1) Then
                Return fmonies(0)
            End If
            Return Me
        End Function


        Public Function Subtract(ByVal m As IMoney) As IMoney Implements IMoney.Subtract
            Return Add(m.Negate())
        End Function
    End Class

End Namespace
