#
# Copyright (c) 2002, 2003, 2004, 2006, 2007 Art Haas
#
#               2009 Matteo Boscolo
#
# This file is part of PythonCAD.
# 
# PythonCAD 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; either version 2 of the License, or
# (at your option) any later version.
# 
# PythonCAD is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY 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 PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# gtk dimension code
#

import pygtk
pygtk.require('2.0')
import gtk
import pango

from PythonCAD.Generic.dimension import Dimension
from PythonCAD.Interface.Gtk import gtktext
from PythonCAD.Generic import snap

#
# linear dimension motion-notify handler
#

def dim_pts_motion_notify_cb(gtkimage, widget, event, tool):
    _tx, _ty = tool.getLocation()
    _px, _py = gtkimage.coordToPixTransform(_tx, _ty)
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _cp = tool.getCurrentPoint()
    _segs = []
    if _cp is not None:
        _xc, _yc = _cp
        _segs.append((_px, _py, _xc, _yc))
    tool.setCurrentPoint(_x, _y)
    _segs.append((_px, _py, _x, _y))
    widget.window.draw_segments(_gc, _segs)
    return True

def dim_txt_motion_notify_cb(gtkimage, widget, event, tool):
    _ix, _iy = gtkimage.image.getCurrentPoint()
    _gc = gtkimage.getGC()
    _ex = int(event.x)
    _ey = int(event.y)
    _cp = tool.getCurrentPoint()
    _dim = tool.getDimension()
    _bar1, _bar2 = _dim.getDimBars()
    _crossbar = _dim.getDimCrossbar()
    _segs = []
    if _cp is not None:
        _ep1, _ep2 = _bar1.getEndpoints()
        _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_px1, _py1, _px2, _py2))
        _ep1, _ep2 = _bar2.getEndpoints()
        _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_px1, _py1, _px2, _py2))
        _ep1, _ep2 = _crossbar.getEndpoints()
        _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_px1, _py1, _px2, _py2))
    tool.setCurrentPoint(_ex, _ey)
    _dim.setLocation(_ix, _iy)
    _dim.calcDimValues(False)
    _ep1, _ep2 = _bar1.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    _ep1, _ep2 = _bar2.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    _ep1, _ep2 = _crossbar.getEndpoints()
    _px1, _py1 = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _px2, _py2 = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_px1, _py1, _px2, _py2))
    widget.window.draw_segments(_gc, _segs)
    return True

def add_dimension(gtkimage, tool=None):
    _tool = gtkimage.getImage().getTool()
    _init_func = _tool.getHandler("initialize")
    _image = gtkimage.getImage()
    _image.startAction()
    try:
        _tool.create(_image)
    finally:
        _image.endAction()
    _init_func(gtkimage)

def linear_text_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.image.getCurrentPoint()
    _ldim = tool.getDimension()
    _ldim.setLocation(_x, _y)
    _ldim.calcDimValues()
    _ldim.reset()
    add_dimension(gtkimage)
    return True
    
def linear_second_button_press_cb(gtkimage, widget, event, tool):
    _snapArray={'perpendicular':False,'tangent':False}
    _strPnt=snap.getSnapOnTruePoint(gtkimage,_snapArray)
    if _strPnt.point is not None:
        _x, _y = _strPnt.point.getCoords()
        tool.setSecondPoint(_strPnt.point)
        tool.setDimPosition(_x, _y)
        tool.clearCurrentPoint()
        tool.makeDimension(gtkimage.getImage())
        #
        # set GraphicsContext to Dimension color
        #
        _gc = gtkimage.getGC()
        _gc.set_function(gtk.gdk.INVERT)
        _col = tool.getDimension().getColor()
        _gc.set_foreground(gtkimage.getColor(_col))
        tool.setHandler("button_press", linear_text_button_press_cb)
        tool.setHandler("motion_notify", dim_txt_motion_notify_cb)
        gtkimage.setPrompt(_('Click where the dimension text should go.'))
        gtkimage.refresh()
    return True

def linear_first_button_press_cb(gtkimage, widget, event, tool):
    _snapArray={'perpendicular':False,'tangent':False}
    _strPnt=snap.getSnapOnTruePoint(gtkimage,_snapArray)
    if _strPnt is not None:
        _x, _y =_strPnt.point.getCoords()
        tool.setLocation(_x, _y)
        tool.setFirstPoint(_strPnt.point)
        tool.setHandler("button_press", linear_second_button_press_cb)
        tool.setHandler("motion_notify", dim_pts_motion_notify_cb)
        gtkimage.setPrompt(_('Click on the second point for the dimension.'))
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
    else:
        gtkimage.setPrompt(_('Click on the first point for the dimension.'))
        tool.setHandler("button_press", linear_first_button_press_cb)
        tool.setHandler("initialize", linear_mode_init)
    return True
