import time
from string import *
import os
import os.path
import thread
import traceback
import sys
import re
import string
import urllib
import webbrowser
import random
from math import sqrt


import wxversion
wxversion.select(["2.6", "2.7", "2.8"])
import wx

class MapCanvas(wx.ScrolledWindow):
    def __init__(self, parent, openrpg):
        wx.ScrolledWindow.__init__(self, parent, wx.ID_ANY, style=wx.HSCROLL | wx.VSCROLL | wx.NO_FULL_REPAINT_ON_RESIZE | wx.SUNKEN_BORDER)

        self.RGBHex = RGBHex()

        self._SetSize((1000,1000))
        self.toolWnd = parent

        self.gridLayer = GridLayer(self)
        self.circleLayer = CircleLayer(self)

        self.tiles = []
        self.lines = []
        self.minis = []
        self.circles = []
        self.text = []

        self.zoomScale = 1.0
        self.lastZoomTime = time.time()
        self.lastZoomScale = 1.0
        self.zoomTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnZoomTimer, self.zoomTimer)
        self.zoomTimer.Start(1000)

        self.imgCache = {}

        #self.circles.append(MapCircle('circle-1', wx.Point(150,150), 50))


        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_MOUSEWHEEL, self.OnZoom)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        self.Bind(wx.EVT_MOTION, self.OnMotion)
        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBackground)

        self.roleTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnRoleTimer, self.roleTimer)


        wx.CallAfter(self.OnSize, None)

    #Public API
    def UpdateMap(self):
        pos = self.GetViewStart()
        unit = self.GetScrollPixelsPerUnit()
        offset = [-pos[0]*unit[0], -pos[1]*unit[1]]

        cdc = wx.ClientDC(self)
        bdc = wx.BufferedDC(cdc, self._buffer)
        bdc.Clear()
        #self.PrepareDC(bdc)
        dc = wx.GCDC(bdc)
        dc.SetBackgroundMode(wx.TRANSPARENT)

        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.SetBrush(wx.TRANSPARENT_BRUSH)

        #Draw BG Color
        r,g,b = self.RGBHex.rgb_tuple(self.backgroundColor)
        brush = wx.Brush(wx.Color(r,g,b,255))
        size  = self.GetClientSizeTuple()
        dc.SetBrush(brush)
        dc.DrawRectangle(0, 0, size[0]+2, size[1]+2)
        dc.SetBrush(wx.NullBrush)


        dc.SetUserScale(self.zoomScale, self.zoomScale)
        #Draw BG Image
        if self.bgImage != None:
            if self.bgType == 'Image':
                dc.DrawBitmap(self.bgImage, 0, 0)
            else:
                bmpW = self.bgImage.GetWidth()
                bmpH = self.bgImage.GetHeight()

                pos = wx.Point(0,0)
                while pos.x < self.size[0]:
                    dc.DrawBitmap(self.bgImage, pos.x, pos.y)
                    while pos.y < self.size[1]:
                        pos.y += bmpW
                        dc.DrawBitmap(self.bgImage, pos.x, pos.y)
                    pos.y = 0
                    pos.x += bmpH

        #Draw Tiles
        for tile in self.tiles:
            tile.Draw(dc)

        #Draw Grid
        self.gridLayer.Draw(dc, offset)

        #Draw Lines
        for line in self.lines:
            line.Draw(dc, offset)

        #Draw Mini's
        for mini in self.minis:
            mini.Draw(dc, offset)

        #Draw Circles
        for circle in self.circles:
            circle.Draw(dc, offset)

        #Draw Text
        for word in self.text:
            word.Draw(dc, offset)



        #Reset the Zoom as we dont need to zoom in on the fog
        dc.SetUserScale(1, 1)

        #Draw Fog
        if self.useFog:
            if self.fogRegion == None:
                r,g,b = self.RGBHex.rgb_tuple(self.backgroundColor)
                brush = wx.Brush(wx.Color(r,g,b,255))
                if self.role == 'GM':
                    brush = wx.Brush(wx.Color(r,g,b,128))

                dc.SetBrush(brush)
                dc.DrawRectangle(0, 0, size[0]+2, size[1]+2)

        dc.SetBrush(wx.NullBrush)

        if self.zoomScale != 1.0000:
            dc.DrawText("Zoom Factor: " + str(self.zoomScale), 0, 0)

        wx.CallAfter(self.Refresh)

    def Clear(self):
        self.bgImage = None
        self.bgType = 'Image'

        self.backgroundColor = '#008040'
        r, g, b = self.RGBHex.rgb_tuple(self.backgroundColor)
        #self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.BGColorBtn)

        self.gridType = 'Square'
        self.gridLines = wx.SOLID
        self.gridSnap = True
        self.gridSize = 60
        self.gridColor = "#000000"

        self.whiteboardColor = "#000000"
        r, g, b = self.RGBHex.rgb_tuple(self.whiteboardColor)
        #self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.ColorBtn)

        self.zoomScale = 1.0
        self.lastZoomTime = time.time()
        self.lastZoomScale = 1.0

        self.useFog = False
        self.fogRegion = None

        for i in xrange(len(self.tiles)):
            del self.tiles[0]

        for i in xrange(len(self.lines)):
            del self.lines[0]

        for i in xrange(len(self.minis)):
            del self.minis[0]

        for i in xrange(len(self.circles)):
            del self.circles[0]

        for i in xrange(len(self.text)):
            del self.text[0]

        #self.toolWnd.Freeze()
        #for btn in self.toolWnd.exclusiveToolList:
        #    self.toolWnd.exclusiveToolList[btn].SetToggled(False)
        #self.toolWnd.SelectorBtn.SetToggled(True)
        #self.toolWnd.Thaw()


    #Map Events
    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self)
        #self.PrepareDC(dc)
        dc.Clear()
        dc.SelectObject(self._buffer)


        #pdc = wx.ClientDC(self)
        #self.PrepareDC(pdc)
        #if self.zoomScale != 1.0:
        #    pdc.DrawText("Zoom Factor: " + str(self.zoomScale), 0, 0)


    def OnSize(self, event):
        size  = self.GetClientSizeTuple()
        self._buffer = wx.EmptyBitmap(size[0]+2, size[1]+2)
        self._FixScroll()


    def OnZoom(self, event):
        if event.GetWheelRotation() < 0:
            self.zoomScale -= .1
            if self.zoomScale < .1:
                self.zoomScale = .1
            else:
                self.lastZoomTime = time.time()
                self.UpdateMap()
        else:
            self.zoomScale += .1

            if self.zoomScale > 5:
                self.zoomScale = 5
            else:
                self.lastZoomTime = time.time()
                self.UpdateMap()

    def OnLeftDown(self, event):
        if self.toolWnd.AddCircleBtn.GetToggled():
            self.circleLayer.OnLeftDown(event)

    def OnLeftDClick(self, event):
        pass

    def OnLeftUp(self, event):
        if self.toolWnd.AddCircleBtn.GetToggled():
            self.circleLayer.OnLeftUp(event)

    def OnRightDown(self, event):
        pass

    def OnMotion(self, event):
        pass

    def OnScroll(self, event):
        event.Skip()
        wx.CallAfter(self.UpdateMap)

    def OnZoomTimer(self, event):
        if (time.time() - self.lastZoomTime) >= 3 and self.lastZoomScale != self.zoomScale:
            #Send Zoome Notice to other clients
            self.lastZoomTime = time.time()
            self.lastZoomScale = self.zoomScale

    def OnRoleTimer(self, event):
        #Figure out the users role
        if self.session.my_role() == self.session.ROLE_GM:
            self.role = 'GM'
        elif self.session.my_role() == self.session.ROLE_PLAYER:
            self.role = 'Player'
        else:
            self.role = 'Lurker'

        if self.role == 'GM' and not self.toolWnd.gmToolBar.IsShown() and not (str(self.session.group_id) == '0' and str(self.session.status) == '1'):
            self.toolWnd.Freeze()
            self.toolWnd.gmToolBar.Show()
            self.toolWnd.Thaw()
        elif self.role == 'Player' and not (str(self.session.group_id) == '0' and str(self.session.status) == '1'):
            if self.toolWnd.gmToolBar.IsShown():
                self.toolWnd.Freeze()
                self.toolWnd.gmToolBar.Hide()
                self.toolWnd.Thaw()

            if not self.toolWnd.playerToolBar.IsShown():
                self.toolWnd.Freeze()
                self.toolWnd.playerToolBar.Show()
                self.toolWnd.Thaw()
        elif self.role == 'Lurker' or (str(self.session.group_id) == '0' and str(self.session.status) == '1'):
            if self.toolWnd.playerToolBar.IsShown():
                self.toolWnd.Freeze()
                self.toolWnd.gmToolBar.Hide()
                self.toolWnd.playerToolBar.Hide()
                self.toolWnd.Thaw()

        try:
            self.toolWnd.Layout()
        except:
            pass


    def OnBackground(self, event):
        pass

    def OnClose(self, event):
        self.zoomTimer.Stop()
        self.roleTimer.Stop()
        event.Skip()

    #Toolbar Events
    def OnDefaultBtn(self, event):
        self.Clear()
        wx.CallAfter(self.UpdateMap)

    def OnColorBtn(self, event):
        self.whiteboardColor = self.RGBHex.do_hex_color_dlg(self.toolWnd)
        r, g, b = self.RGBHex.rgb_tuple(self.whiteboardColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.ColorBtn)

    def OnBGColorBtn(self, event):
        self.backgroundColor = self.RGBHex.do_hex_color_dlg(self.toolWnd)
        r, g, b = self.RGBHex.rgb_tuple(self.backgroundColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.BGColorBtn)
        self.UpdateMap()

    def OnExlusiveBtn(self, event):
        id = event.GetId()
        #This is backwards because the Toggle Switch does not get set until AFTER The mouse gets released
        if not self.toolWnd.exclusiveToolList[id].GetToggled():
            self.toolWnd.Freeze()
            #Disable all mutualy exclusive tools
            for btn in self.toolWnd.exclusiveToolList:
                if self.toolWnd.exclusiveToolList[btn].GetId() != id:
                    self.toolWnd.exclusiveToolList[btn].SetToggled(False)
            self.toolWnd.Thaw()
        else:
            wx.CallAfter(self.toolWnd.SelectorBtn.SetToggled, True)

    #Private Methods
    def _SetSize(self, size):
        if size[0] == -1:
            size[0] = self.size[0]
        if size[1] == -1:
            size[1] = self.size[1]

        if size[0] < 300:
            size = (300, size[1])
        if size[1] < 300:
            size = (size[0], 300)

        self.sizeChanged = 1
        self.size = size
        self._FixScroll()

    def _FixScroll(self):
        scale = 1
        pos = self.GetViewStart()
        unit = self.GetScrollPixelsPerUnit()
        pos = [pos[0]*unit[0],pos[1]*unit[1]]
        size = self.GetClientSize()
        unit = [10*scale,10*scale]
        if (unit[0] == 0 or unit[1] == 0):
            return
        pos[0] /= unit[0]
        pos[1] /= unit[1]
        mx = [int(self.size[0]*scale/unit[0])+1, int(self.size[1]*scale/unit[1]+1)]
        self.SetScrollbars(unit[0], unit[1], mx[0], mx[1], pos[0], pos[1])


