import wx
import Image
import ImageFilter
import math

def BitmapToPil(bitmap):

    return ImageToPil(BitmapToImage(bitmap))


def BitmapToImage(bitmap):

    return wx.ImageFromBitmap(bitmap)


def PilToBitmap(pil):

    return ImageToBitmap(PilToImage(pil))


def PilToImage(pil):

    image = wx.EmptyImage(pil.size[0], pil.size[1])
    image.SetData(pil.convert('RGB').tostring())
    return image


def PiltoImageAlpha(pil, alpha=True):

   if alpha:
       image = apply(wx.EmptyImage, pil.size)
       image.SetData(pil.convert( "RGB").tostring())
       image.SetAlphaData(pil.convert("RGBA").tostring()[3::4])
   else:
       image = wx.EmptyImage(pil.size[0], pil.size[1])
       new_image = pil.convert('RGB')
       data = new_image.tostring()
       image.SetData(data)
       
   return image


def ImageToPil(image):

    pil = Image.new('RGB',(image.GetWidth(), image.GetHeight()))
    pil.fromstring(image.GetData())
    return pil


def ImageToBitmap(image):

    return image.ConvertToBitmap()


def DropShadow(image, offset=(5,5), background=0xffffff, shadow=0x444444, 
              border=8, iterations=3):
    """
    Add a gaussian blur drop shadow to an image.  

    image       - The image to overlay on top of the shadow.
    offset      - Offset of the shadow from the image as an (x,y) tuple.  Can be
                  positive or negative.
    background  - Background colour behind the image.
    shadow      - Shadow colour (darkness).
    border      - Width of the border around the image.  This must be wide
                  enough to account for the blurring of the shadow.
    iterations  - Number of times to apply the filter.  More iterations 
                  produce a more blurred shadow, but increase processing time.
    """

    # Create the backdrop image -- a box in the background colour with a 
    # shadow on it.
    totalWidth = image.size[0] + abs(offset[0]) + 2*border
    totalHeight = image.size[1] + abs(offset[1]) + 2*border
    back = Image.new(image.mode, (totalWidth, totalHeight), background)

    # Place the shadow, taking into account the offset from the image
    shadowLeft = border + max(offset[0], 0)
    shadowTop = border + max(offset[1], 0)
    back.paste(shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0],
                        shadowTop + image.size[1]])

    # Apply the filter to blur the edges of the shadow.  Since a small kernel
    # is used, the filter must be applied repeatedly to get a decent blur.
    n = 0
    while n < iterations:
        back = back.filter(ImageFilter.BLUR)
        n += 1
      
    # Paste the input image onto the shadow backdrop  
    imageLeft = border - min(offset[0], 0)
    imageTop = border - min(offset[1], 0)
    back.paste(image, (imageLeft, imageTop))

    return back


class ScrolledImage(wx.ScrolledWindow):

    def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):

        wx.ScrolledWindow.__init__(self, parent, id, pos, size)        

        self.zoom_levels = range(8, 1, -1) + [1./r for r in range(1, 9)]
        self.zoom_level = self.zoom_levels.index(1.)
        self.scale = 1.
        self.size = wx.Size(0, 0)
        self.wheel = 0
        self.SetExtraStyle(0)

        self.backcolour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)

        self.MainFrame = wx.GetTopLevelParent(self)        


    def CalculateShadow(self, image):
        
        size = image.size
        
        if size[0] <= 32:
            xshadow = 3
        elif 32 < size[0] <= 200:
            xshadow = 5
        elif 200 < size[0] <= 600:
            xshadow = 8
        else:
            xshadow = 10

        if size[1] <= 32:
            yshadow = 3
        elif 32 < size[1] <= 200:
            yshadow = 5
        elif 200 < size[1] <= 600:
            yshadow = 8
        else:
            yshadow = 10

        border = max(xshadow, yshadow)

        return xshadow, yshadow, border        
            

    def ShowImage(self, image):

        PIL_bitmap = Image.open(image)

        x, y, border = self.CalculateShadow(PIL_bitmap)
                
        PIL_bitmap = DropShadow(PIL_bitmap, offset=(x, y), border=border)
        bmp = PilToBitmap(PIL_bitmap)

        self.SetScrollbars(1, 1, PIL_bitmap.size[0], PIL_bitmap.size[1], 0, 0)

        size = self.GetSize()

        xmin = (size.x - PIL_bitmap.size[0])/2
        ymin = (size.y - PIL_bitmap.size[1])/2
        
        self.currentbmp = bmp
        self.size = wx.Size(self.currentbmp.GetSize().width*self.scale, self.currentbmp.GetSize().height*self.scale)
        self.SetViewScale(self.zoom_levels[self.zoom_level])
        
        self.Refresh()
        

    def Zoom(self, dir):

        self.zoom_level += dir
        if self.zoom_level < 0:
            self.zoom_level = 0
            return
        elif self.zoom_level >= len(self.zoom_levels):
            self.zoom_level = len(self.zoom_levels)-1
            return

        self.SetViewScale(self.zoom_levels[self.zoom_level])
        self.MainFrame.bottompanel.SetScrollValue(self.zoom_level)


    def OnPaint(self, event):

        self.SetScale(1./self.scale,1./self.scale)
        dc = wx.PaintDC(self)
        dc.SetBackground(wx.Brush(self.backcolour))

        iw, ih = self.size
