defaults =
    initialX: 50
    initialY: 50
    boxHeight: 100
    boxWidth: 200
    horizontalSpace: 0
    verticalSpace: 75
    indicatorSize: 40
    indicatorMargin: 10
    defaultNodeFillColor: '#FFF'
    selectedNodeFillColor: '#989FED'
    labelFont: { font: "16px Times", opacity: 1 }
    nodeAfterClickHandler: (node) ->
    openCloseAfterClickHandler: (node) ->

class SVGTree
    constructor: (options) ->       
        @options = $.extend {}, defaults, options
       
        @paper = new Raphael @options.elementId, '100%', '100%'
        @backgroundColor = $('#' + @options.elementId).css("background-color")
        @currX
        @currY
        @xInc = @options.boxWidth + @options.horizontalSpace
        @yInc = @options.boxHeight + @options.verticalSpace
        @currentlySelectedNode
        @defaultNodeFill = { fill: @options.defaultNodeFillColor }
        @selectedNodeFill = { fill: @options.selectedNodeFillColor }

        #Event callbacks
        self = @
        @openCloseClickHandler = () ->
            handleOpenClose.call(self, this)
            self.options.openCloseAfterClickHandler.call(self, this)
       
        @nodeClickHandler = () ->
            handleNodeClick.call(self, this)
            self.options.nodeAfterClickHandler.call(self, this)
       
    render: ->
        #this can be called over and over to refresh so reset initial state
        @paper.clear()
        @currX = @options.initialX
        @currY = @options.initialY
        @renderingTop = true #the rendering of the top node is just a little different so keep track

        #define functions
        renderNode = (elementData, x) =>
            rect = @paper.rect x, @currY, @options.boxWidth, @options.boxHeight
            rect.data('identifier', elementData.identifier)
            rect.attr(if elementData.selected then @selectedNodeFill else @defaultNodeFill)
            if elementData.selected then @currentlySelectedNode = rect
            rect.click @nodeClickHandler

            createTextLabel rect, elementData

            @currY = @currY + @yInc
           
            if elementData.children?
                drawOpenCloseIndicator rect, elementData
                if elementData.childrenOpen
                    for child in elementData.children
                        childNode = renderNode child, x + @xInc
                        drawConnector rect, childNode, child
           
            rect
       
        drawOpenCloseIndicator = (node, elementData) =>
            startXOffset = if @renderingTop then @options.indicatorMargin else (@options.boxWidth / 2 + @options.horizontalSpace) + @options.indicatorMargin
            indicatorHeight = Math.round(@options.indicatorSize * 0.866) #Aprox Math.sqrt(3) / 2
            indicatorStartX = node.attr("x") - @options.indicatorSize - startXOffset
            indicatorStartY = node.attr("y") + ((@options.boxHeight - indicatorHeight) / 2)
                   
            calculatedPath = if elementData.childrenOpen
                "M #{indicatorStartX} #{indicatorStartY} l #{indicatorHeight} 0 l -#{indicatorHeight / 2} #{indicatorHeight} l -#{indicatorHeight / 2} -#{indicatorHeight}"
            else
                "M #{indicatorStartX} #{indicatorStartY} l 0 #{indicatorHeight} l #{indicatorHeight} -#{indicatorHeight / 2} l -#{indicatorHeight} -#{indicatorHeight / 2}"

            #Create a rectangle with the same click handler to surround the visible indicator to make the click easier to hit
            target = @paper.rect(indicatorStartX - 6, indicatorStartY - 6, @options.indicatorSize + 10, @options.indicatorSize + 10)
            target.attr "stroke-opacity": 0
            target.attr "fill": @backgroundColor
            target.attr "fill-opacity": 0
            target.data("elementData", elementData)
            target.click @openCloseClickHandler

            triangle = @paper.path calculatedPath
            triangle.attr({fill: '#000'})
            triangle.data("elementData", elementData)
            triangle.click @openCloseClickHandler

            @renderingTop = false

        #Handle name wrapping
        createTextLabel = (rect, elementData) =>
            textLabel = @paper.text(rect.attr('x') + (@options.boxWidth / 2), @currY + (@options.boxHeight / 2), "")
                .attr(@options.labelFont).data('identifier', elementData.identifier)
            textLabel.click () => @nodeClickHandler.call(rect)

            tempText = " "
            for word in elementData.name.split(" ")
                textLabel.attr("text", tempText + " " + word)
                tempText += if textLabel.getBBox().width > @options.boxWidth then "\n" + word else " " + word

            textLabel.attr("text", tempText.substring 1)
            return textLabel

        drawConnector = (parentNode, childNode, inChildPosition) =>
            parentBottomY = parentNode.attr("y") + @options.boxHeight
            parentBottomX = parentNode.attr("x") + (@options.boxWidth / 2)
            childSideY = childNode.attr("y") + (@options.boxHeight / 2)
            childSideX = childNode.attr("x")
            calculatedPath = "M #{parentBottomX} #{parentBottomY} L #{parentBottomX} #{childSideY} L #{childSideX} #{childSideY}"
            connector1 = @paper.path calculatedPath

        #execute
        renderNode @options.nodeData, @currX

    handleOpenClose = (node) ->
        elementData = node.data("elementData")
        elementData.childrenOpen = not elementData.childrenOpen
        @render() #redraw

    handleNodeClick = (node) ->
        @currentlySelectedNode.attr @defaultNodeFill
        node.attr @selectedNodeFill
        @currentlySelectedNode = node

this.SVGTree = SVGTree