class MapCircle:
    def __init__(self, id, center, radius, color="#000000"):
        self.id = id
        self.center = center
        self.radius = radius
        self.color = color

        self.RGBHex = RGBHex()

    def Draw(self, dc, offset):
        dc.SetPen(wx.TRANSPARENT_PEN)

        r, g, b = self.RGBHex.rgb_tuple(self.color)
        brush = wx.Brush(wx.Color(r, g, b, 128))
        dc.SetBrush(brush)

        dc.DrawCircle(self.center.x+offset[0], self.center.y+offset[1], self.radius)

        dc.SetBrush(wx.TRANSPARENT_BRUSH)




class CircleLayer:
    def __init__(self, canvas):
        self.canvas = canvas


    def OnLeftDown(self, event):
        dc = wx.ClientDC(self.canvas)
        self.center = event.GetLogicalPosition(dc)

    def OnMotion(self, event):
        pass

    def OnLeftUp(self, event):
        dc = wx.ClientDC(self.canvas)
        pos = event.GetLogicalPosition(dc)
        xd = (self.center.x-pos.x)*(self.center.x-pos.x)
        yd = (self.center.y-pos.y)*(self.center.y-pos.y)
        radius = sqrt(xd+yd)

        print self.center
        print pos
        print radius

        if radius > 15:
            #Create ID
            self.canvas.lines.append(MapCircle(1, self.center, radius, self.canvas.whiteboardColor))
            self.canvas.UpdateMap()

