"""Subclass of HotspotDialog, which is generated by wxFormBuilder."""

import wx
import wx.combo
import os
from HotspotDialog import HotspotDialog


class CursorComboBox(wx.combo.OwnerDrawnComboBox):

    def __init__(self, parent, cursors):
        wx.combo.OwnerDrawnComboBox.__init__(self, parent, wx.ID_ANY, choices=cursors.keys(), style=wx.CB_READONLY)
        self.cursors = cursors

    # Overridden from OwnerDrawnComboBox, called to draw each
    # item in the list
    def OnDrawItem(self, dc, rect, item, flags):
        if item == wx.NOT_FOUND:
            # painting the control, but there is no valid item selected yet
            return

        r = wx.Rect(*rect)  # make a copy
        #r.Deflate(3, 5)

        current_cursor = self.cursors.keys()[item]

        dc.DrawBitmap(self.cursors[current_cursor], r.x + 5, r.y + 2, True)
        dc.DrawText(current_cursor, r.x + 10 + self.cursors[current_cursor].GetSize().GetWidth(), r.y + 2)


    # Overridden from OwnerDrawnComboBox, called for drawing the
    # background area of each item.
    def OnDrawBackground(self, dc, rect, item, flags):
        # If the item is selected, or its item # iseven, or we are painting the
        # combo control itself, then use the default rendering.
        if (item & 1 == 0 or flags & (wx.combo.ODCB_PAINTING_CONTROL |
                                      wx.combo.ODCB_PAINTING_SELECTED)):
            wx.combo.OwnerDrawnComboBox.OnDrawBackground(self, dc, rect, item, flags)
            return

        # Otherwise, draw every other background with different colour.
        bgCol = wx.Colour(240,240,250)
        dc.SetBrush(wx.Brush(bgCol))
        dc.SetPen(wx.Pen(bgCol))
        dc.DrawRectangleRect(rect);



    # Overridden from OwnerDrawnComboBox, should return the height
    # needed to display an item in the popup, or -1 for default
    def OnMeasureItem(self, item):
        return 21 # each bitmap is 17 pixels high. Add 2 pixels of marhin on top and bottom

    # Overridden from OwnerDrawnComboBox.  Callback for item width, or
    # -1 for default/undetermined
    def OnMeasureItemWidth(self, item):
        return -1; # default - will be measured from text width

MapSelectorSize = [400, 300]

