From 7e29abf2e62892ec354d2a45d3bee8388d8d23bb Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Sun, 10 May 2020 15:36:11 +0200
Subject: Add a concept of "history gap"

---
 poezio/text_buffer.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 82 insertions(+), 1 deletion(-)

diff --git a/poezio/text_buffer.py b/poezio/text_buffer.py
index 3b3ac051..15869676 100644
--- a/poezio/text_buffer.py
+++ b/poezio/text_buffer.py
@@ -19,9 +19,15 @@ from typing import (
     Tuple,
     Union,
 )
+from dataclasses import dataclass
 from datetime import datetime
 from poezio.config import config
-from poezio.ui.types import Message, BaseMessage
+from poezio.ui.types import (
+    BaseMessage,
+    Message,
+    MucOwnJoinMessage,
+    MucOwnLeaveMessage,
+)
 
 if TYPE_CHECKING:
     from poezio.windows.text_win import TextWin
@@ -35,6 +41,15 @@ class AckError(Exception):
     pass
 
 
+@dataclass
+class HistoryGap:
+    """Class representing a period of non-presence inside a MUC"""
+    leave_message: Optional[MucOwnLeaveMessage]
+    join_message: Optional[MucOwnJoinMessage]
+    last_timestamp_before_leave: Optional[datetime]
+    first_timestamp_after_join: Optional[datetime]
+
+
 class TextBuffer:
     """
     This class just keep trace of messages, in a list with various
@@ -58,6 +73,72 @@ class TextBuffer:
     def add_window(self, win) -> None:
         self._windows.append(win)
 
+    def find_last_gap_muc(self) -> Optional[HistoryGap]:
+        """Find the last known history gap contained in buffer"""
+        leave, join = None, None
+        for i, item in enumerate(reversed(self.messages)):
+            if isinstance(item, MucOwnLeaveMessage):
+                leave = (i, item)
+                break
+            if isinstance(item, MucOwnJoinMessage):
+                join = (i, item)
+        if join and leave:  # Skip if we find a message in the interval
+            real_leave = len(self.messages) - leave[0] - 1
+            real_join = len(self.messages) - join[0] - 1
+            for i in range(real_leave, real_join, 1):
+                if isinstance(self.messages[i], Message):
+                    return None
+        elif not (join or leave):
+            return None
+        if leave is None:
+            last_timestamp, leave_msg = None, None
+        else:
+            last_timestamp = None
+            leave_msg = leave[1]
+            for i in range(len(self.messages) - leave[0] - 1, 0, -1):
+                if isinstance(self.messages[i], Message):
+                    last_timestamp = self.messages[i].time
+                    break
+        first_timestamp = datetime.now()
+        if join is None:
+            join_msg = None
+        else:
+            join_msg = join[1]
+            for i in range(len(self.messages) - join[0], len(self.messages)):
+                msg = self.messages[i]
+                if isinstance(msg, Message) and msg.time < first_timestamp:
+                    first_timestamp = msg.time
+                    break
+        return HistoryGap(
+            leave_message=leave_msg,
+            join_message=join_msg,
+            last_timestamp_before_leave=last_timestamp,
+            first_timestamp_after_join=first_timestamp,
+        )
+
+    def get_gap_index(self, gap: HistoryGap) -> Optional[int]:
+        """Find the first index to insert into inside a gap"""
+        if gap.leave_message is None:
+            return 0
+        for i, msg in enumerate(self.messages):
+            if msg is gap.leave_message:
+                return i + 1
+        return None
+
+    def add_history_messages(self, messages: List[Message], gap: Optional[HistoryGap] = None) -> None:
+        """Insert history messages at their correct place """
+        index = 0
+        if gap is not None:
+            index = self.get_gap_index(gap)
+        if index is None:  # Not sure what happened, abort
+            return
+        for message in messages:
+            self.messages.insert(index, message)
+            index += 1
+            log.debug('inserted message: %s', message)
+        for window in self._windows:  # make the associated windows
+            window.rebuild_everything(self)
+
     @property
     def last_message(self) -> Optional[BaseMessage]:
         return self.messages[-1] if self.messages else None
-- 
cgit v1.2.3