#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 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 sys, os

synonyms = ['co']
summary  = """Check out all files in a given changeset from the repository"""
usage = """Usage: cscvs checkout [branch.]cset destination
        [branch.]cset	Check out repository as of specified changeset/tag
			If the provided changeset descriptor evaluates to
			more than one changeset, only the first will be used.
	destination	Location to place the checked-out tree. Must not be
			previously existing."""

# cvs_get and add here are scaled down versions from totla.py; they should be
# factored out of here completely at soonest convenience [that is, *before*
# moving out of --experimental--].

def cvs_get(config, rev):
	pipe = os.popen("cvs -q update -p -r%s '%s'" % (rev.revision, rev.filename))
	file = pipe.read()
	pipe.close()
	return file

def add(config, rev):
	sys.stdout.write("%s [%s]\n" % (rev.filename, rev.revision))

	oldwd = os.getcwd()
	delta = cvs_get(config, rev)

	try:
		os.chdir(config.destdir)

		dirname = os.path.dirname(rev.filename)
		if len(dirname) and not os.path.exists(dirname):
			base = ''
			for dir in dirname.split('/'):
				base = os.path.join(base, dir)
				if os.path.exists(base): continue
				os.mkdir(base, 0755)

		# FIXME
		file = open(rev.filename, "w")
		file.write(delta)
		file.close()
		# FIXME

		os.utime(rev.filename, (rev.time, rev.time))

		dirname = os.path.dirname(rev.filename)
		while dirname:
			os.utime(dirname, (rev.time, rev.time))
			dirname = os.path.dirname(dirname)
	finally:
		os.chdir(oldwd)


def checkout(config):
	from RangeArgParser import RangeArgParser
	import StorageLayer
	SCM = StorageLayer.convertingImport('SCM')
	CVS = StorageLayer.convertingImport('CVS')

	catalog = SCM.Catalog.Catalog(config)
	selectedChangeset = RangeArgParser(catalog)

	if len(config.args) != 2:
		raise CVS.Usage

	if not selectedChangeset.parseArg(config.args[0]):
		raise CVS.Usage

	config.destdir = os.path.normpath(config.args[1])
	if os.path.exists(config.destdir):
		sys.stderr.write('Destination directory must not exist.\n')
		raise CVS.Usage

	if not os.path.exists(os.path.dirname(config.destdir)):
		sys.stderr.write('Parent of destination directory must exist.\n')
		raise CVS.Usage
	
	os.mkdir(config.destdir)

	try: changeset = selectedChangeset.getChangesetIterator().next()
	except StopIteration:
		sys.stderr.write('No changeset selected by expression given.\n')
		raise CVS.Usage

	for rev in changeset.getAllRevisionsIterator():
		if rev.type in (SCM.Revision.Revision.REMOVE,
		                SCM.Revision.Revision.PLACEHOLDER):
			continue
		elif rev.type == SCM.Revision.Revision.ADD or \
		    (rev.type == SCM.Revision.Revision.CHANGE and
		     rev.predecessor != None and
		     rev.predecessor.type in (SCM.Revision.Revision.PLACEHOLDER,
		                              SCM.Revision.Revision.REMOVE)):
			add(config, rev)
		elif rev.type == SCM.Revision.Revision.CHANGE:
			add(config, rev)