class MapSelector(wx.PopupTransientWindow ):

    def __init__(self, parent, position, bgImage, age, currentSlideId, clickCallback):
        wx.PopupTransientWindow.__init__(self, parent, wx.BORDER_SIMPLE)

        self.currentSlideId = currentSlideId
        self.clickCallback = clickCallback

        self.standpoint2dIcon = wx.Bitmap("data/icon_standpoint2d.png")
        self.standpoint3dIcon = wx.Bitmap("data/icon_standpoint3d.png")
        self.youAreHereIcon = wx.Bitmap("data/youAreHere.png")

        self.iconOffsetW = self.standpoint2dIcon.GetWidth()/2
        self.iconOffsetH = self.standpoint2dIcon.GetHeight()/2

        self.x_scroll = 0
        self.y_scroll = 0

        # Center on the currently selected standpoint
        for currslide in age.getSlides():
            if currslide.getId() == currentSlideId:
                self.x_scroll = currslide.getMinimapX() - MapSelectorSize[0]/2
                self.y_scroll = currslide.getMinimapY() - MapSelectorSize[1]/2
                break

        #self.Position(position - wx.Point(400, 300), wx.Size(1,1))
        self.SetPosition(position)
        self.SetSize(wx.Size(MapSelectorSize[0], MapSelectorSize[1]))
        self.bgImage = bgImage
        self.age = age
        self.SetBackgroundStyle(wx.BG_STYLE_PAINT)

        self.Bind(wx.EVT_PAINT, self.onPaintEvent)

        self.middleMouseButtonDown = False
        self.panCursor = wx.StockCursor(wx.CURSOR_SIZING)

        self.Bind(wx.EVT_LEFT_DOWN, self.onMouseDown)
        self.Bind(wx.EVT_MIDDLE_DOWN, self.onMiddleMouseButtonDown)
        self.Bind(wx.EVT_MIDDLE_UP, self.onMiddleMouseButtonUp)
        self.Bind(wx.EVT_MOTION, self.onMouseMove)
        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onMiddleMouseButtonUp)

    def onPaintEvent(self, evt):
        dc = wx.AutoBufferedPaintDC(self)
        dc.Clear()
        dc.DrawBitmap(self.bgImage, -self.x_scroll, -self.y_scroll)

        for currslide in self.age.getSlides():
            if currslide.getId() == self.currentSlideId:
                bmp = self.youAreHereIcon
            elif currslide.is3DSlide():
                bmp = self.standpoint3dIcon
            else:
                bmp = self.standpoint2dIcon

            dc.DrawBitmap(bmp,
                          currslide.getMinimapX() - self.iconOffsetW - self.x_scroll,
                          currslide.getMinimapY() - self.iconOffsetH - self.y_scroll)

    def onMouseDown(self, evt):
        mx = evt.GetX() + self.x_scroll
        my = evt.GetY() + self.y_scroll
        for currslide in self.age.getSlides():
            if mx > (currslide.getMinimapX() - self.iconOffsetW) and \
               mx < (currslide.getMinimapX() + self.iconOffsetW) and \
               my > (currslide.getMinimapY() - self.iconOffsetH) and \
               my < (currslide.getMinimapY() + self.iconOffsetH):
                print "You clicked on", currslide.getId()
                self.clickCallback(currslide.getId())
                self.Dismiss()

    def onMiddleMouseButtonDown(self, evt):
        self.SetFocus()

        self.middleMouseButtonDown = True
        self.CaptureMouse()
        self.drag_previous_x = evt.GetX()
        self.drag_previous_y = evt.GetY()

    def onMiddleMouseButtonUp(self, evt):
        if self.HasCapture():
            self.ReleaseMouse()
        self.middleMouseButtonDown = False
        self.SetCursor(wx.NullCursor)

    def onMouseMove(self, evt):

        if self.middleMouseButtonDown:
            d_x = self.drag_previous_x - evt.GetX()
            d_y = self.drag_previous_y - evt.GetY()

            self.x_scroll += d_x
            self.y_scroll += d_y
            self.Refresh()

            self.drag_previous_x = evt.GetX()
            self.drag_previous_y = evt.GetY()
            self.SetCursor(self.panCursor)


# =================================================================================================
# =================================================================================================

IMG_SIZE = 256

