#------------------------------------------------------------------------------
# Copyright (C) 2007 Richard W. Lincoln
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This software is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#------------------------------------------------------------------------------

""" Defines an ellipse component.

References:
    Jose.R.Fonseca, 'XDot', http://code.google.com/p/jrfonseca/wiki/XDot

"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from math import pi, sqrt

from enthought.traits.api import Instance, Float, Int, Bool, on_trait_change
from enthought.traits.ui.api import View, Item, Group
from enthought.enable.api import Component, Pointer
from enthought.kiva import FILL_STROKE

from pen import Pen

#------------------------------------------------------------------------------
#  "Ellipse" class:
#------------------------------------------------------------------------------

class Ellipse(Component):
    """ Component with Ellipse traits """

    #--------------------------------------------------------------------------
    #  "Ellipse" interface:
    #--------------------------------------------------------------------------

    # Pen used to draw the ellipse
    pen = Instance(Pen, desc="Pen instance with which to draw the ellipse")

    # X-axis coordinate of ellipse origin
    x_origin = Float(desc="x-axis coordinate of ellipse origin")

    # Y-axis coordinate of ellipse origin
    y_origin = Float(desc="y-axis coordinate of ellipse origin")

    # Width of the ellipse (semi-major axis)
    e_width = Float(desc="Ellipse width")

    # Height of the ellipse (semi-minor axis)
    e_height = Float(desc="Ellipse height")

    # Is the ellipse filled?
    filled = Bool(False, desc="Fill the ellipse")

    # The background color of this component.
    bgcolor = "transparent" #"fuchsia"

    #--------------------------------------------------------------------------
    #  Views:
    #--------------------------------------------------------------------------

    traits_view = View(
        Group(
            Item("pen", style="custom", show_label=False),
            label="Pen", show_border=True
        ),
        Item("x_origin"), Item("y_origin"),
        Item("e_width", label="Width"),
        Item("e_height", label="Height"),
        Item("filled")
    )

    #--------------------------------------------------------------------------
    #  Draw component on the graphics context:
    #--------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):
        """ Draws the component """

        x_origin = self.x_origin
        y_origin = self.y_origin

        gc.save_state()
        try:
#            self._draw_bounds(gc)
            gc.begin_path()
            gc.translate_ctm(x_origin, y_origin)
            gc.scale_ctm(self.e_width, self.e_height)
            gc.arc(0.0, 0.0, 1.0, 0, 2.0*pi)
            gc.close_path()

            # Draw stroke at same scale as graphics context
#            ctm = gc.get_ctm()
#            if hasattr(ctm, "__len__") and len(ctm) == 6:
#                scale = sqrt( (ctm[0]+ctm[1]) * (ctm[0]+ctm[1]) / 2.0 + \
#                              (ctm[2]+ctm[3]) * (ctm[2]+ctm[3]) / 2.0 )
#            elif hasattr(gc, "get_ctm_scale"):
#                scale = gc.get_ctm_scale()
#            else:
#                raise RuntimeError("Unable to get scale from GC.")

            gc.set_line_width(self.pen.line_width)
            gc.set_stroke_color(self.pen.color_)

            if self.filled:
                gc.set_fill_color(self.pen.fill_color_)
                gc.draw_path(FILL_STROKE)
            else:
                gc.stroke_path()
        finally:
            gc.restore_state()

    def is_in(self, point_x, point_y):
        """ Test if the point is within this ellipse """

        x = self.x_origin
        y = self.y_origin
        a = self.e_width#/2 # FIXME: Why divide by two
        b = self.e_height#/2

        return ((point_x-x)**2/(a**2)) + ((point_y-y)**2/(b**2)) < 1.0

    def _draw_bounds(self, gc):
        """ Draws the component bounds for testing purposes """

        dx, dy = self.bounds
        x, y = self.position
        gc.rect(x, y, dx, dy)
        gc.stroke_path()

    def normal_left_down(self, event):
        """ Handles left mouse button clicks in 'normal' mode """

        print "Ellipse selected at (%d, %d)" % (event.x, event.y)

#    def _position_changed(self, new):
#        """ Handles the position of the component changing.
#        """
#        x, y = new
#        self.x_origin = x + self.e_width
#        self.y_origin = y + self.e_height

    @on_trait_change("pen.+,x_origin,y_origin,e_width,e_height,filled,container")
    def _update(self):

        x_origin = self.x_origin
        y_origin = self.y_origin

        x = x_origin - (self.e_width)
        x2 = x_origin + (self.e_width)
        y = y_origin-(self.e_height)
        y2 = y_origin + (self.e_height)

        self.position = [x, y]
        # If bounds are set to 0, horizontal/vertical lines will not render.
        self.bounds = [ max(x2-x, 1), max(y2-y, 1) ]

        self.request_redraw()

#------------------------------------------------------------------------------
#  Stand-alone call:
#------------------------------------------------------------------------------

if __name__ == "__main__":
    from godot.component.component_viewer import ComponentViewer

    pen = Pen()
    ellipse = Ellipse(
#        filled=True,
        pen=pen, x_origin=200, y_origin=150, e_width=100, e_height=50,
#        bounds=[50, 50], position=[0, 0]

    )

    from enthought.enable.api import Container
    container = Container(
#        fit_window=False, auto_size=True,
        bounds=[200, 100], position=[100, 100],
        bgcolor="green")
    container.add( ellipse )

    ellipse.x_origin -= container.x
    ellipse.y_origin -= container.y

    viewer = ComponentViewer( component=container )

    from enthought.enable.primitives.api import Box
    box = Box(
        color="steelblue", border_color="darkorchid", border_size=1,
        bounds=[50, 50], position=[50, 50]
    )
    viewer.canvas.add(box)

    viewer.configure_traits()

# EOF -------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Copyright (C) 2009 Richard W. Lincoln
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This software is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#------------------------------------------------------------------------------

""" Defines actions for the Godot plug-in.
"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from os.path import exists, dirname, join

from enthought.io.api import File
from enthought.pyface.api import ImageResource, FileDialog, CANCEL, OK
from enthought.pyface.action.api import Action as PyFaceAction

from enthought.envisage.ui.action.api import Action, Group, Menu
from enthought.envisage.ui.workbench.api import WorkbenchActionSet

from wizard import NewDotGraphWizard

#------------------------------------------------------------------------------
#  Constants:
#------------------------------------------------------------------------------

IMAGE_LOCATION = join(dirname(__file__), "..", "ui", "images")

#------------------------------------------------------------------------------
#  "NewDotGraphAction" class:
#------------------------------------------------------------------------------

class NewDotGraphAction(PyFaceAction):
    """ An action for creating a new Dot graph.
    """

    #--------------------------------------------------------------------------
    #  "Action" interface:
    #--------------------------------------------------------------------------

    # A longer description of the action:
    description = "Create a new Dot graph"

    # The action"s name (displayed on menus/tool bar tools etc):
    name = "Graph"

    # A short description of the action used for tooltip text etc:
    tooltip = "Create a Dot graph"

    # The action's image (displayed on tool bar tools etc):
    image = ImageResource("graph", search_path=[IMAGE_LOCATION])

    #--------------------------------------------------------------------------
    #  "Action" interface:
    #--------------------------------------------------------------------------

    def perform(self, event):
        """ Perform the action.
        """
        wizard = NewDotGraphWizard(parent=self.window.control,
            window=self.window, title="New Graph")

        # Open the wizard
        if wizard.open() == OK:
            wizard.finished = True

#------------------------------------------------------------------------------
#  "GodotWorkbenchActionSet" class:
#------------------------------------------------------------------------------

class GodotWorkbenchActionSet(WorkbenchActionSet):
    """ A set of workbench related actions for the Godot plug-in.
    """

    #--------------------------------------------------------------------------
    #  "ActionSet" interface:
    #--------------------------------------------------------------------------

    # The action set"s globally unique identifier.
    id = "godot.plugin.workbench_action_set"

    menus = [ Menu(name="&New", path="MenuBar/File", group="OpenGroup",
        groups=["ContainerGroup", "ComponentGroup", "OtherGroup"]) ]

    actions = [
        Action(path="MenuBar/File/New", group="ComponentGroup",
            class_name="godot.plugin.action:NewDotGraphAction"),

        Action(path="Resource/New", group="ComponentGroup",
            class_name="godot.plugin.action:NewDotGraphAction") ]

# EOF -------------------------------------------------------------------------


""" William Park, 'Simple Recipes in Python', March 1999 """

from math import pow, hypot, atan2, pi, sqrt, cos
import cmath

def cbrt(x):
    """ cbrt(x) = x^{1/3},  if x >= 0
                = -|x|^{1/3},  if x < 0

    """

    if x >= 0:
        return pow(x, 1.0/3.0)
    else:
        return -pow(abs(x), 1.0/3.0)

def polar(x, y, deg=0):        # radian if deg=0; degree if deg=1
    """ Convert from rectangular (x,y) to polar (r,w)
        r = sqrt(x^2 + y^2)
        w = arctan(y/x) = [-\pi,\pi] = [-180,180]

    """

    if deg:
        return hypot(x, y), 180.0 * atan2(y, x) / pi
    else:
        return hypot(x, y), atan2(y, x)

def quadratic(a, b, c=None):
    """ x^2 + ax + b = 0  (or ax^2 + bx + c = 0)
    By substituting x = y-t and t = a/2, the equation reduces to
        y^2 + (b-t^2) = 0
    which has easy solution
        y = +/- sqrt(t^2-b)

    """

    if c: # (ax^2 + bx + c = 0)
        a, b = b / float(a), c / float(a)

    t = a / 2.0
    r = t**2 - b

    if r >= 0: # real roots
        y1 = sqrt(r)
    else: # complex roots
        y1 = cmath.sqrt(r)

    y2 = -y1

    return y1 - t, y2 - t

def cubic(a, b, c, d=None):
    """ x^3 + ax^2 + bx + c = 0  (or ax^3 + bx^2 + cx + d = 0)
    With substitution x = y-t and t = a/3, the cubic equation reduces to
        y^3 + py + q = 0,
    where p = b-3t^2 and q = c-bt+2t^3.  Then, one real root y1 = u+v can
    be determined by solving
        w^2 + qw - (p/3)^3 = 0
    where w = u^3, v^3.  From Vieta's theorem,
        y1 + y2 + y3 = 0
        y1 y2 + y1 y3 + y2 y3 = p
        y1 y2 y3 = -q,
    the other two (real or complex) roots can be obtained by solving
        y^2 + (y1)y + (p+y1^2) = 0

    """

    if d: # (ax^3 + bx^2 + cx + d = 0)
        a, b, c = b / float(a), c / float(a), d / float(a)

    t = a / 3.0
    p, q = b - 3 * t**2, c - b * t + 2 * t**3
    u, v = quadratic(q, -(p/3.0)**3)

    if type(u) == type(0j): # complex cubic root
        r, w = polar(u.real, u.imag)
        y1 = 2 * cbrt(r) * cos(w / 3.0)
    else: # real root
        y1 = cbrt(u) + cbrt(v)
    y2, y3 = quadratic(y1, p + y1**2)

    return y1 - t, y2 - t, y3 - t

if __name__ == "__main__":
    a = 0.02
    b = 0.5
    c = 2
    d = 21

    # -1.1842899222534518   +   6.707691983624522 i
    # -1.1842899222534518   -   6.707691983624522 i
    # -22.631420155493096

    print cubic(a, b, c, d)


# encoding: utf-8

try:
	from sqlalchemy import create_engine
	from sqlalchemy.orm import scoped_session, sessionmaker
except ImportError:  # pragma: no cover
	raise ImportError('Unable to import sqlalchemy; pip install sqlalchemy to fix this.')

from .util import redact_uri

log = __import__('logging').getLogger(__name__)

class SQLAlchemyConnection(object):
	"""SQLAlchemy database engine support for WebCore's DatabaseExtension."""

	def __init__(self, uri, alias=None, **config):
		"""Prepare SQLAlchemy configuration."""
		# def __init__(self, uri, session=None, metadata=None, session_opts=None, alias=None, **kw):

		config.setdefault('pool_recycle', 3600)

		self.uri = uri
		self.alias = alias
		self.config = config
		self.engine = None
		self.Session = None

	def __repr__(self):
		return '{self.__class__.__name__}({self.alias}, "{uri}")'.format(
				self = self,
				uri = redact_uri(self.uri),
			)

	def start(self, context):
		"""Construct the SQLAlchemy engine and session factory."""

		if __debug__:
			log.info("Connecting SQLAlchemy database layer.", extra=dict(
					uri = redact_uri(self.uri),
					config = self.config,
					alias = self.alias,
				))

		# Construct the engine.
		engine = self.engine = create_engine(self.uri, **self.config)

		# Construct the session factory.
		self.Session = scoped_session(sessionmaker(bind=engine))

		# Test the connection.
		engine.connect().close()

		# Assign the engine to our database alias.
		context.db[self.alias] = engine

	def prepare(self, context):
		"""Prepare a sqlalchemy session on the WebCore context"""

		# Assign the session factory to our database alias.
		context.db[self.alias] = self.Session

	def done(self, context):
		"""Close and clean up the request local session, if any."""

		context.db[self.alias].remove()

	def stop(self, context):
		"""Disconnect any hanging connections in the pool."""

		self.engine.dispose()