#
# linear dimensions
#

def linear_mode_init(gtkimage, tool=None):
    gtkimage.setPrompt(_('Click on the first point for the dimension.'))
    _tool = gtkimage.getImage().getTool()
    _tool.setHandler("button_press", linear_first_button_press_cb)
    _tool.setHandler("initialize", linear_mode_init)

#
# horizontal dimensions
#

def horizontal_mode_init(gtkimage, tool=None):
    gtkimage.setPrompt(_('Click on the first point for the dimension.'))
    _tool = gtkimage.getImage().getTool()
    _tool.setHandler("button_press", linear_first_button_press_cb)
    _tool.setHandler("initialize", horizontal_mode_init)

#
# vertical dimensions
#


def vertical_mode_init(gtkimage, tool=None):
    gtkimage.setPrompt(_('Click on the first point for the dimension.'))
    _tool = gtkimage.getImage().getTool()
    _tool.setHandler("button_press", linear_first_button_press_cb)
    _tool.setHandler("initialize", vertical_mode_init)

#
# radial dimensions
#

def radial_txt_motion_notify_cb(gtkimage, widget, event, tool):
    _gc = gtkimage.getGC()
    _x = int(event.x)
    _y = int(event.y)
    _rdim = tool.getDimension()
    _crossbar = _rdim.getDimCrossbar()
    _cp = tool.getCurrentPoint()
    _segs = []
    if _cp is not None:
        _p1, _p2 = _crossbar.getEndpoints()
        _p0x, _p0y = gtkimage.coordToPixTransform(_p1[0], _p1[1])
        _p1x, _p1y = gtkimage.coordToPixTransform(_p2[0], _p2[1])
        _segs.append((_p0x, _p0y, _p1x, _p1y))
    tool.setCurrentPoint(_x, _y)
    _ix, _iy = gtkimage.image.getCurrentPoint()
    _rdim.setLocation(_ix, _iy)
    _rdim.calcDimValues(False)
    _p1, _p2 = _crossbar.getEndpoints()
    _p0x, _p0y = gtkimage.coordToPixTransform(_p1[0], _p1[1])
    _p1x, _p1y = gtkimage.coordToPixTransform(_p2[0], _p2[1])
    _segs.append((_p0x, _p0y, _p1x, _p1y))
    widget.window.draw_segments(_gc, _segs)
    return True

def radial_text_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.image.getCurrentPoint()
    _rdim = tool.getDimension()
    _rdim.setLocation(_x, _y)
    _rdim.calcDimValues()
    _rdim.reset()
    add_dimension(gtkimage)
    return True

def radial_button_press_cb(gtkimage, widget, event, tool):
    _tol = gtkimage.getTolerance()
    _image = gtkimage.getImage()
    _x, _y = _image.getCurrentPoint()
    _dc = None
    _layers = [_image.getTopLayer()]
    while(len(_layers)):
        _layer = _layers.pop()
        if _layer.isVisible():
            _cobjs = (_layer.getLayerEntities("circle") +
                      _layer.getLayerEntities("arc"))
            for _cobj in _cobjs:
                _mp = _cobj.mapCoords(_x, _y, _tol)
                if _mp is not None:
                    _dc = _cobj
                    break
        _layers.extend(_layer.getSublayers())
    if _dc is not None:
        _x, _y = _mp
        tool.setDimObject(_dc)
        tool.setDimPosition(_x, _y)
        tool.makeDimension(_image)
        tool.setHandler("motion_notify", radial_txt_motion_notify_cb)
        tool.setHandler("button_press", radial_text_button_press_cb)
        gtkimage.setPrompt(_('Click where the dimension text should be placed.'))
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
    return True

def radial_mode_init(gtkimage, tool=None):
    gtkimage.setPrompt(_('Click on an arc or a circle to dimension.'))
    _tool = gtkimage.getImage().getTool()
    _tool.setHandler("initialize", radial_mode_init)
    _tool.setHandler("button_press", radial_button_press_cb)

#
# angular dimensions
#