# Implementing HotspotDialog
class IlathidHotspotDialog( HotspotDialog ):
    def __init__( self, parent, hotspot, cursors, age, ageDir, slide, bgImage, dialogClosedCallback ):
        HotspotDialog.__init__( self, parent )
        self.hotspot = hotspot
        self.age = age
        self.ageDir = ageDir
        self.slide = slide
        self.bgImage = bgImage
        self.dialogClosedCallback = dialogClosedCallback

        self.ccb = CursorComboBox(self.m_cursorPopupContainer, cursors)
        ccb_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ccb_sizer.Add(self.ccb)
        self.m_cursorPopupContainer.SetSizer(ccb_sizer)
        self.ccb.SetValue(hotspot.getCursor())

        self.m_action.SetValue(hotspot.getAction() if hotspot.getAction() is not None else "")

        self.currentDest = None
        self.currentDestImages = []
        self.destOrientation = hotspot.getDestOrientation()

        self.m_destinationChoice.Clear()
        self.m_destinationChoice.Append("(none)")
        for curr in age.getSlides():
            self.m_destinationChoice.Append(curr.getId())

        dest = hotspot.getDestination()
        if dest is None:
            self.m_destinationChoice.SetStringSelection("(none)")
        else:
            self.m_destinationChoice.SetStringSelection(dest)
            self.setDestination(dest)

        self.mouseDown = False
        self.m_orientation_preview.SetBackgroundStyle(wx.BG_STYLE_PAINT)

        self.m_orientation_preview.Bind(wx.EVT_PAINT, self.onPaintOrientationPreview)
        self.m_orientation_preview.Bind(wx.EVT_LEFT_DOWN, self.onMouseDown)
        self.m_orientation_preview.Bind(wx.EVT_LEFT_UP, self.onMouseUp)
        self.m_orientation_preview.Bind(wx.EVT_MOTION, self.onMouseMove)

    # ---------------------------------------------------------------------------------------------

    # Called when the destination standpoint is modified
    def setDestination(self, dest):
        self.currentDest = dest
        self.currentDestImages = []

        if dest is None:
            return

        slide = None

        try:
            slide = self.age.getSlideNamed(dest)
        except:
            pass # let slide be None

        if slide is None:
            print "Cannot locate slide named '" + dest + "'"
            return
        if not slide.is3DSlide():
            return

        files = slide.getFiles()
        l = wx.LogNull() # shut up warnings, when images don't load it will be visible enough in the panel

        try:
            wx.BeginBusyCursor()
            for i in range(0, 4):
                archive = files[i].getArchive()
                if archive is None: archive = ""

                # TODO: support for archives
                print "Loading", os.path.join(self.ageDir, "slides", files[i].getPath())
                tmp_bmp = wx.Image(os.path.join(self.ageDir, "slides", files[i].getPath()), wx.BITMAP_TYPE_ANY)
                if tmp_bmp.IsOk():
                    tmp_bmp.Rescale( tmp_bmp.GetWidth()/4, tmp_bmp.GetHeight()/4, wx.IMAGE_QUALITY_HIGH )
                    self.currentDestImages.append( tmp_bmp.ConvertToBitmap() )
                else:
                    self.currentDestImages.append( None )
        finally:
            wx.EndBusyCursor()

    # ---------------------------------------------------------------------------------------------

    # Handlers for HotspotDialog events.
    def onSelectCursor( self, event ):
        pass

    # ---------------------------------------------------------------------------------------------

    def onSelectDestination( self, event ):
        dest = self.m_destinationChoice.GetStringSelection()
        self.setDestination(dest)
        self.m_orientation_preview.Refresh()

    # ---------------------------------------------------------------------------------------------

    def onOKButton(self, event):
        self.hotspot.setCursor(self.ccb.GetValue())

        dest = self.m_destinationChoice.GetStringSelection()
        if dest == "(none)":
            dest = None
        self.hotspot.setDestination(dest)
        self.hotspot.setDestOrientation(self.destOrientation)

        actionVal = self.m_action.GetValue()
        if len(actionVal) == 0: actionVal = None

        self.hotspot.setAction(actionVal)
        self.EndModal(self.GetReturnCode())

        self.dialogClosedCallback()

    # ---------------------------------------------------------------------------------------------

    # Paint the destination preview panel, used to select the orientation on the destination standpoint
    def onPaintOrientationPreview(self, evt):
        dc = wx.BufferedPaintDC(self.m_orientation_preview)
        dc.Clear()

        orientationPreviewX = int((360.0 - self.destOrientation)/360.0*(IMG_SIZE*4))
        selectedAreaX = int((IMG_SIZE*4.0)/2.0 - IMG_SIZE/2.0)

        x = selectedAreaX
        for currImg in self.currentDestImages:
            if currImg is not None:
                dc.DrawBitmap(currImg, x + orientationPreviewX, 0)
                dc.DrawBitmap(currImg, x + orientationPreviewX - IMG_SIZE*4, 0)
            if x + orientationPreviewX > IMG_SIZE*4:
                x -= IMG_SIZE*4
            x += IMG_SIZE

        dc.SetPen(wx.Pen(wx.BLACK, 3))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.DrawRectangle(selectedAreaX + 1, 1, IMG_SIZE - 2, IMG_SIZE - 2)
        dc.SetPen(wx.Pen(wx.WHITE, 1))
        dc.DrawRectangle(selectedAreaX + 1, 1, IMG_SIZE - 2, IMG_SIZE - 2)

        gc = wx.GraphicsContext.Create(dc)
        dc2 = wx.GCDC(gc)

        dc2.SetPen(wx.TRANSPARENT_PEN)
        dc2.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 150)))
        right_dark_x2 = selectedAreaX + IMG_SIZE
        dc2.DrawRectangle(right_dark_x2, 1, IMG_SIZE*4, IMG_SIZE - 2)
        dc2.DrawRectangle(0, 1, selectedAreaX, IMG_SIZE - 2)

        dc2.SetTextForeground(wx.WHITE)

        strOrientation = ""
        angles = [23, 68, 113, 158, 203, 248, 293, 338]
        if self.destOrientation > angles[-1] or self.destOrientation <= angles[0]:
            strOrientation = " (N)"
        elif self.destOrientation > angles[0] and self.destOrientation <= angles[1]:
            strOrientation = " (NE)"
        elif self.destOrientation > angles[1] and self.destOrientation <= angles[2]:
            strOrientation = " (E)"
        elif self.destOrientation > angles[2] and self.destOrientation <= angles[3]:
            strOrientation = " (SE)"
        elif self.destOrientation > angles[3] and self.destOrientation <= angles[4]:
            strOrientation = " (S)"
        elif self.destOrientation > angles[4] and self.destOrientation <= angles[5]:
            strOrientation = " (SW)"
        elif self.destOrientation > angles[5] and self.destOrientation <= angles[6]:
            strOrientation = " (W)"
        elif self.destOrientation > angles[6] and self.destOrientation <= angles[7]:
            strOrientation = " (NW)"

        dc2.DrawText(str(self.destOrientation) + " degrees" + strOrientation, int(IMG_SIZE*3.5), IMG_SIZE - 25)

    # ---------------------------------------------------------------------------------------------

    def onMouseDown(self, evt):
        self.m_orientation_preview.CaptureMouse()
        self.previous_drag_x = evt.GetX()
        self.mouseDown = True
        self.floatDestOrientation = self.destOrientation

    # ---------------------------------------------------------------------------------------------

    def onMouseMove(self, evt):
        if self.mouseDown:
            delta_x = self.previous_drag_x - evt.GetX()
            self.previous_drag_x = evt.GetX()

            self.floatDestOrientation += 360.0*delta_x / (IMG_SIZE*4.0)
            if self.floatDestOrientation < 0: self.floatDestOrientation += 360.0
            elif self.floatDestOrientation >= 360: self.floatDestOrientation -= 360.0

            self.destOrientation = int(self.floatDestOrientation)
            self.m_orientation_preview.Refresh()

    # ---------------------------------------------------------------------------------------------

    def onMouseUp(self, evt):
        if self.m_orientation_preview.HasCapture():
            self.m_orientation_preview.ReleaseMouse()
        self.mouseDown = False

    # ---------------------------------------------------------------------------------------------

    def onDeleteHotspot(self, event):
        answer = wx.MessageBox("Do you really want to delete this hotspot?", "Confirm", wx.OK | wx.CANCEL | wx.CENTRE)
        if answer != wx.OK:
            return
        self.slide.removeHotspot(self.hotspot)
        self.EndModal(self.GetReturnCode())
        self.dialogClosedCallback()

    # ---------------------------------------------------------------------------------------------

    def setDestinationSlide(self, s):
        # compute a default angle value
        otherSlide = self.age.getSlideNamed(s)
        import math
        dest_angle = math.atan2( otherSlide.getMinimapY() - self.slide.getMinimapY(),
                                  otherSlide.getMinimapX() - self.slide.getMinimapX() )*180.0/math.pi + 90.0
        if dest_angle < 0.0: dest_angle += 360.0

        self.floatDestOrientation = dest_angle
        self.destOrientation = int(dest_angle)

        self.m_destinationChoice.SetStringSelection(s)
        self.onSelectDestination(None)

    # ---------------------------------------------------------------------------------------------

    def onSelectFromMap(self, event):
        pt = self.m_selectFromMapBtn.GetPosition()
        pt = wx.Point( pt[0] + self.m_selectFromMapBtn.GetSize().GetWidth(),
                       pt[1] + self.m_selectFromMapBtn.GetSize().GetHeight() )
        picker = MapSelector(self, pt, self.bgImage, self.age, self.slide.getId(),
                             lambda s: self.setDestinationSlide(s))
        picker.Popup()