#------------------------------------------------------------------------------
#  Copyright (c) 2008 Richard W. Lincoln
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
#  IN THE SOFTWARE.
#------------------------------------------------------------------------------

""" Defines a view model for Graphs. """

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

import sys
from os.path import join, dirname, expanduser, isfile
import logging
import pickle

from enthought.traits.api import \
    HasTraits, Instance, File, Bool, Str, List, on_trait_change, \
    Float, Tuple, Property, Delegate, Code, Button

from enthought.traits.ui.api import \
    View, Handler, UIInfo, Group, Item, TableEditor, InstanceEditor, \
    Label, Tabbed, HGroup, VGroup, ModelView, FileEditor, StatusItem, \
    spring, TextEditor

from enthought.traits.ui.menu import NoButtons, OKCancelButtons, Separator
from enthought.pyface.api import error, confirm, YES, FileDialog, OK
from enthought.pyface.image_resource import ImageResource
from enthought.naming.unique_name import make_unique_name
from enthought.logger.api import add_log_queue_handler
from enthought.logger.log_queue_handler import LogQueueHandler

#------------------------------------------------------------------------------
#  Local imports:
#------------------------------------------------------------------------------

from godot.base_graph import BaseGraph
from godot.api import Graph, Cluster, Node, Edge, GodotDataParser, Subgraph

from godot.ui.graph_menu import menubar, toolbar
from godot.ui.graph_view import nodes_view, edges_view, attr_view, about_view
from godot.ui.graph_tree import graph_tree_editor

#------------------------------------------------------------------------------
#  Constants:
#------------------------------------------------------------------------------

frame_icon = ImageResource("dot.ico")

#------------------------------------------------------------------------------
#  "GraphViewModel" class:
#------------------------------------------------------------------------------

class GraphViewModel(ModelView):
    """ Defines a view model for Graphs.  """

    #--------------------------------------------------------------------------
    #  Trait definitions:
    #--------------------------------------------------------------------------

    # File path to to use for saving.
    save_file = File

    # Is the tree view of the network displayed?
    show_tree = Bool(True, desc="that the network tree view is visible")

    # All graphs, subgraphs and clusters.
#    all_graphs = Property(List(Instance(HasTraits)))
    all_graphs = Delegate("model")

    # Select graph when adding to the graph?
    select_graph = Bool(True)

    # Working graph instance.
    selected_graph = Instance(BaseGraph, allow_none=False)

    # Exit confirmation.
    prompt_on_exit = Bool(False, desc="exit confirmation request")

    # Representation of the graph in the Dot language.
    dot_code = Code

    # Parse the dot_code and replace the existing model.
    parse_dot_code = Button("Parse", desc="dot code parsing action that "
        "replaces the existing model.")

    #--------------------------------------------------------------------------
    #  Views:
    #--------------------------------------------------------------------------

    # Default model view.
    traits_view = View(
        HGroup(
            Item(
                name="model", editor=graph_tree_editor,
                show_label=False, id=".tree_editor",
                visible_when="show_tree==True", width=.14
            ),
            Item("model", show_label=False),
        ),
        id="graph_view_model.graph_view", title="Godot", icon=frame_icon,
        resizable=True, style="custom", width=.81, height=.81, kind="live",
        buttons=NoButtons, menubar=menubar,
#        toolbar=toolbar,
        dock="vertical",
#        statusbar=[StatusItem(name="status", width=0.5),
#            StatusItem(name="versions", width=200)]
    )

    # File selection view.
#    file_view = View(
#        Item(name="file", id="file"),#, editor=FileEditor(entries=6)),
#        id="graph_view_model.file_view", title="Select a file",
#        icon=frame_icon, resizable=True, width=.3, kind="livemodal",
#        buttons=OKCancelButtons
#    )

    # Graph selection view.
    all_graphs_view = View(
        Item(name   = "selected_graph",
             editor = InstanceEditor( name     = "all_graphs",
                                      editable = False),
             label  = "Graph"),
        Item("select_graph", label="Always ask?"),
        icon = frame_icon, kind = "livemodal", title = "Select a graph",
        buttons = OKCancelButtons, close_result = False
    )

    # Model view options view.
    options_view = View(
        Item("prompt_on_exit"),
        "_",
        Item("select_graph"),
        Item("selected_graph",
             enabled_when = "not select_graph",
             editor       = InstanceEditor( name     = "all_graphs",
                                            editable = False ),
             label        = "Graph" ),
        icon = frame_icon, kind = "livemodal", title = "Options",
        buttons = OKCancelButtons, close_result = True
    )

    # Text representation of the graph viewed in a text editor
    dot_code_view = View(
        Item("dot_code", show_label=False, style="custom"),
        Item("parse_dot_code", show_label=False),
        id="godot.view_model.dot_code",
        icon = frame_icon, kind = "livemodal",
        title = "Dot Code", resizable = True,
        buttons = [], height = .3, width = .3
    )

    #--------------------------------------------------------------------------
    #  Trait intialisers:
    #--------------------------------------------------------------------------

    def _selected_graph_default(self):
        """ Trait intialiser.
        """
        return self.model

    def _parse_dot_code_fired(self):
        """ Parses the dot_code string and replaces the existing model.
        """
        parser = GodotDataParser()
        graph  = parser.parse_dot_data(self.dot_code)
        if graph is not None:
            self.model = graph

    #--------------------------------------------------------------------------
    #  Event handlers:
    #--------------------------------------------------------------------------

    def _model_changed(self, old, new):
        """ Handles the model changing.
        """
        self.selected_graph = new

    #--------------------------------------------------------------------------
    #  Action handlers:
    #--------------------------------------------------------------------------

    def new_model(self, info):
        """ Handles the new Graph action. """

        if info.initialized:
            retval = confirm(parent  = info.ui.control,
                             message = "Replace existing graph?",
                             title   = "New Graph",
                             default = YES)
            if retval == YES:
                self.model = Graph()

    def open_file(self, info):
        """ Handles the open action. """

        if not info.initialized: return # Escape.