##        if iw < self.windowsize.x or ih < self.windowsize.y:
        dc.Clear()
            
        self.DoPaint(dc)
        del dc
        self.SetScale(1., 1.)
        

    def DoPaint(self, dc=None):

        if dc is None:
            dc = wx.ClientDC(self)

        self.PrepareDC(dc)

        iw, ih = self.size

        xmin = (self.windowsize.x - iw)/2
        ymin = (self.windowsize.y - ih)/2

        bm_dc = wx.MemoryDC()        
        bm_dc.SelectObject(self.currentbmp)

##        if xmin >= 0 and ymin >= 0:
##            self.DrawContour(bm_dc)

        if xmin < 0:
            xmin = 0
        if ymin < 0:
            ymin = 0

        dc.Blit(xmin*self.scale, ymin*self.scale, iw*self.scale, ih*self.scale, bm_dc, 0, 0, wx.COPY, True)
        bm_dc.SelectObject(wx.NullBitmap)


    def DrawContour(self, dc):

        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen(wx.BLACK, 2))

        dc.DrawRectangle(1, 1, self.currentbmp.GetWidth()-1, self.currentbmp.GetHeight()-1)


    def OnEraseBackground(self, event):
        
        pass


    def OnSize(self, event):

        iw, ih = self.size.width, self.size.height
        w, h = event.GetSize()

        scroll_x = w < iw
        scroll_y = h < ih

        scroll = scroll_x | scroll_y

        x, y = self.GetViewStart()

        self.SetScrollbars(scroll,scroll,iw,ih,x,y)
        self.windowsize = wx.Size(w, h)


    def SetViewScale(self, scale):

        self.SetScale(1.,1.)
        self.GetParent().SetSizeHints(-1,-1,-1,-1)

        x, y = self.GetViewStart()
        cw, ch = self.GetClientSize()
        midx, midy = x + cw / 2, y + ch / 2

        midx /= float(self.size.width)
        midy /= float(self.size.height)

        self.scale = scale
        self.size = wx.Size(self.currentbmp.GetSize().width/self.scale, self.currentbmp.GetSize().height/self.scale)

        iw, ih = self.size.width, self.size.height
        w, h = self.GetSize()

        scroll_x = w < iw
        scroll_y = h < ih

        x, y = self.GetViewStart()

        new_midx = midx * self.size.width
        new_midy = midy * self.size.height

        x = new_midx - cw/2
        y = new_midy - ch/2

        self.SetScrollbars(scroll_x,scroll_y,iw,ih,x,y)

        zoom_percent = round((1./self.scale)*100, 2)
        if zoom_percent == math.floor(zoom_percent):
            zoom_percent = str(zoom_percent).rstrip( '0' )[0:-1]

        self.SetScale(self.scale,self.scale)


    def OnMouseWheel(self, event):

        if event.m_controlDown:

            self.wheel += event.GetWheelRotation()
            if abs(self.wheel) >= event.GetWheelDelta():
                if event.GetWheelRotation() < 0:
                    self.Zoom(-1)
                    self.wheel+= event.GetWheelDelta()
                else:
                    self.Zoom(+1)
                    self.wheel-= event.GetWheelDelta()

        else:

            iw, ih = self.size.width, self.size.height
            w, h = self.GetSize()

            scroll_x = w < iw
            scroll_y = h < ih

            if scroll_x or scroll_y:            
                event.Skip()            
                