def adim_txt_motion_notify_cb(gtkimage, widget, event, tool):
    _ix, _iy = gtkimage.image.getCurrentPoint()
    _gc = gtkimage.getGC()
    _ex = int(event.x)
    _ey = int(event.y)
    _cp = tool.getCurrentPoint()
    _adim = tool.getDimension()
    _vx, _vy = _adim.getVertexPoint().getCoords()
    _px, _py = gtkimage.coordToPixTransform(_vx, _vy)
    _win = widget.window
    _bar1, _bar2 = _adim.getDimBars()
    _crossarc = _adim.getDimCrossarc()
    _segs = []
    if _cp is not None:
        #
        # draw bars
        #
        _ep1, _ep2 = _bar1.getEndpoints()        
        _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_p1x, _p1y, _p2x, _p2y))
        _ep1, _ep2 = _bar2.getEndpoints()
        _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
        _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
        _segs.append((_p1x, _p1y, _p2x, _p2y))
        _win.draw_segments(_gc, _segs)
        del _segs[:]
        #
        # draw arc
        #
        _sa = _crossarc.getStartAngle()
        _ea = _crossarc.getEndAngle()
        _rad = int(_crossarc.getRadius()/gtkimage.getUnitsPerPixel())
        _pxmin = _px - _rad
        _pymin = _py - _rad
        _cw = _ch = _rad * 2
        if _sa < _ea:
            _sweep = _ea - _sa
        else:
            _sweep = 360.0 - (_sa - _ea)
        _win.draw_arc(_gc, False, _pxmin, _pymin, _cw, _ch,
                      int(_sa * 64), int(_sweep * 64))
    tool.setCurrentPoint(_ex, _ey)
    _adim.setLocation(_ix, _iy)
    _adim.calcDimValues(False)
    #
    # draw bars
    #
    _ep1, _ep2 = _bar1.getEndpoints()        
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    _ep1, _ep2 = _bar2.getEndpoints()
    _p1x, _p1y = gtkimage.coordToPixTransform(_ep1[0], _ep1[1])
    _p2x, _p2y = gtkimage.coordToPixTransform(_ep2[0], _ep2[1])
    _segs.append((_p1x, _p1y, _p2x, _p2y))
    _win.draw_segments(_gc, _segs)
    #
    # draw arc
    #
    _sa = _crossarc.getStartAngle()
    _ea = _crossarc.getEndAngle()
    _rad = int(_crossarc.getRadius()/gtkimage.getUnitsPerPixel())
    _pxmin = _px - _rad
    _pymin = _py - _rad
    _cw = _ch = _rad * 2
    if _sa < _ea:
        _sweep = _ea - _sa
    else:
        _sweep = 360.0 - (_sa - _ea)
    _win.draw_arc(_gc, False, _pxmin, _pymin, _cw, _ch,
                  int(_sa * 64), int(_sweep * 64))
    return True

def angular_pts_button_press_cb(gtkimage, widget, event, tool):
    _tol = gtkimage.getTolerance()
    _image = gtkimage.getImage()
    _x, _y = _image.getCurrentPoint()
    _active_layer = _image.getActiveLayer()
    _pt = None
    _pts = _active_layer.find('point',_x, _y, _tol)
    if len(_pts) > 0:
        _pt = _pts[0]
    if _pt is None:
        _layers = [_image.getTopLayer()]
        while(len(_layers)):
            _layer = _layers.pop()
            if _layer is not _active_layer and _layer.isVisible():
                _pts = _layer.find('point', _x, _y, _tol)
                if len(_pts) > 0:
                    _pt = _pts[0]
                    break
            _layers.extend(_layer.getSublayers())
    if _pt is not None:
        _x, _y = _pt.getCoords()
        tool.storeCoords(_x, _y)
        if len(tool) == 2:
            tool.pushObject(_pt)
        else:
            from PythonCAD.Generic.dimension import AngularDimension
            _p1 = tool.popObject()
            _vp = tool.popObject()
            _ds = _image.getOption("DIM_STYLE")
            _adim = AngularDimension(_vp, _p1, _pt, _x, _y, _ds)
            tool.pushObject(_adim)
    return True

def angular_text_button_press_cb(gtkimage, widget, event, tool):
    _x, _y = gtkimage.image.getCurrentPoint()
    _adim = tool.getDimension()
    _adim.setLocation(_x, _y)
    _adim.calcDimValues()
    _adim.reset()
    add_dimension(gtkimage)
    return True