#        retval = self.edit_traits(parent=info.ui.control, view="file_view")

        dlg = FileDialog( action = "open",
            wildcard = "Graphviz Files (*.dot, *.xdot, *.txt)|"
                "*.dot;*.xdot;*.txt|Dot Files (*.dot)|*.dot|"
                "All Files (*.*)|*.*|")

        if dlg.open() == OK:
            parser = GodotDataParser()
            model = parser.parse_dot_file(dlg.path)
            if model is not None:
                self.model = model
            else:
                print "error parsing: %s" % dlg.path

            self.save_file = dlg.path

        del dlg

#            fd = None
#            try:
#                fd = open(self.file, "rb")
#                parser = DotParser()
#                self.model = parser.parse_dot_file(self.file)
##            except:
##                error(parent=info.ui.control, title="Load Error",
##                    message="An error was encountered when loading\nfrom %s"
##                    % self.file)
#            finally:
#                if fd is not None:
#                    fd.close()

    def save(self, info):
        """ Handles saving the current model to the last file.
        """
        save_file = self.save_file

        if not isfile(save_file):
            self.save_as(info)
        else:
            fd = None
            try:
                fd = open(save_file, "wb")
                dot_code = str(self.model)
                fd.write(dot_code)
            finally:
                if fd is not None:
                    fd.close()

    def save_as(self, info):
        """ Handles saving the current model to file.
        """
        if not info.initialized:
            return

#        retval = self.edit_traits(parent=info.ui.control, view="file_view")

        dlg = FileDialog( action = "save as",
            wildcard = "Graphviz Files (*.dot, *.xdot, *.txt)|" \
                "*.dot;*.xdot;*.txt|Dot Files (*.dot)|*.dot|" \
                "All Files (*.*)|*.*|")

        if dlg.open() == OK:
            fd = None
            try:
                fd = open(dlg.path, "wb")
                dot_code = str(self.model)
                fd.write(dot_code)

                self.save_file = dlg.path

            except:
                error(parent=info.ui.control, title="Save Error",
                      message="An error was encountered when saving\nto %s"
                      % self.file)

            finally:
                if fd is not None:
                    fd.close()

        del dlg

    def configure_graph(self, info):
        """ Handles display of the graph dot traits.
        """
        if info.initialized:
            self.model.edit_traits(parent=info.ui.control,
                kind="live", view=attr_view)

    def configure_nodes(self, info):
        """ Handles display of the nodes editor.
        """
        if info.initialized:
            self.model.edit_traits(parent=info.ui.control,
                kind="live", view=nodes_view)

    def configure_edges(self, info):
        """ Handles display of the edges editor.
        """
        if info.initialized:
            self.model.edit_traits(parent=info.ui.control,
                kind="live", view=edges_view)

    def about_godot(self, info):
        """ Handles displaying a view about Godot.
        """
        if info.initialized:
            self.edit_traits(parent=info.ui.control,
                kind="livemodal", view=about_view)

    def add_node(self, info):
        """ Handles adding a Node to the graph.
        """
        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is None:
            return

        IDs = [v.ID for v in graph.nodes]
        node = Node(ID=make_unique_name("node", IDs))
        graph.nodes.append(node)

        retval = node.edit_traits(parent=info.ui.control, kind="livemodal")

        if not retval.result:
            graph.nodes.remove(node)

    def add_edge(self, info):
        """ Handles adding an Edge to the graph.
        """
        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is None:
            return

        n_nodes = len(graph.nodes)
        IDs = [v.ID for v in graph.nodes]

        if n_nodes == 0:
            tail_node = Node(ID=make_unique_name("node", IDs))
            head_name = make_unique_name("node", IDs + [tail_node.ID])
            head_node = Node(ID=head_name)
        elif n_nodes == 1:
            tail_node = graph.nodes[0]
            head_node = Node(ID=make_unique_name("node", IDs))
        else:
            tail_node = graph.nodes[0]
            head_node = graph.nodes[1]

        edge = Edge(tail_node, head_node, _nodes=graph.nodes)

        retval = edge.edit_traits(parent=info.ui.control, kind="livemodal")

        if retval.result:
            graph.edges.append(edge)

    def add_subgraph(self, info):
        """ Handles adding a Subgraph to the main graph.
        """
        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is not None:
            subgraph = Subgraph()#root=graph, parent=graph)
            retval = subgraph.edit_traits(parent = info.ui.control,
                                          kind   = "livemodal")
            if retval.result:
                graph.subgraphs.append(subgraph)

    def add_cluster(self, info):
        """ Handles adding a Cluster to the main graph. """

        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is not None:
            cluster = Cluster()#root=graph, parent=graph)
            retval = cluster.edit_traits(parent = info.ui.control,
                                         kind   = "livemodal")
            if retval.result:
                graph.clusters.append(cluster)

    def _request_graph(self, parent=None):
        """ Displays a dialog for graph selection if more than one exists.
            Returns None if the dialog is canceled.
        """

        if (len(self.all_graphs) > 1) and (self.select_graph):
            retval = self.edit_traits(parent = parent,
                                      view   = "all_graphs_view")
            if not retval.result:
                return None

        if self.selected_graph is not None:
            return self.selected_graph
        else:
            return self.model

    def toggle_tree(self, info):
        """ Handles displaying the tree view """

        if info.initialized:
            self.show_tree = not self.show_tree

    def godot_options(self, info):
        """ Handles display of the options menu. """

        if info.initialized:
            self.edit_traits( parent = info.ui.control,
                              kind   = "livemodal",
                              view   = "options_view" )

    def configure_dot_code(self, info):
        """ Handles display of the dot code in a text editor.
        """
        if not info.initialized:
            return

        self.dot_code = str(self.model)
        retval = self.edit_traits( parent = info.ui.control,
                                   kind   = "livemodal",
                                   view   = "dot_code_view" )
#        if retval.result:
#            parser = DotParser()
#            graph  = parser.parse_dot_data(self.dot_code)
#            if graph is not None:
#                self.model = graph

    #---------------------------------------------------------------------------
    #  Handle the user attempting to exit Godot:
    #---------------------------------------------------------------------------

    def on_exit(self, info):
        """ Handles the user attempting to exit Godot.
        """
        if self.prompt_on_exit:# and (not is_ok):
            retval = confirm(parent  = info.ui.control,
                             message = "Exit Godot?",
                             title   = "Confirm exit",
                             default = YES)
            if retval == YES:
                self._on_close( info )
        else:
            self._on_close( info )

#------------------------------------------------------------------------------
#  Stand-alone call:
#------------------------------------------------------------------------------

if __name__ == "__main__":
    import sys
    logger = logging.getLogger()
    logger.addHandler(logging.StreamHandler(sys.stdout))
    logger.setLevel(logging.DEBUG)

    graph = Graph(ID="G")
#    sg1 = Subgraph(ID="SG1")
#    graph.subgraphs.append(sg1)
#    sg2 = Subgraph(ID="SG2")
#    sg1.subgraphs.append(sg2)
#
#    n1 = Node(ID="N1")
#    sg2.nodes = [n1]

    graph.add_node("node1")
    graph.add_node("node2")

    view_model = GraphViewModel(model=graph)
    view_model.configure_traits()

# EOF -------------------------------------------------------------------------


#------------------------------------------------------------------------------
#  Copyright (c) 2009 Richard W. Lincoln
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
#  IN THE SOFTWARE.
#------------------------------------------------------------------------------

""" Defines utilities for Godot.
"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from __future__ import with_statement

import os

from enthought.traits.api import HasTraits, Enum

from enthought.kiva.fonttools.font import str_to_font

from godot.component.api import Ellipse, Text, Polygon, BSpline

#------------------------------------------------------------------------------
#  File extension for load/save protocol mapping.
#------------------------------------------------------------------------------

known_extensions = {
    'xml': 'xml',
    'dot': 'dot',
    'pkl': 'pickle' }

#------------------------------------------------------------------------------
#   "Serializable" class:
#------------------------------------------------------------------------------

class Serializable ( HasTraits ):
    """ Class that implements shortcuts to serialize an object.

        Serialization is done by various formats. At the moment, only 'pickle'
        is supported.

        @author: Tom Schaul, tom@idsia.ch; Justin Bayer, bayerj@in.tum.de
    """

    format = Enum("pickle")

    def save_to_file_like(self, flo, format=None, **kwargs):
        """ Save the object to a given file like object in the given format.
        """
        format = self.format if format is None else format
        save = getattr(self, "save_%s" % format, None)
        if save is None:
            raise ValueError("Unknown format '%s'." % format)
        save(flo, **kwargs)

    @classmethod
    def load_from_file_like(cls, flo, format=None):
        """ Load the object to a given file like object with the given
            protocol.
        """
        format = self.format if format is None else format
        load = getattr(cls, "load_%s" % format, None)
        if load is None:
            raise ValueError("Unknown format '%s'." % format)
        return load(flo)

    def save_to_file(self, filename, format=None, **kwargs):
        """ Save the object to file given by filename.
        """
        if format is None:
            # try to derive protocol from file extension
            format = format_from_extension(filename)
        with file(filename, 'wb') as fp:
            self.save_to_file_like(fp, format, **kwargs)

    @classmethod
    def load_from_file(cls, filename, format=None):
        """ Return an instance of the class that is saved in the file with the
            given filename in the specified format.
        """
        if format is None:
            # try to derive protocol from file extension
            format = format_from_extension(filename)
        with file(filename,'rbU') as fp:
            obj = cls.load_from_file_like(fp, format)
            obj.filename = filename
            return obj

    def save_pickle(self, flo, protocol=0):
        pickle.dump(self, flo, protocol)

    @classmethod
    def load_pickle(cls, flo):
        return pickle.load(flo)

#------------------------------------------------------------------------------
#  Tries to infer a protocol from the file extension.
#------------------------------------------------------------------------------

def format_from_extension(fname):
    """ Tries to infer a protocol from the file extension.
    """
    _base, ext = os.path.splitext(fname)
    if not ext:
        return None
    try:
        format = known_extensions[ext.replace('.','')]
    except KeyError:
        format = None
    return format

#------------------------------------------------------------------------------
#  Position components at the origin.
#------------------------------------------------------------------------------

def move_to_origin(components):
    """ Components are positioned relative to their container. Use this
        method to position the bottom-left corner of the components at
        the origin.
    """
    for component in components:
        if isinstance(component, Ellipse):
            component.x_origin = component.e_width
            component.y_origin = component.e_height

        elif isinstance(component, (Polygon, BSpline)):
            min_x = min( [t[0] for t in component.points] )
            min_y = min( [t[1] for t in component.points] )

            component.points = [
                ( p[0]-min_x, p[1]-min_y ) for p in component.points
            ]

        elif isinstance(component, Text):
            font = str_to_font( str(component.pen.font) )
            component.text_x = 0#-( component.text_w / 2 )
            component.text_y = 0#-( font.size / 2 )

# EOF -------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Copyright (C) 2007 Richard W. Lincoln
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This software is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#------------------------------------------------------------------------------

""" Defines a text component.

