##########################################################################
#
#  Copyright (c) 2013, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * 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.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 os
import weakref

import IECore

import Gaffer
import GafferUI

## The Bookmarks class provides a registry of named locations for use
# in Path UIs. To allow different gaffer applications to coexist in the
# same process, separate bookmarks are maintained per application.
class Bookmarks( object ) :

	## Use acquire() in preference to this constructor.
	def __init__( self, applicationRoot, pathType, category ) :

		self.__applicationRoot = weakref.ref( applicationRoot )
		self.__pathType = pathType
		self.__category = category

	## Acquires a set of bookmarks for the specified target. Bookmarks are
	# grouped according to the type of path they can be applied to and to an
	# arbitrary category. The None category is special - bookmarks added to this
	# category are available in all categories with the same path type.
	#
	# Bookmarks are stored on a per-application basis but target can be any
	# of the following :
	#
	#  - An instance of Gaffer.Application
	#  - An instance of Gaffer.ApplicationRoot
	#  - An instance of Gaffer.GraphComponent. In this case, the bookmarks
	#     of the ApplicationRoot ancestor of target are returned, with None
	#     being returned in the absence of such an ancestor.
	#  - An of instance of GafferUI.Widget. In this case, an instance of
	#     of EditorWidget or ScriptWindow will be sought, and the application
	#     determined using the attached script. This too may return None if
	#     no application can be found.
	#  - A tuple or list, containing potential targets in the above form. Each
	#     is tried in turn until bookmarks are found - this is useful when both
	#     a GraphComponent and a Widget are available, but it is not known that
	#     they each have a suitable ancestor for bookmark acquisition.
	#
	@classmethod
	def acquire( cls, target, pathType=Gaffer.FileSystemPath, category=None ) :

		if isinstance( target, ( tuple, list ) ) :
			for t in target :
				result = cls.acquire( t, pathType, category )
				if result is not None :
					return result
			return None

		if isinstance( target, Gaffer.Application ) :
			applicationRoot = target.root()
		elif isinstance( target, Gaffer.ApplicationRoot ) :
			applicationRoot = target
		elif isinstance( target, Gaffer.GraphComponent ) :
			applicationRoot = target.ancestor( Gaffer.ApplicationRoot )
		else :
			assert( isinstance( target, GafferUI.Widget ) )
			scriptWidget = None
			if isinstance( target, ( GafferUI.EditorWidget, GafferUI.ScriptWindow ) ) :
				scriptWidget = target
			else :
				scriptWidget = target.ancestor( GafferUI.EditorWidget )
				if scriptWidget is None :
					scriptWidget = target.ancestor( GafferUI.ScriptWindow )

			if scriptWidget is None :
				# needed to find bookmarks for floating op windows
				# in the browser app. ideally we'd have a more general
				# mechanism for finding scriptWidget in the closest
				# descendant-of-an-ancestor.
				window = target
				while window is not None :
					window = window.ancestor( GafferUI.Window )
					if window is not None and isinstance( window.getChild(), GafferUI.EditorWidget ) :
						scriptWidget = window.getChild()
						break

			if scriptWidget is not None :
				applicationRoot = scriptWidget.scriptNode().ancestor( Gaffer.ApplicationRoot )
			else :
				applicationRoot = None

		if applicationRoot is None :
			return None

		return Bookmarks( applicationRoot, pathType, category )

	## Adds a bookmark. If persistent is True, then the bookmark
	# will be saved in the application preferences and restored
	# when the application next runs. The path passed may either
	# be a string or a callable which takes the optional forWidget
	# argument passed to get() and returns a string - this latter
	# option makes it possible to define context sensitive bookmarks.
	def add( self, name, path, persistent=False ) :

		assert( isinstance( path, basestring ) or ( callable( path ) and not persistent ) )

		# backwards compatibility with old mechanism for storing recents -
		# convert to new form.
		if name.startswith( "Recent/" ) :
			self.addRecent( path )
			return

		s = self.__storage( self.__category )