50c8f4c243fe59666437bee404dfbd3a158120bd
[aquarium] / src / main / scala / gr / grnet / aquarium / charging / reason / ChargingReason.scala
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.aquarium.charging.reason
37
38 import gr.grnet.aquarium.computation.BillingMonthInfo
39 import gr.grnet.aquarium.event.model.im.IMEventModel
40 import gr.grnet.aquarium.util.shortClassNameOf
41
42 /**
43  * Provides information explaining the reason Aquarium calculated a new
44  * [[gr.grnet.aquarium.computation.state.UserState]].
45  */
46 case class ChargingReason(
47     details: Map[String, Any],
48     billingMonthInfo: Option[BillingMonthInfo],
49     parentReason: Option[ChargingReason]
50 ) {
51
52   require(
53     details.contains(ChargingReason.Names.name),
54     "No name present in the details of %s".format(shortClassNameOf(this))
55   )
56
57   private[this] def booleanFromDetails(name: String, default: Boolean) = {
58     details.get(name) match {
59       case Some(value) ⇒
60         value.toString.toBoolean
61
62       case _ ⇒
63         false
64     }
65   }
66
67   def calculateCreditsForImplicitlyTerminated: Boolean =
68     booleanFromDetails(ChargingReason.Names.calculateCreditsForImplicitlyTerminated, false)
69
70   def forBillingMonthInfo(bmi: BillingMonthInfo) = {
71     copy(
72       parentReason = Some(this),
73       billingMonthInfo = Some(bmi)
74     )
75   }
76
77   def name: String = {
78     // This must be always present
79     details.get(ChargingReason.Names.name).map(_.toString).getOrElse("<unknown>")
80   }
81 }
82
83 object ChargingReason {
84
85   object Names {
86     final val name = "name"
87
88     final val imEvent = "imEvent"
89     final val forWhenMillis = "forWhenMillis"
90
91     final val calculateCreditsForImplicitlyTerminated = "calculateCreditsForImplicitlyTerminated"
92   }
93
94 }
95
96 /**
97  * Used when the very first user state is saved.
98  */
99 object InitialUserStateSetup {
100   def name = "InitialUserStateSetup"
101
102   /**
103    * When the user state is initially set up.
104    */
105   def apply(parentReason: Option[ChargingReason]) = {
106     ChargingReason(
107       Map(
108         ChargingReason.Names.name -> name
109       ),
110       None,
111       parentReason
112     )
113   }
114 }
115
116 object InitialUserActorSetup {
117   def name = "InitialUserActorSetup"
118
119   /**
120    * When the user processing unit (actor) is initially set up.
121    */
122   def apply() = {
123     ChargingReason(
124       Map(
125         ChargingReason.Names.name -> name
126       ),
127       None,
128       None
129     )
130   }
131 }
132
133 object NoSpecificChargingReason {
134   def name = "NoSpecificChargingReason"
135
136   /**
137    * A calculation made for no specific reason. Can be for testing, for example.
138    */
139   def apply() = {
140     ChargingReason(
141       Map(
142         ChargingReason.Names.name -> name
143       ),
144       None,
145       None
146     )
147   }
148 }
149
150 object MonthlyBillChargingReason {
151   def name = "MonthlyBillChargingReason"
152
153   /**
154    * An authoritative calculation for the billing period.
155    */
156   def apply(parentReason: ChargingReason, billingMongthInfo: BillingMonthInfo) = {
157     ChargingReason(
158       Map(
159         ChargingReason.Names.name -> name,
160         ChargingReason.Names.calculateCreditsForImplicitlyTerminated -> true.toString
161       ),
162       Some(billingMongthInfo),
163       Some(parentReason)
164     )
165   }
166 }
167
168 object RealtimeChargingReason {
169   def name = "RealtimeChargingReason"
170
171   /**
172    * Used for the real-time billing calculation.
173    */
174   def apply(parentReason: Option[ChargingReason], forWhenMillis: Long) = {
175     ChargingReason(
176       Map(
177         ChargingReason.Names.name -> name,
178         ChargingReason.Names.forWhenMillis -> forWhenMillis.toString
179       ),
180       None,
181       parentReason
182     )
183   }
184 }
185
186 object IMEventArrival {
187   def name = "IMEventArrival"
188
189   def apply(imEvent: IMEventModel) = {
190     ChargingReason(
191       Map(
192         ChargingReason.Names.name -> name,
193         ChargingReason.Names.imEvent -> imEvent.toJsonString
194       ),
195       None,
196       None
197     )
198   }
199 }