References:
    Jose.R.Fonseca, 'XDot', http://code.google.com/p/jrfonseca/wiki/XDot

"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from math import sqrt

from enthought.traits.api import \
    Instance, Float, Int, String, Trait, on_trait_change

from enthought.traits.ui.api import View, Item, Group
from enthought.enable.api import Component
#from enthought.kiva import Font as KivaFont
#from enthought.kiva import MODERN
from enthought.kiva.fonttools.font import str_to_font
#from enthought.kiva import Font, MODERN

from pen import Pen

#------------------------------------------------------------------------------
#  "Text" class:
#------------------------------------------------------------------------------

class Text(Component):
    """ Component with text traits """

    #--------------------------------------------------------------------------
    #  "Text" interface:
    #--------------------------------------------------------------------------

    # The background color of this component.
    bgcolor = "transparent"#"fuchsia"

    # Pen for drawing text
    pen = Instance(Pen, desc="pen instance with which to draw the text")

    # X-axis coordinate
    text_x = Float(desc="x-axis coordinate")

    # Y-axis coordinate
    text_y = Float(desc="y-axis coordinate")

    # Text justification
    justification = Int(-1, desc="(LEFT, CENTER, RIGHT = -1, 0, 1)")
#    justification = Trait("Left", {"Left": -1, "Centre": 0, "Right": 1})

    # Width of the text
    text_w = Float(desc="width of the text as computed by the library")

    # Text to be drawn
    text = String(desc="text")

    #--------------------------------------------------------------------------
    #  Views:
    #--------------------------------------------------------------------------

    traits_view = View(
        Group(
            Item("pen", style="custom", show_label=False),
            label="Pen", show_border=True
        ),
        Item("text_x"), Item("text_y"), Item("text_w"),
        Item("justification"), Item("text")
    )

    #--------------------------------------------------------------------------
    #  Draw component on the graphics context:
    #--------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):
        """ Draws the component """

        gc.save_state()
        try:
            # Specify the font
            font = str_to_font(str(self.pen.font))
            gc.set_font(font)

            gc.set_fill_color(self.pen.color_)

            x = self.text_x - ( self.text_w / 2 )
            y = self.text_y - ( font.size / 2 )

            # Show text at the same scale as the graphics context
            ctm = gc.get_ctm()
            if hasattr(ctm, "__len__") and len(ctm) == 6:
                scale = sqrt( (ctm[0] + ctm[1]) * (ctm[0] + ctm[1]) / 2.0 + \
                              (ctm[2] + ctm[3]) * (ctm[2] + ctm[3]) / 2.0 )
            elif hasattr(gc, "get_ctm_scale"):
                scale = gc.get_ctm_scale()
            else:
                raise RuntimeError("Unable to get scale from GC.")
            x *= scale
            y *= scale
            gc.show_text_at_point(self.text, x, y)
        finally:
            gc.restore_state()

    @on_trait_change("pen.+,text_x,text_y,text_w,justification,text")
    def _update(self):
        if self.pen is None:
            return

        x = self.text_x - (self.text_w / 2)
        x2 = x + self.text_w

        font = str_to_font( str(self.pen.font) )

        y = self.text_y - (font.size / 2)
        y2 = y + font.size

        self.position = [x, y]
        # If bounds are set to 0, horizontal/vertical lines will not render.
        self.bounds = [max(x2 - x, 1), max(y2 - y, 1)]

        self.request_redraw()

    def normal_left_down(self, event):
        print "Text [%s] selected at (%d, %d)" % (self.text, event.x, event.y)

#------------------------------------------------------------------------------
#  Stand-alone call:
#------------------------------------------------------------------------------

if __name__ == "__main__":
    from godot.component.component_viewer import ComponentViewer
    from enthought.enable.api import Container

    text = Text(
        pen=Pen(), text="Foo",
        text_x=50, text_y=50, text_w=30
    )

    container = Container(
#        fit_window=False, auto_size=True,
        bounds=[30, 10], position=[50, 50],
        bgcolor="green")
    container.add( text )

    viewer = ComponentViewer( component=text )

    viewer.configure_traits()

# EOF -------------------------------------------------------------------------


#------------------------------------------------------------------------------
#  Copyright (c) 2008 Richard W. Lincoln
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
#  IN THE SOFTWARE.
#------------------------------------------------------------------------------