class GridLayer:
    def __init__(self, canvas):
        self.canvas = canvas
        self.RGBHex = RGBHex()

    def Draw(self, dc, offset):
        r, g, b = self.RGBHex.rgb_tuple(self.canvas.gridColor)
        pen = wx.Pen(wx.Color(r, g, b, 255), 1, self.canvas.gridLines)
        dc.SetPen(pen)

        if self.canvas.gridType == 'Square':
            self._DrawSquare(dc, offset)

        dc.SetPen(wx.NullPen)

    def _DrawSquare(self, dc, offset):
        y = offset[1]
        while y < self.canvas.size[1]:
            dc.DrawLine(offset[0], y, self.canvas.size[0], y)
            y += 60

        x = offset[0]
        while x < self.canvas.size[0]:
            dc.DrawLine(x, offset[1], x, self.canvas.size[0])
            x += 60



class MapWnd(wx.Panel):
    def __init__(self, parent, openrpg):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        self.Freeze()
        sizer = wx.GridBagSizer(hgap=1, vgap=1)
        sizer.SetEmptyCellSize((0,0))

        self.canvas = MapCanvas(self, openrpg)
        sizer.Add(self.canvas, (0,0), flag=wx.EXPAND)

        #self.gmToolBar = BP.ButtonPanel(self, wx.ID_ANY)
        #sizer.Add(self.gmToolBar, (1,0), flag=wx.EXPAND)
        #self.playerToolBar = BP.ButtonPanel(self, wx.ID_ANY)
        #sizer.Add(self.playerToolBar, (2,0), flag=wx.EXPAND)

        sizer.AddGrowableCol(0)
        sizer.AddGrowableRow(0)

        self.SetSizer(sizer)
        self.SetAutoLayout(True)

        #self._CreateToolBar()

        self.Bind(wx.EVT_MOUSEWHEEL, self.canvas.OnZoom)
        self.Layout()
        self.Thaw()

        wx.CallAfter(self.PostLoad)

    #Public API
    def PostLoad(self):
        self.canvas.Clear()
        self.canvas.circles.append(MapCircle('circle-1', wx.Point(100,100), 50, "#ff0000"))
        self.canvas.circles.append(MapCircle('circle-1', wx.Point(200,100), 50, "#00ff00"))
        self.canvas.circles.append(MapCircle('circle-1', wx.Point(300,100), 50, "#0000ff"))
        self.canvas.circles.append(MapCircle('circle-1', wx.Point(400,100), 50, "#000000"))
        #self.canvas.roleTimer.Start(100)
        self.canvas.UpdateMap()

    #Events

