from collections import namedtuple

from ...util import ordered
from . import defaults

Revert = namedtuple("Revert", ['reverting', 'reverteds', 'reverted_to'])
"""
Represents a revert event.  This class behaves like 
:class:`collections.namedtuple`.  Note that the datatypes of `reverting`, 
`reverteds` and `reverted_to` is not specified since those types will depend
on the revision data provided during revert detection.  

:Members:
	**reverting**
		The reverting revision data : `mixed`
	**reverteds**
		The reverted revision data (ordered chronologically) : list( `mixed` )
	**reverted_to**
		The reverted-to revision data : `mixed`
"""

class Detector(ordered.HistoricalMap):
	"""
	Detects revert events in a stream of revisions (to the same page) based on 
	matching checksums.  To detect reverts, construct an instance of this class and call 
	:meth:`process` in chronological order (``direction == "newer"``).
	
	See `<https://meta.wikimedia.org/wiki/R:Identity_revert>`_
	
	:Parameters:
		radius : int
			the maximum revision distance that a revert can span.
	
	:Example:
		>>> from mw.lib import reverts
		>>> detector = reverts.Detector()
		>>> 
		>>> detector.process("aaa", {'rev_id': 1})
		>>> detector.process("bbb", {'rev_id': 2})
		>>> detector.process("aaa", {'rev_id': 3})
		Revert(reverting={'rev_id': 3}, reverteds=[{'rev_id': 2}], reverted_to={'rev_id': 1})
		>>> detector.process("ccc", {'rev_id': 4})
		
	"""
	
	def __init__(self, radius=defaults.RADIUS):
		"""
		:Parameters:
			radius : int
				the maximum revision distance that a revert can span.
		"""
		super().__init__(maxlen=radius+1)
		
	def process(self, checksum, revision=None):
		"""
		Process a new revision and detect a revert if it occurred.  Note that 
		you can pass whatever you like as `revision` and it will be returned in 
		the case that a revert occurs.
		
		:Parameters:
			checksum : str
				Any identity-machable string-based hash of revision content
			revision : `mixed`
				Revision data.  Note that any data will just be returned in the
				case of a revert.
		
		:Returns:
			a :class:`~mw.lib.reverts.Revert` if one occured or `None`
		"""
		revert = None
		
		if checksum in self: #potential revert
			
			reverteds = list(self.up_to(checksum))
			
			if len(reverteds) > 0: # If no reverted revisions, this is a noop
				revert = Revert(revision, reverteds, self[checksum])
			
		self.insert(checksum, revision)
		return revert
	