""" Defines traits used in more than on graph element.
"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from enthought.traits.api import \
    HasTraits, Color, Str, Enum, Float, Font, Any, Bool, Int, File, Trait, \
    List, Tuple, ListStr, Property, Either, Regex

from enthought.enable.colors import ColorTrait

#------------------------------------------------------------------------------
#  Syntactically concise alias trait:
#------------------------------------------------------------------------------

def Alias(name, **metadata):
    """ Syntactically concise alias trait but creates a pair of lambda
    functions for every alias you declare.

    class MyClass(HasTraits):
        line_width = Float(3.0)
        thickness = Alias("line_width")

    """

    return Property(lambda obj: getattr(obj, name),
                    lambda obj, val: setattr(obj, name, val),
                    **metadata)

#------------------------------------------------------------------------------
#  Syntactically verbose alias trait:
#------------------------------------------------------------------------------

def _get_synced ( self, name ):
    return getattr( self, self.trait( name ).sync_to )

def _set_synced ( self, name, value ):
    setattr( self, self.trait( name ).sync_to, value )

Synced = Property( _get_synced, _set_synced )

#------------------------------------------------------------------------------
#  Trait definitions
#------------------------------------------------------------------------------

# An ID is one of the following:
#  * Any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores
#    ('_') or digits ([0-9]), not beginning with a digit;
#  * a number [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );
#  * any double-quoted string ("...") possibly containing escaped
#    quotes (\")1;
#  * an HTML string (<...>).
alphanum = "[a-zA-Z]"#\200-\377] "# | [0-9] "#| [_]"
number   = "[-]?(.[0-9]+ | [0-9]+(.[0-9]*)? ) "
dquote   = '\" '
html     = "<...>"
id_trait = Regex(regex=alphanum+"|"+number+"|"+dquote+"|"+html)

pointf_trait = Tuple(Float, Float, desc="the point (x,y)", graphviz=True)

point_trait = pointf_trait #Either(
#    pointf_trait, Tuple(Float, Float, Float, desc="the point (x,y,z)")
#)

color_schemes = ["X11", "Accent", "Blues", "BRBG", "BUGN", "BUPU", "Dark",
    "GUBU", "Greens", "Greys", "Oranges", "OORD", "Paired", "Pastel", "PIYG",
    "PRGN", "PUBU", "PUBUGN", "PUOR", "PURD", "Purples", "RDBU", "RDGY",
    "RDPU", "RDYLBU", "RDYLGN", "Reds", "Set", "Spectral", "YLGN",
    "YLGNBU", "YLORBR", "YLORRD"]

color_scheme_trait = Enum(color_schemes, desc="a color scheme namespace",
    label="Color scheme", graphviz=True)

color_trait = Color("black", desc="drawing color for graphics, not text",
    graphviz=True)

comment_trait = Str(desc="comments inserted into output", graphviz=True)

fontcolor_trait = Color("black", desc="color used for text",
    label="Font color", graphviz=True)

fontname_trait = Font("Times-Roman", desc="font used for text",
    label="Font name", graphviz=True)

fontsize_trait = Float(14.0, desc="size, in points, used for text",
    label="Font size", graphviz=True)

label_trait = Str(desc="text label attached to objects", graphviz=True)

# FIXME: Implement layerRange
#
# layerId or layerIdslayerId,
#     where layerId = "all", a decimal integer or a layer name. (An integer i
#     corresponds to layer i.) The string s consists of 1 or more separator
#     characters specified by the layersep attribute.
layer_trait = Str(desc="layers in which the node or edge is present",
    graphviz=True)

#margin_trait = Either(
#    Float, pointf_trait, desc="x and y margins of canvas or node label"
#)
margin_trait = Float(desc="x and y margins of canvas or node label",
    graphviz=True)

nojustify_trait = Bool(False, label="No justify",
    desc="multi-line labels will be justified in the context of itself",
    graphviz=True)

peripheries_trait = Int(desc="number of peripheries used in polygonal shapes "
    "and cluster boundaries", graphviz=True)

# FIXME: Implement splineType
#
# splineType
#    spline ( ';' spline )*
#    where spline    =    (endp)? (startp)? point (triple)+
#    and triple    =    point point point
#    and endp    =    "e,%d,%d"
#    and startp    =    "s,%d,%d"
# If a spline has points p1 p2 p3 ... pn, (n = 1 (mod 3)), the points
# correspond to the control points of a B-spline from p1 to pn. If startp is
# given, it touches one node of the edge, and the arrowhead goes from p1 to
# startp. If startp is not given, p1 touches a node. Similarly for pn and endp.
pos_trait = Tuple(Float, Float,
    desc="position of node, or spline control points", graphviz=True)

rectangle_trait = Tuple(Float, Float, Float, Float,
    desc="The rect llx,lly,urx,ury gives the coordinates, in points, of the "
    "lower-left corner (llx,lly) and the upper-right corner (urx,ury).",
    graphviz=True)

root_trait = Str(desc="nodes to be used as the center of the layout and the "
    "root of the generated spanning tree")

showboxes_trait = Trait("beginning", {"beginning": 1, "end": 2},
    label="Show boxes", desc="guide boxes in PostScript output",
    graphviz=True)

# Additional styles are available in device-dependent form. Style lists are
# passed to device drivers, which can use this to generate appropriate output.
edge_styles = ["dashed", "dotted", "solid", "invis", "bold"]
cluster_styles = ["filled", "rounded"]
node_styles = edge_styles + cluster_styles + ["diagonals"]

target_trait = Str(desc="if the object has a URL, this attribute determines "
    "which window of the browser is used", graphviz=True)

tooltip_trait = Str(desc="tooltip annotation attached to the node or edge",
    graphviz=True)

url_trait = Str(desc="hyperlinks incorporated into device-dependent output",
    graphviz=True)

# EOF -------------------------------------------------------------------------


#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Version: Python 3
# Author: fm4d

import random
import shelve

class ObjectLikeDbfilenameShelf():
    """
    Proxy class that allows object-like manipulation with DbfilenameShelf object
    """
    def __init__(self, ds):
        """
        Args:
            ds: DbfilenameShelf object
        """

        super(ObjectLikeDbfilenameShelf, self).__setattr__('_ds', ds)

    def __getattr__(self, name):
        if name in self._ds:
            return self._ds[name]
        else:
            raise AttributeError("{} object has no attribute {}".format(
                self._ds.__class__.__name__, name
            ))

    def __setattr__(self, name, value):
        self._ds[name] = value

    def __delattr__(self, name):
        if name in self._ds:
            del self._ds[name]
        else:
            raise AttributeError("{} object has no attribute {}".format(
                self._ds.__class__.__name__, name
            ))

    def __repr__(self):
        return '\n'.join("{}: {}".format(k, v.order)
                         for k, v in self._ds.items())

    def __iter__(self):
        for key in self._ds.keys():
            yield key

    def close(self):
        self._ds.close()

class WalkByGroup:
    """
    Iterator that walks throught iterable by step_size of elements
    """

    def __init__(self, iterable, step_size):
        """
        Args:
            iterable: iterable object (object that can be turned into iterator)
            step_size: number of elements returned at once
        """

        self.iterator = iter(iterable)
        self.step_size = step_size
        self.buffer = (None, ) + tuple(next(self.iterator) for x in range(step_size-1))

    def __iter__(self):
        return self

    def __next__(self):
        self.buffer = self.buffer[1:] + (next(self.iterator), )
        return self.buffer

def parse(filename, encoding=None):
    """
    !DEMO!
    Simple file parsing generator

    Args:
        filename: absolute or relative path to file on disk
        encoding: encoding string that is passed to open function
    """

    with open(filename, encoding=encoding) as source:
        for line in source:
            for word in line.split():
                yield word

class MarkovChain():
    """
    Single markov chain
    """

    def __init__(self, order, content=None):
        self.order = order
        self.content = {} if content is None else content
        self._start_words = None

    @property
    def startwords(self):
        """
        !DEMO!
        Cached list of keys that can be used to generate sentence.
        """

        if self._start_words is not None:
            return self._start_words
        else:
            self._start_words = list(filter(
                lambda x: str.isupper(x[0][0]) and x[0][-1] not in ['.', '?', '!'],
                self.content.keys()
            ))
            return self._start_words

    def decache(self):
        """
        Delete cached value of startwords
        """
        self._start_words = None

class MarkovGenerator():
    """
    Base object used to build chains and generate sentences.
    """

    def __init__(self, shelve_file='chains_shelve'):
        """
        Args:
            shelve_file: path to shelve file on disk
        """

        self.ds = shelve.open(shelve_file, writeback=True)
        self.chains = ObjectLikeDbfilenameShelf(self.ds)

    def add_chain(self, name, order):
        """
        Add chain to current shelve file

        Args:
            name: chain name
            order: markov chain order
        """

        if name not in self.chains:
            setattr(self.chains, name, MarkovChain(order=order))
        else:
            raise ValueError("Chain with this name already exists")

    def remove_chain(self, name):
        """
        Remove chain from current shelve file

        Args:
            name: chain name
        """

        if name in self.chains:
            delattr(self.chains, name)
        else:
            raise ValueError("Chain with this name not found")

    def build_chain(self, source, chain):
        """
        Build markov chain from source on top of existin chain

        Args:
            source: iterable which will be used to build chain
            chain: MarkovChain in currently loaded shelve file that
                   will be extended by source
        """

        for group in WalkByGroup(source, chain.order+1):
            pre = group[:-1]
            res = group[-1]

            if pre not in chain.content:
                chain.content[pre] = {res: 1}
            else:
                if res not in chain.content[pre]:
                    chain.content[pre][res] = 1
                else:
                    chain.content[pre][res] += 1

        chain.decache()

    def generate_sentence(self, chain):
        """
        !DEMO!
        Demo function that shows how to generate a simple sentence starting with
        uppercase letter without lenght limit.

        Args:
            chain: MarkovChain that will be used to generate sentence
        """

        def weighted_choice(choices):
            total_weight = sum(weight for val, weight in choices)
            rand = random.uniform(0, total_weight)

            upto = 0
            for val, weight in choices:
                if upto + weight >= rand:
                    return val
                upto += weight

        sentence = list(random.choice(chain.startwords))

        while not sentence[-1][-1] in ['.', '?', '!']:
            sentence.append(
                weighted_choice(
                    chain.content[tuple(sentence[-2:])].items()
                )
            )

        return ' '.join(sentence)

    def close(self):
        """
        Close current shelve db file
        """
        self.chains.close()


#------------------------------------------------------------------------------
#  Copyright (c) 2009 Richard W. Lincoln
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
#  IN THE SOFTWARE.
#------------------------------------------------------------------------------

""" Defines a base class for many graphs. """

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

import os
import logging
import tempfile
import subprocess

from enthought.traits.api import \
    HasTraits, Str, List, Instance, Bool, Property, Constant, Button, \
    ReadOnly, Dict, TraitListEvent, Int, Enum, on_trait_change

from enthought.enable.api \
    import Viewport, Container

from enthought.enable.tools.api \
    import ViewportPanTool, ViewportZoomTool

from dot2tex.dotparsing import find_graphviz

from node \
    import Node

from edge \
    import Edge

from common \
    import id_trait, Alias

import godot

#from util import Serializable

FORMATS = ['dot', 'canon', 'cmap', 'cmapx', 'cmapx_np', 'dia', 'fig', 'gd',
           'gd2', 'gif', 'hpgl', 'imap', 'imap_np', 'ismap', 'jpe', 'jpeg',
           'jpg', 'mif', 'mp', 'pcl', 'pdf', 'pic', 'plain', 'plain-ext',
           'png', 'ps', 'ps2', 'svg', 'svgz', 'vml', 'vmlz', 'vrml', 'vtx',
           'wbmp', 'xdot', 'xlib', 'bmp', 'eps', 'gtk', 'ico', 'tga', 'tiff']

RENDERERS = ['cairo', 'gd']

FORMATTERS = ['cairo', 'gd', 'gdk_pixbuf']

logger = logging.getLogger(__name__)

#------------------------------------------------------------------------------
#  "BaseGraph" class:
#------------------------------------------------------------------------------

class BaseGraph ( HasTraits ):
    """ Defines a representation of a graph in Graphviz's dot language """

    #--------------------------------------------------------------------------
    #  Trait definitions.
    #--------------------------------------------------------------------------

    # Optional unique identifier.
    ID = id_trait

    # Synonym for ID.
    name = Alias("ID", desc="synonym for ID") # Used by InstanceEditor

    # Main graph nodes.
    nodes = List( Instance(Node) )

    # Map if node IDs to node objects.
#    id_node_map = Dict

    # Graph edges.
    edges = List(Instance(Edge))

    # Separate layout regions.
    subgraphs = List(Instance("godot.subgraph.Subgraph"))

    # Clusters are encoded as subgraphs whose names have the prefix 'cluster'.
    clusters = List(Instance("godot.cluster.Cluster"))

    # Node from which new nodes are cloned.
    default_node = Instance(Node)

    # Edge from which new edges are cloned.
    default_edge = Instance(Edge)

    # Graph from which new subgraphs are cloned.
    default_graph = Instance(HasTraits)

    # Level of the graph in the subgraph hierarchy.
#    level = Int(0, desc="level in the subgraph hierarchy")

    # Padding to use for pretty string output.
    padding = Str("        ", desc="padding for pretty printing")

    # A dictionary containing the Graphviz executable names as keys
    # and their paths as values.  See the trait initialiser.
    programs = Dict(desc="names and paths of Graphviz executables")

    # The Graphviz layout program
    program = Enum("dot", "circo", "neato", "twopi", "fdp",
        desc="layout program used by Graphviz")

    # Format for writing to file.
    format = Enum(FORMATS, desc="format used when writing to file")

    # Use Graphviz to arrange all graph components.
    arrange = Button("Arrange All")

    # Parses the Xdot attributes for all graph components.
    redraw = Button("Redraw Canvas")

    #--------------------------------------------------------------------------
    #  Enable trait definitions.
    #--------------------------------------------------------------------------

    # Container of graph components.
    component = Instance(Container, desc="container of graph components.")

    # A view into a sub-region of the canvas.
    vp = Instance(Viewport, desc="a view of a sub-region of the canvas")

    #--------------------------------------------------------------------------
    #  Xdot trait definitions:
    #--------------------------------------------------------------------------

    # For a given graph object, one will typically a draw directive before the
    # label directive. For example, for a node, one would first use the
    # commands in _draw_ followed by the commands in _ldraw_.
    _draw_ = Str(desc="xdot drawing directive")

    # Label draw directive.
    _ldraw_ = Str(desc="xdot label drawing directive")

    #--------------------------------------------------------------------------
    #  "object" interface:
    #--------------------------------------------------------------------------

