WIP Resource event handling
[aquarium] / src / main / scala / gr / grnet / aquarium / computation / data / RoleHistory.scala
index 5a710e6..760d523 100644 (file)
@@ -37,6 +37,8 @@ package gr.grnet.aquarium.computation.data
 
 import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
 import scala.collection.immutable.{TreeMap, SortedMap}
+import scala.collection.mutable.ListBuffer
+import scala.annotation.tailrec
 
 /**
  *
@@ -48,24 +50,92 @@ case class RoleHistory(
                          * The head role is the most recent. The same rule applies for the tail.
                          */
                        roles: List[RoleHistoryItem]) {
-  def rolesByTimeslot: SortedMap[Timeslot, String] = {
-    TreeMap(roles.map(role => (role.timeslot, role.name)): _*)
+
+  def roleNamesByTimeslot: SortedMap[Timeslot, String] = {
+    TreeMap(roles.map(role ⇒ (role.timeslot, role.name)): _*)
   }
 
-  /**
-   * Insert the new role in front of the other ones and adjust the `validTo` limit of the previous role.
-   */
-  def addMostRecentRole(role: String, validFrom: Long) = {
-    val newItem = RoleHistoryItem(role, validFrom)
-    val newRoles = roles match {
-      case head :: tail ⇒
-        newItem :: head.withNewValidTo(validFrom) :: tail
+  def rolesByTimeslot: SortedMap[Timeslot, RoleHistoryItem] = {
+    TreeMap(roles.map(role ⇒ (role.timeslot, role)): _*)
+  }
+
+  def updateWithRole(role: String, validFrom: Long) = {
+    def fixValidTo(validFrom: Long, validTo: Long): Long = {
+      if(validTo == validFrom) {
+        // Since validTo is exclusive, make at least 1ms gap
+        validFrom + 1
+      } else {
+        validTo
+      }
+    }
 
+    val newItems = roles match {
       case Nil ⇒
-        newItem :: Nil
+        RoleHistoryItem(role, validFrom) :: Nil
+
+      case head :: tail ⇒
+        if(head.startsStrictlyAfter(validFrom)) {
+          // must search history items to find where this fits in
+          @tailrec
+          def check(allChecked: ListBuffer[RoleHistoryItem],
+                    lastCheck: RoleHistoryItem,
+                    toCheck: List[RoleHistoryItem]): List[RoleHistoryItem] = {
+
+            toCheck match {
+              case Nil ⇒
+                allChecked.append(RoleHistoryItem(role, validFrom, fixValidTo(validFrom, lastCheck.validFrom)))
+                allChecked.toList
+
+              case toCheckHead :: toCheckTail ⇒
+                if(toCheckHead.startsStrictlyAfter(validFrom)) {
+                  allChecked.append(toCheckHead)
+
+                  check(allChecked, toCheckHead, toCheckTail)
+                } else {
+                  allChecked.append(RoleHistoryItem(role, validFrom, fixValidTo(validFrom, lastCheck.validFrom)))
+                  allChecked.toList
+                }
+            }
+          }
+
+          val buffer = new ListBuffer[RoleHistoryItem]
+          buffer.append(head)
+          check(buffer, head, tail)
+        } else {
+          // assume head.validTo goes to infinity,
+          RoleHistoryItem(role, validFrom) :: head.copyWithValidTo(fixValidTo(head.validFrom, validFrom)) :: tail
+        }
     }
 
-    RoleHistory(newRoles)
+    RoleHistory(newItems)
+  }
+
+  /**
+   * Returns the first, chronologically, role.
+   */
+  def firstRole: Option[RoleHistoryItem] = {
+    rolesByTimeslot.valuesIterator.toList.lastOption
+  }
+
+  /**
+   * Returns the name of the first, chronologically, role.
+   */
+  def firstRoleName: Option[String] = {
+    roleNamesByTimeslot.valuesIterator.toList.lastOption
+  }
+
+  /**
+   * Returns the last, chronologically, role.
+   */
+  def lastRole: Option[RoleHistoryItem] = {
+    rolesByTimeslot.valuesIterator.toList.headOption
+  }
+
+  /**
+   * Returns the name of the last, chronologically, role.
+   */
+  def lastRoleName: Option[String] = {
+    roleNamesByTimeslot.valuesIterator.toList.headOption
   }
 }