def angular_second_button_press_cb(gtkimage, widget, event, tool):
    _tol = gtkimage.getTolerance()
    _image = gtkimage.getImage()
    _x, _y = _image.getCurrentPoint()
    _layers = [_image.getTopLayer()]
    while len(_layers):
        _layer = _layers.pop()
        if _layer.isVisible():
            _pt = None
            _pts = _layer.find('point', _x, _y, _tol)
            if len(_pts) > 0:
                _pt = _pts[0]
            if _pt is not None:
                _x, _y = _pt.getCoords()
                tool.setLocation(_x, _y)
                tool.setSecondPoint(_pt)
                tool.setDimPosition(_x, _y)
                tool.makeDimension(_image)
                #
                # set GraphicsContext to Dimension color
                #
                _gc = gtkimage.getGC()
                _gc.set_function(gtk.gdk.INVERT)
                _col = tool.getDimension().getColor()
                _gc.set_foreground(gtkimage.getColor(_col))
                tool.setHandler("button_press", angular_text_button_press_cb)
                tool.setHandler("motion_notify", adim_txt_motion_notify_cb)
                gtkimage.setPrompt(_('Click where the dimension text should be located.'))
                gtkimage.refresh()
                break
        _layers.extend(_layer.getSublayers())
    return True
    
def angular_first_button_press_cb(gtkimage, widget, event, tool):
    _tol = gtkimage.getTolerance()
    _image = gtkimage.getImage()
    _x, _y = _image.getCurrentPoint()
    _layers = [_image.getTopLayer()]
    while len(_layers):
        _layer = _layers.pop()
        if _layer.isVisible():
            _pt = None
            _pts = _layer.find('point', _x, _y, _tol)
            if len(_pts) > 0:
                _pt = _pts[0]
            if _pt is not None:
                _x, _y = _pt.getCoords()
                tool.setLocation(_x, _y)
                tool.setFirstPoint(_pt)
                tool.setHandler("button_press", angular_second_button_press_cb)
                gtkimage.setPrompt(_('Click on the second point for the dimension.'))
                break
        _layers.extend(_layer.getSublayers())
    return True

def _test_layer(l, x, y, tol):
    _pt = _arc = None
    _pts = l.find('point', x, y)
    if len(_pts) > 0:
        _pt = _pts[0]
    if _pt is None:
        _pts = l.find('point', x, y, tol)
        if len(_pts) > 0:
            _pt = _pts[0]
        if _pt is None:
            _arc_pt = None
            for _arc in l.getLayerEntities("arc"):
                _arc_pt = _arc.mapCoords(x, y, tol)
                if _arc_pt is not None:
                    break
            if _arc_pt is None:
                _arc = None # no hits on any arcs ...
    return _pt, _arc

def angular_initial_button_press_cb(gtkimage, widget, event, tool):
    _tol = gtkimage.getTolerance()
    _image = gtkimage.getImage()
    _x, _y = _image.getCurrentPoint()
    _active_layer = _image.getActiveLayer()
    _pt, _arc = _test_layer(_active_layer, _x, _y, _tol)
    if _pt is None and _arc is None:
        _layers = [_image.getTopLayer()]
        while len(_layers):
            _layer = _layers.pop()
            if _layer is not _active_layer and _layer.isVisible():
                _pt, _arc = _test_layer(_layer, _x, _y, _tol)
                if _pt is not None or _arc is not None:
                    break
            _layers.extend(_layer.getSublayers())
    if _pt is not None:
        tool.setVertexPoint(_pt)
        tool.setHandler("button_press", angular_first_button_press_cb)
        gtkimage.setPrompt(_('Click on the first endpoint for the dimension.'))
    elif _arc is not None:
        _cp = _arc.getCenter()
        tool.setVertexPoint(_cp)
        _ep1, _ep2 = _arc.getEndpoints()
        _ex, _ey = _ep1
        _pts = _active_layer.find('point', _ex, _ey)
        assert len(_pts) > 0, "Missing arc first endpoint"
        tool.setFirstPoint(_pts[0])
        _ex, _ey = _ep2
        _pts = _active_layer.find('point', _ex, _ey)
        assert len(_pts) > 0, "Missing arc second endpoint"
        tool.setSecondPoint(_pts[0])
        tool.setDimPosition(_x, _y)
        tool.makeDimension(_image)
        tool.setHandler("button_press", angular_text_button_press_cb)
        tool.setHandler("motion_notify", adim_txt_motion_notify_cb)
        gtkimage.getGC().set_function(gtk.gdk.INVERT)
        gtkimage.setPrompt(_('Click where the dimension text should be located.'))
    return True

def angular_mode_init(gtkimage, tool=None):
    gtkimage.setPrompt(_('Click on the angle vertex point or an arc.'))
    _tool = gtkimage.getImage().getTool()
    _tool.initialize()
    _tool.setHandler("button_press", angular_initial_button_press_cb)
    _tool.setHandler("initialize", angular_mode_init)