#    def __init__(self, **traits):
#        """ Initialises a new BaseGraph instance.
#        """
#        super(BaseGraph, self).__init__(**traits)
#
#        # Automatically creates all the methods enabling the saving
#        # of output in any of the supported formats.
#        for frmt in FORMATS:
#            self.__setattr__('save_'+frmt,
#                             lambda flo, f=frmt, prog=self.program: \
#                             flo.write( self.create(format=f, prog=prog) ))
#            f = self.__dict__['save_'+frmt]
#            f.__doc__ = '''Refer to the docstring accompanying the 'create'
#            method for more information.'''

    def __len__(self):
        """ Return the order of the graph when requested by len().

            @rtype:  number
            @return: Size of the graph.
        """
        return len(self.nodes)

    def __iter__(self):
        """ Return a iterator passing through all nodes in the graph.

            @rtype:  iterator
            @return: Iterator passing through all nodes in the graph.
        """
        for each in self.nodes:
            yield each

    def __getitem__(self, node):
        """ Return a iterator passing through all neighbours of the given node.

            @rtype:  iterator
            @return: Iterator passing through all neighbours of the given node.
        """
        for each_edge in self.edges:
            if (each_edge.tail_node == node) or (each_edge.head_node == node):
                yield each_edge

    def __str__(self):
        """ Returns a string representation of the graph in dot language. It
            will return the graph and all its subelements in string form.
        """
        s = ""
        padding = self.padding
        if self.ID:
            s += "%s {\n" % self.ID
        else:
            s += "{\n"

        # Traits to be included in string output have 'graphviz' metadata.
        for trait_name, trait in self.traits(graphviz=True).iteritems():
            # Get the value of the trait for comparison with the default.
            value = getattr(self, trait_name)

            # Only print attribute value pairs if not defaulted.
            # FIXME: Alias/Synced traits default to None.
            if ( value != trait.default ) and ( trait.default is not None ):
                if isinstance( value, basestring ):
                    # Add double quotes to the value if it is a string.
                    valstr = '"%s"' % value
                else:
                    valstr = str(value)

                s += "%s%s=%s;\n" % ( padding, trait_name, valstr )

        def prepend_padding(s):
            return "\n".join( [padding + line for line in s.splitlines()] )

        for node in self.nodes:
            s += "%s%s\n" % ( padding, str(node) )
        for edge in self.edges:
            s += "%s%s\n" % ( padding, str(edge) )
        for subgraph in self.subgraphs:
            s += prepend_padding( str( subgraph ) ) + "\n"
        for cluster in self.clusters:
            s += prepend_padding( str( cluster ) ) + "\n"

        s += "}"

        return s

    #--------------------------------------------------------------------------
    #  Trait initialisers:
    #--------------------------------------------------------------------------

    def _default_node_default(self):
        """ Trait initialiser.
        """
        return Node("default")

    def _default_edge_default(self):
        """ Trait initialiser.
        """
        return Edge("tail", "head")

    def _default_graph_default(self):
        """ Trait initialiser.
        """
        return godot.cluster.Cluster(ID="cluster_default")

    def _programs_default(self):
        """ Trait initaliser.
        """
        progs = find_graphviz()
        if progs is None:
            logger.warning("GraphViz's executables not found")
            return {}
        else:
            return progs

    def _component_default(self):
        """ Trait initialiser.
        """
        return Container(draw_axes=True, fit_window=False, auto_size=True)

    def _vp_default(self):
        """ Trait initialiser.
        """
        vp = Viewport(component=self.component)

        vp.enable_zoom=True
        vp.view_position = [-5, -5]
        vp.tools.append(ViewportPanTool(vp))

        return vp

    #--------------------------------------------------------------------------
    #  Public interface:
    #--------------------------------------------------------------------------

    def save_dot(self, flo, prog=None):
        """ Writes a graph to a file.

            Given a file like object 'flo' it will truncate it and write a
            representation of the graph defined by the dot object and in the
            format specified.
            The format 'raw' is used to dump the string representation
            of the Dot object, without further processing.
            The output can be processed by any of graphviz tools, defined
            in 'prog', which defaults to 'dot'.
        """
        flo.write( str(self) )

    def save_xdot(self, flo, prog=None):
        prog = self.program if prog is None else prog
        flo.write( self.create(prog, "xdot") )

    def save_png(self, flo, prog=None):
        prog = self.program if prog is None else prog
        flo.write( self.create(prog, "png") )

    @classmethod
    def load_dot(cls, flo):
        parser = godot.dot_data_parser.GodotDataParser()
        return parser.parse_dot_file(flo)

    @classmethod
    def load_xdot(cls, flo):
        parser = godot.dot_data_parser.GodotDataParser()
        return parser.parse_dot_file(flo)

    def create(self, prog=None, format=None):
        """ Creates and returns a representation of the graph using the
            Graphviz layout program given by 'prog', according to the given
            format.

            Writes the graph to a temporary dot file and processes it with
            the program given by 'prog' (which defaults to 'dot'), reading
            the output and returning it as a string if the operation is
            successful. On failure None is returned.
        """
        prog = self.program if prog is None else prog
        format = self.format if format is None else format

        # Make a temporary file ...
        tmp_fd, tmp_name = tempfile.mkstemp()
        os.close( tmp_fd )
        # ... and save the graph to it.
        dot_fd = file( tmp_name, "w+b" )
        self.save_dot( dot_fd )
        dot_fd.close()

        # Get the temporary file directory name.
        tmp_dir = os.path.dirname( tmp_name )

        # TODO: Shape image files (See PyDot). Important.

        # Process the file using the layout program, specifying the format.
        p = subprocess.Popen(
            ( self.programs[ prog ], '-T'+format, tmp_name ),
            cwd=tmp_dir,
            stderr=subprocess.PIPE, stdout=subprocess.PIPE)

        stderr = p.stderr
        stdout = p.stdout

        # Make sense of the standard output form the process.
        stdout_output = list()
        while True:
            data = stdout.read()
            if not data:
                break
            stdout_output.append(data)
        stdout.close()

        if stdout_output:
            stdout_output = ''.join(stdout_output)

        # Similarly so for any standard error.
        if not stderr.closed:
            stderr_output = list()
            while True:
                data = stderr.read()
                if not data:
                    break
                stderr_output.append(data)
            stderr.close()

            if stderr_output:
                stderr_output = ''.join(stderr_output)

        #pid, status = os.waitpid(p.pid, 0)
        status = p.wait()

        if status != 0 :
            logger.error("Program terminated with status: %d. stderr " \
                "follows: %s" % ( status, stderr_output ) )
        elif stderr_output:
            logger.error( "%s", stderr_output )

        # TODO: Remove shape image files from the temporary directory.

        # Remove the temporary file.
        os.unlink(tmp_name)

        return stdout_output

    @on_trait_change("arrange")
    def arrange_all(self):
        """ Sets for the _draw_ and _ldraw_ attributes for each of the graph
            sub-elements by processing the xdot format of the graph.
        """
        import godot.dot_data_parser

        parser = godot.dot_data_parser.GodotDataParser()

        xdot_data = self.create( format = "xdot" )
        print "GRAPH DOT:\n", str( self )
        print "XDOT DATA:\n", xdot_data

        parser.dotparser.parseWithTabs()
        ndata = xdot_data.replace( "\\\n", "" )
        tokens = parser.dotparser.parseString( ndata )[0]
        parser.build_graph( graph=self, tokens=tokens[3] )

    def add_node(self, node_or_ID, **kwds):
        """ Adds a node to the graph.
        """
        if not isinstance(node_or_ID, Node):
            nodeID = str( node_or_ID )
            if nodeID in self.nodes:
                node = self.nodes[ self.nodes.index(nodeID) ]
            else:
                if self.default_node is not None:
                    node = self.default_node.clone_traits(copy="deep")
                    node.ID = nodeID
                else:
                    node = Node(nodeID)
                self.nodes.append( node )
        else:
            node = node_or_ID
            if node in self.nodes:
                node = self.nodes[ self.nodes.index(node_or_ID) ]
            else:
                self.nodes.append( node )

        node.set( **kwds )

        return node

    def delete_node(self, node_or_ID):
        """ Removes a node from the graph.
        """
        if isinstance(node_or_ID, Node):
#            name = node_or_ID.ID
            node = node_or_ID
        else:
#            name = node_or_ID
            node = self.get_node(node_or_ID)
            if node is None:
                raise ValueError("Node %s does not exists" % node_or_ID)

#        try:
#            del self.nodes[name]
#        except:
#            raise ValueError("Node %s does not exists" % name)