class RGBHex:
    "Tools for Converting from hex to rgb and versa vicea"

    def rgb_tuple(self,hexnum):
        red = self.c2rgb(hexnum[1:3])
        green = self.c2rgb(hexnum[3:5])
        blue = self.c2rgb(hexnum[5:7])
        #print "Converted %s to %s, %s, %s" % (hexnum, red, green, blue)
        return (red, green, blue)

    def hexstring(self, red, green, blue):
        hexcolor = "#" + self.c2hex(red)
        hexcolor = hexcolor + self.c2hex(green)
        hexcolor = hexcolor + self.c2hex(blue)
        return hexcolor

    def c2rgb(self,num):
        "Converts from hex to rgb"
        first = num[0]
        second = num[1]
        s = 0
        if first == 'a': s = 10 * 16
        elif first == 'b': s = 11 * 16
        elif first == 'c': s = 12 * 16
        elif first == 'd': s = 13 * 16
        elif first == 'e': s = 14 * 16
        elif first == 'f': s = 15 * 16
        else: s = s+ int(first) * 16
        if second == 'a': s = s + 10
        elif second == 'b': s = s + 11
        elif second == 'c': s = s + 12
        elif second == 'd': s = s + 13
        elif second == 'e': s = s + 14
        elif second == 'f': s = s + 15
        else: s = s + int(second)
        return s

    def c2hex(self,num):
        "Converts from RGB to Hex"
        first = num/16
        second = num%16
        s = ""
        if first == 10: s = s+"a"
        elif first == 11: s = s+"b"
        elif first == 12: s = s+"c"
        elif first == 13: s = s+"d"
        elif first == 14: s = s+"e"
        elif first == 15: s = s+"f"
        else: s = s+ str(first)
        if second == 10: s = s+"a"
        elif second == 11: s = s+"b"
        elif second == 12: s = s+"c"
        elif second == 13: s = s+"d"
        elif second == 14: s = s+"e"
        elif second == 15: s = s+"f"
        else: s = s+ str(second)
        return s

    def do_hex_color_dlg(self,parent):
        data = wx.ColourData()
        data.SetChooseFull(1)
        dlg = wx.ColourDialog(parent, data)
        if dlg.ShowModal() == wx.ID_OK:
            data = dlg.GetColourData()
            (red,green,blue) = data.GetColour().Get()
            hexcolor = self.hexstring(red, green, blue)
            dlg.Destroy()
            return hexcolor
        else:
            dlg.Destroy()
            return None


### Test Stuff
class BlankFrame(wx.Frame):
    def __init__(self, openrpg):
        wx.Frame.__init__(self, None, title="New Map Test Window", size=(640,480))

        self.map = MapWnd(self, openrpg)
        self.basesizer = wx.BoxSizer(wx.VERTICAL)
        self.basesizer.Add(self.map, 1, wx.EXPAND)

        self.SetSizer(self.basesizer)
        self.SetAutoLayout(True)


class BlankApp(wx.App):
    def OnInit(self):
        self.frame = BlankFrame(None)
        self.frame.Show()
        self.SetTopWindow(self.frame)


        return True

if __name__ == "__main__":
    app = BlankApp(0)
    app.MainLoop()