# -*- Mode: python -*-
#
# Copyright (C) 2002-2003 Mark Ferrell <xrxgrok@yahoo.com>
# Copyright (C) 2003 Charles Duffy <cduffy@spamcop.net>
#
# -----------------------------------------------------------------------
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   1. Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#
#   2. Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
# -----------------------------------------------------------------------

import re, types, sys

_re_set_string = r'[0-9]+|[a-zA-Z][a-zA-Z0-9_-]+'
_re_branch_string = r'[a-zA-Z][a-zA-Z0-9_-]+'
_re_set_range = re.compile(r'^(?:(%s)\.)?(%s)?:(:)?(%s)?$' % (_re_branch_string, _re_set_string, _re_set_string))
_re_set_list = re.compile(r'^(?:(%s)\.)?((?:%s)(?:,(?:%s))*)$' % (_re_branch_string, _re_set_string, _re_set_string))
_re_set_head = re.compile(r'^([a-zA-Z][a-zA-Z0-9_-]+\.)$')

class RangeArgParser(object):
	"""Describes a set of changesets or a mechanism for selecting a set of
	changesets to operate on"""
	def __init__(self, catalog):
		self.catalog = catalog
		self.changesets = []
		self.branches=None
		self.authors=[]
		self.branch='MAIN'
		self.defaultAllChangesets=False
	def parseArg(self, val):
		match = _re_set_range.match(val)
		if match:
			#sys.stderr.write("Matched range\n")
			(branch, start, exclude, end) = match.groups()
			if branch is not None: self.branch = branch
			self.changesets.append((start, end, exclude))
			return True

		match = _re_set_list.match(val)
		if match:
			#sys.stderr.write("Matched list\n")
			(branch, values) = match.groups()
			if branch is not None: self.branch = branch
			self.changesets.append(values.split(','))
			return True

		match = _re_set_head.match(val)
		if match:
			#sys.stderr.write("Matched head\n")
			head = match.groups()
			self.changesets.append(head[0][:-1])
			return True
		return False
	def getBranch(self):
		return self.catalog.getBranch(self.branch)
	def getChangesetIterator(self):
		cslist = self.changesets[:]
		branch = self.getBranch()
		if self.defaultAllChangesets == True and len(cslist) == 0:
			cslist.append(('1', len(branch), None))
		for set in cslist:
			# Range of changesets
			if type(set) is types.TupleType:
				# gather our endpoints
				if not set[0]: # start
					start = 1
				else:
					if type(set[0]) is types.IntType or set[0].isdigit():
						start = int(set[0])
					else:
						tag = set[0]
						start = branch.getChangesetIdForTag(tag)

				if not set[1]: # end
					end = len(branch)
				else:
					if type(set[1]) is types.IntType or set[1].isdigit():
						end = int(set[1])
					else:
						tag = set[1]
						end = branch.getChangesetIdForTag(tag)

				if set[2]: # exclude
					if set[0]: start += 1
					if set[1]: end -= 1

				for index in xrange(start,end+1):
					changeset = branch.getChangeset(index)
					yield changeset
					
			# List of changesets
			elif type(set) == types.ListType:
				for entry in set:
					if not entry: continue
					if not entry.isdigit():
						if self.catalog.hasBranch(entry):
							## display each changeset in the given branch?
							for cs in self.catalog.getBranch(entry).getChangesetIterator():
								yield cs
						else:
							## display the given tag (presumably in the default branch)
							changeset = branch.getChangeset(branch.getChangesetIdForTag(entry))
							yield changeset
					else:
						changeset = branch.getChangeset(int(entry))
						yield changeset

			# Head of branch request
			else:
				branch = self.catalog.getBranch(set)
				if not branch:
					sys.stderr.write('cscvs: unable to find branch: "%s"\n' % set)
					continue
				yield branch.getChangeset(len(branch))
	def getChangesets(self):
		return list(self.getChangesetIterator())