#        self.nodes = [n for n in self.nodes if n.ID != name]
#        idx = self.nodes.index(name)
#        return self.nodes.pop(idx)

        self.nodes.remove(node)

    def get_node(self, ID):
        """ Returns the node with the given ID or None.
        """
        for node in self.nodes:
            if node.ID == str(ID):
                return node
        return None

    def delete_edge(self, tail_node_or_ID, head_node_or_ID):
        """ Removes an edge from the graph. Returns the deleted edge or None.
        """
        if isinstance(tail_node_or_ID, Node):
            tail_node = tail_node_or_ID
        else:
            tail_node = self.get_node(tail_node_or_ID)

        if isinstance(head_node_or_ID, Node):
            head_node = head_node_or_ID
        else:
            head_node = self.get_node(head_node_or_ID)

        if (tail_node is None) or (head_node is None):
            return None

        for i, edge in enumerate(self.edges):
            if (edge.tail_node == tail_node) and (edge.head_node == head_node):
                edge = self.edges.pop(i)
                return edge

        return None

    def add_edge(self, tail_node_or_ID, head_node_or_ID, **kwds):
        """ Adds an edge to the graph.
        """
        tail_node = self.add_node(tail_node_or_ID)
        head_node = self.add_node(head_node_or_ID)

        # Only top level graphs are directed and/or strict.
        if "directed" in self.trait_names():
            directed = self.directed
        else:
            directed = False

        if self.default_edge is not None:
            edge = self.default_edge.clone_traits(copy="deep")
            edge.tail_node = tail_node
            edge.head_node = head_node
            edge.conn = "->" if directed else "--"
            edge.set( **kwds )
        else:
            edge = Edge(tail_node, head_node, directed, **kwds)

        if "strict" in self.trait_names():
            if not self.strict:
                self.edges.append(edge)
            else:
                self.edges.append(edge)
                # FIXME: Implement strict graphs.
#                raise NotImplementedError
        else:
            self.edges.append(edge)

    def add_subgraph(self, subgraph_or_ID):
        """ Adds a subgraph to the graph.
        """
        if not isinstance(subgraph_or_ID, (godot.subgraph.Subgraph,
                                           godot.cluster.Cluster)):
            subgraphID = str( subgraph_or_ID )
            if subgraph_or_ID.startswith("cluster"):
                subgraph = godot.cluster.Cluster(ID=subgraphID)
            else:
                subgraph = godot.subgraph.Subgraph(ID=subgraphID)
        else:
            subgraph = subgraph_or_ID

        subgraph.default_node = self.default_node
        subgraph.default_edge = self.default_edge
#        subgraph.level = self.level + 1
#        subgraph.padding += self.padding

        if isinstance(subgraph, godot.subgraph.Subgraph):
            self.subgraphs.append(subgraph)
        elif isinstance(subgraph, godot.cluster.Cluster):
            self.clusters.append(subgraph)
        else:
            raise

        return subgraph

    def add_cluster(self, cluster_or_ID):
        """ Adds a cluster to the graph.
        """
        return self.add_subgraph(cluster_or_ID)

    #--------------------------------------------------------------------------
    #  "BaseGraph" interface:
    #--------------------------------------------------------------------------

    def _program_changed(self, new):
        """ Handles the Graphviz layout program selection changing.
        """
        progs = self.progs

        if not progs.has_key(prog):
            logger.warning( 'GraphViz\'s executable "%s" not found' % prog )

        if not os.path.exists( progs[prog] ) or not \
            os.path.isfile( progs[prog] ):
            logger.warning( "GraphViz's executable '%s' is not a "
                "file or doesn't exist" % progs[prog] )

    def _component_changed(self, new):
        """ Handles the graph canvas changing.
        """
        self.vp.component = new

#    @on_trait_change("nodes,nodes_items")
#    def remove_duplicates(self, new):
#        """ Ensures node ID uniqueness.
#        """
#        if isinstance(new, TraitListEvent):
#            old = event.removed
#            new = event.added
#
#        set = {}
#        self.set( trait_change_notify = False,
#                  nodes = [set.setdefault(e, e) for e in new if e not in set] )

    @on_trait_change("nodes,nodes_items")
    def _set_node_lists(self, new):
        """ Maintains each edge's list of available nodes.
        """
        for edge in self.edges:
            edge._nodes = self.nodes

#    @on_trait_change("nodes,nodes_items")
#    def _manage_id_node_map(self, obj, name, old, new):
#        """ Maintains a dictionary mapping node IDs to nodes.
#        """
#        if isinstance(new, TraitListEvent):
#            old = event.removed
#            new = event.added
#        else:
#            self.id_node_map = {}
#
#        for new_node in new:
#            self.id_node_map[new_node.ID] = new_node
#
#        for old_node in old:
#            try:
#                self.id_node_map.pop(node.ID)
#            except KeyError:
#                print "Removed node not found in ID map. Updating."
#                self._update_id_node_map()

#    @on_trait_change("nodes,edges")
#    def _on_node_or_edge(self, object, name, old, new):
#        """ Handles the list of nodes being set.
#        """
#        self.component.remove( *[r.component for r in old] )
#        self.component.add( *[a.component for a in new] )
#        self.component.request_redraw()

#    @on_trait_change("nodes_items,edges_items")
#    def _on_node_or_edge_items(self, event):
#        """ Handles addition and removal of nodes.
#        """
#        self.component.add( *[a.component for a in event.added] )
#        self.component.remove( *[r.component for r in event.removed] )
#        self.component.request_redraw()

#    def _update_id_node_map(self):
#        """ Sets the map of node IDs to nodes.
#        """
#        d = {}
#        for node in self.nodes:
#            d[node.ID] = node
#        self.id_node_map = d

# EOF -------------------------------------------------------------------------


#------------------------------------------------------------------------------
#  Copyright (c) 2009 Richard W. Lincoln
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
#  IN THE SOFTWARE.
#------------------------------------------------------------------------------

""" Defines a parser of Graphviz dot data.
"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from dot2tex.dotparsing import \
    DotDataParser, ADD_NODE, ADD_EDGE, ADD_GRAPH_TO_NODE_EDGE, \
    ADD_NODE_TO_GRAPH_EDGE, ADD_GRAPH_TO_GRAPH_EDGE, ADD_SUBGRAPH, \
    SET_DEF_NODE_ATTR, SET_DEF_EDGE_ATTR, SET_DEF_GRAPH_ATTR, SET_GRAPH_ATTR

from enthought.traits.api import Float, Tuple, List

from graph import Graph
from subgraph import Subgraph
from cluster import Cluster
from node import Node
from edge import Edge

#------------------------------------------------------------------------------
#  "GodotDataParser" class:
#------------------------------------------------------------------------------

class GodotDataParser(DotDataParser):
    """ Parses Graphviz dot data.
    """

    def _proc_node_stmt(self, toks):
        """ Return (ADD_NODE, node_name, options)
        """
        opts = toks[1]
        dummy_node = Node("dummy")
        # Coerce attribute types.
        for key, value in opts.iteritems():
            trait = dummy_node.trait(key)
            if trait is not None:
                if trait.is_trait_type( Float ):
                    opts[key] = float( value )

                elif trait.is_trait_type( Tuple ):
                    opts[key] = tuple( [float(c) for c in value.split(",")] )

        return super(GodotDataParser, self)._proc_node_stmt(toks)

    def _proc_edge_stmt(self, toks):
        """ Returns a tuple of the form (ADD_EDGE, src, dest, options).
        """
        opts = toks[3]
        dummy_edge = Edge("dummy1", "dummy2")
        # Coerce attribute types.
        for key, value in opts.iteritems():
            trait = dummy_edge.trait(key)
            if trait is not None:
                # FIXME: Implement Graphviz spline types.
                if trait.is_trait_type( List ):
                    p = [] # List of float doublets.
                    for t in value.split( " " ):
                        l = t.split( "," )
                        if len(l) == 3: # pos="e,39,61 39,97 39,89 39,80 39,71"
                            l.pop(0)
                        f = [ float(a) for a in l ]
                        p.append( tuple(f) )
                    opts[key] = p

                elif trait.is_trait_type( Float ):
                    opts[key] = float( value )

                elif trait.is_trait_type( Tuple ):
                    opts[key] = tuple( [float(c) for c in value.split(",")] )

        return super(GodotDataParser, self)._proc_edge_stmt(toks)

    def parse_dot_file(self, file_or_filename):
        """ Returns a graph given a file or a filename.
        """
        if isinstance(file_or_filename, basestring):
            file = None
            try:
                file = open(file_or_filename, "rb")
                data = file.read()
            except:
                print "Could not open %s." % file_or_filename
                return None
            finally:
                if file is not None:
                    file.close()
        else:
            file = file_or_filename
            data = file.read()

        return self.parse_dot_data(data)

    def build_top_graph(self,tokens):
        """ Build a Godot graph instance from parsed data.
        """
        # Get basic graph information.
        strict = tokens[0] == 'strict'
        graphtype = tokens[1]
        directed = graphtype == 'digraph'
        graphname = tokens[2]
        # Build the graph
        graph = Graph(ID=graphname, strict=strict, directed=directed)
        self.graph = self.build_graph(graph, tokens[3])

    def build_graph(self, graph, tokens):
        """ Builds a Godot graph.
        """
        subgraph = None

        for element in tokens:
            cmd = element[0]
            if cmd == ADD_NODE:
                cmd, nodename, opts = element
                graph.add_node(nodename, **opts)

            elif cmd == ADD_EDGE:
                cmd, src, dest, opts = element
                srcport = destport = ""
                if isinstance(src,tuple):
                    srcport = src[1]
                    src = src[0]
                if isinstance(dest,tuple):
                    destport = dest[1]
                    dest = dest[0]

                graph.add_edge(src, dest, tailport=srcport, headport=destport,
                               **opts)

            elif cmd in [ADD_GRAPH_TO_NODE_EDGE,
                         ADD_GRAPH_TO_GRAPH_EDGE,
                         ADD_NODE_TO_GRAPH_EDGE]:
                cmd, src, dest, opts = element
                srcport = destport = ""

                if isinstance(src,tuple):
                    srcport = src[1]

                if isinstance(dest,tuple):
                    destport = dest[1]

                if not (cmd == ADD_NODE_TO_GRAPH_EDGE):
                    if cmd == ADD_GRAPH_TO_NODE_EDGE:
                        src = subgraph
                    else:
                        src = prev_subgraph
                        dest = subgraph
                else:
                    dest = subgraph

                src_is_graph = isinstance(src, (Subgraph, Cluster))
                dst_is_graph = isinstance(dst, (Subgraph, Cluster))

                if src_is_graph:
                    src_nodes = src.nodes
                else:
                    src_nodes = [src]
                if dst_is_graph:
                    dst_nodes = dst.nodes
                else:
                    dst_nodes = [dst]

                for src_node in src_nodes:
                    for dst_node in dst_nodes:
                        graph.add_edge(from_node=src_node, to_node=dst_node,
                                       tailport=srcport, headport=destport,
                                       **kwds)

            elif cmd == SET_GRAPH_ATTR:
                graph.set( **element[1] )

            elif cmd == SET_DEF_NODE_ATTR:
                graph.default_node.set( **element[1] )

            elif cmd == SET_DEF_EDGE_ATTR:
                graph.default_edge.set( **element[1] )

            elif cmd == SET_DEF_GRAPH_ATTR:
                graph.default_graph.set( **element[1] )

            elif cmd == ADD_SUBGRAPH:
                cmd, name, elements = element
                if subgraph:
                    prev_subgraph = subgraph
                if name.startswith("cluster"):
                    cluster = Cluster(ID=name)
                    cluster = self.build_graph(cluster, elements)
                    graph.add_cluster(cluster)
                else:
                    subgraph = Subgraph(ID=name)
                    subgraph = self.build_graph(subgraph, elements)
                    graph.add_subgraph(subgraph)

        return graph

def parse_dot_file(filename):
    """ Parses a DOT file and returns a Godot graph.
    """
    parser = GodotDataParser()
    graph  = parser.parse_dot_file(filename)
    del parser

    return graph

# EOF -------------------------------------------------------------------------


#!/usr/bin/env python
# -*- encoding: utf-8 -*-

# Copyright (c) 2014-2016 Chris Reed
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# o Redistributions of source code must retain the above copyright notice, this list
#   of conditions and the following disclaimer.
#
# o 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.
#
# o Neither the name of Chris Reed nor the names of contributors 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 HOLDER 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.

from __future__ import print_function
import datetime
import sys
import logging
try:
    from time import monotonic as hires_clock
except ImportError:
    import time
    # Select which clock routine is highest resolution for this OS.
    if sys.platform == 'win32':
        hires_clock = time.clock
    else:
        hires_clock = time.time

__all__ = ['ElapsedTimer', 'Timeout', 'TimeoutError']

__version__ = '0.4'

# Global enable for printing timer results.
enable = True

## Table of time units.
#
# List elements are a 3-tuple containing:
# - Cutoff value for this entry. The entry should be used if the time value is less
#   than this cutoff.
# - Unit string.
# - Multiplier to convert the time value to this entry's units.
units_table = [   (1e-9, u'ps', 1e12),
            (1e-6, u'ns', 1e9),
            (1e-3, u'µs', 1e6),
            (1.0,  u'ms', 1e3),
            (60,   u's',  1.0),
            (60*60, u'min', 1.0/60),
            (24*60*60, u'hrs', 1.0/(60*60)),
            (0, u's', 1.0)  ]   # Revert to seconds if very large

def get_time_units_and_multiplier(seconds):
    """
    Given a duration in seconds, determines the best units and multiplier to
    use to display the time. Return value is a 2-tuple of units and multiplier.
    """
    for cutoff, units, multiplier in units_table:
        if seconds < cutoff:
            break
    return units, multiplier

def format_duration(seconds):
    """Formats a number of seconds using the best units."""
    units, divider = get_time_units_and_multiplier(seconds)
    seconds *= divider
    return "%.3f %s" % (seconds, units)

class TimeoutError(RuntimeError):
    pass

class ElapsedTimer(object):
    """
    Timer meant to be used in a with statement. Pass the constructor an optional
    string describing the task that is being measured. When the with statement
    exits, a message will be printed to stdout with the elapsed time and the
    task description.
    """
    def __init__(self, task='', output=sys.stdout, logger=None, loglevel=logging.DEBUG):
        self._task = task
        self._start = 0
        self._end = 0
        self._delta = 0
        self._enable = True
        self._file = output
        self._logger = logger
        self._loglevel = loglevel

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, type, value, traceback):
        self.stop()
        self._print_message()

    def start(self):
        self._start = hires_clock()
        self._end = 0
        self._delta = 0

    def stop(self):
        if self._end == 0:
            self._end = hires_clock()
            self._delta = self._end - self._start

    @property
    def enable(self):
        return self._enable

    @enable.setter
    def enable(self, value):
        self._enable = value

    @property
    def elapsed(self):
        if self._end:
            return self._delta
        else:
            return hires_clock() - self._start

    @property
    def timedelta(self):
        return datetime.timedelta(seconds=self.elapsed)

    def _print_message(self):
        global enable
        if enable and self._enable:
            displayDelta = format_duration(self._delta)
            if len(self._task):
                msg = "%s: %s" % (displayDelta, self._task)
            else:
                msg = displayDelta
            if not self._logger:
                msg += '\n'
            if self._logger:

                self._logger.log(self._loglevel, msg)
            else:
                self._file.write(msg)

class Timeout(ElapsedTimer):
    def __init__(self, timeout=0, task='', output=sys.stdout, logger=None, loglevel=logging.DEBUG):
        super(Timeout, self).__init__(task, output, logger, loglevel)
        self._timeout = timeout
        self._did_timeout = False

    def check(self):
        if self._timeout is None:
            return False
        if self._did_timeout:
            return True
        self._did_timeout = self.elapsed >= self._timeout
        return self._did_timeout

    def check_and_raise(self):
        if self.check():
            raise TimeoutError(self._task)

    @property
    def timed_out(self):
        return self.check()


#------------------------------------------------------------------------------
# Copyright (C) 2009 Richard W. Lincoln
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This software is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#------------------------------------------------------------------------------

""" Defines a graph editor based on a tree control.
"""

#------------------------------------------------------------------------------
#  Imports:
#------------------------------------------------------------------------------

from os.path import exists, basename, join, dirname

from enthought.traits.api \
    import HasTraits, Code, Instance, Str, Adapter, adapts, AdaptsTo

from enthought.io.api import File
from enthought.pyface.workbench.api import TraitsUIEditor
from enthought.traits.ui.api import View, Item, Group
from enthought.traits.ui.api import ImageEditor as ImageTraitEditor
from enthought.pyface.image_resource import ImageResource

from puddle.resource.api \
    import IResource, ResourceEditor, ResourcePlugin, Editor

from godot.api import Graph, parse_dot_file
from godot.ui.graph_tree import graph_tree_editor

from dot_adapter import DotFileIResourceAdapter

IMAGE_LOCATION = join(dirname(__file__), "..", "ui", "images")

#------------------------------------------------------------------------------
#  "TreeEditor" class:
#------------------------------------------------------------------------------

class TreeEditor(ResourceEditor):
    """ A graph tree editor.
    """

    #--------------------------------------------------------------------------
    #  "TreeEditor" interface:
    #--------------------------------------------------------------------------

    # Dot file on the file system.
    editor_input = AdaptsTo(IResource)

    # Graph loaded from the resource.
    graph = Instance(Graph)

    # An optional reference to the currently selected object in the editor.
    selected = Instance(HasTraits) # Used by the properties view.

    #--------------------------------------------------------------------------
    #  "TraitsUIEditor" interface.
    #--------------------------------------------------------------------------

    def _name_default(self):
        """ Trait initialiser.
        """
        # 'obj' is a io.File
        self.obj.on_trait_change(self.on_path, "path")

        return basename(self.obj.path)

    def on_path(self, new):
        """ Handle the file path changing.
        """
        self.name = basename(new)
        self.graph = self.editor_input.load()

    #--------------------------------------------------------------------------
    #  "TreeEditor" interface.
    #--------------------------------------------------------------------------

    def _editor_input_default(self):
        """ Trait initialiser.
        """
        return self.obj

    def create_ui(self, parent):
        """ Creates the toolkit-specific control that represents the
            editor. 'parent' is the toolkit-specific control that is
            the editor's parent.
        """
        self.graph = self.editor_input.load()

        view = View(Item(name="graph", editor=graph_tree_editor,
                show_label=False),
            id="godot.graph_editor", kind="live", resizable=True)

        ui = self.edit_traits(view=view, parent=parent, kind="subpanel")

        return ui

    #--------------------------------------------------------------------------
    #  "ResourceEditor" interface:
    #--------------------------------------------------------------------------

    def save(self):
        """ Saves the editor content.
        """
        self.editor_input.save(self.graph)

    def save_as(self):
        """ Saves the editor content to a new file name.
        """
        pass

#------------------------------------------------------------------------------
#  "TreeEditorExtension" class:
#------------------------------------------------------------------------------

class TreeEditorExtension(Editor):
    """ Associates a tree editor with '.dot' file extensions.
    """

    # The object contribution's globally unique identifier.
    id = "godot.plugin.tree_editor"

    # A name that will be used in the UI for this editor
    name = "Tree Editor"

    # An icon that will be used for all resources that match the
    # specified extensions
    image = ImageResource("tree", search_path=[IMAGE_LOCATION])

    # The contributed editor class
    editor_class = "godot.plugin.tree_editor:TreeEditor"

    # The list of file types understood by the editor
    extensions = [".dot", ".xdot"]

    # If true, this editor will be used as the default editor for the type
    default = False

# EOF -------------------------------------------------------------------------