local MDT = DungeonTools
local L = MDT.L
local sizex,sizey = 350,33
local AceGUI = LibStub("AceGUI-3.0")
local db
local toolbarTools = {}
local drawingActive = false
local currentTool
local objectDrawLayer = "ARTWORK"

local twipe,tinsert,tremove,tgetn,CreateFrame,tonumber,pi,max,min,atan2,abs,pairs,ipairs,GetCursorPosition,GameTooltip = table.wipe,table.insert,table.remove,table.getn,CreateFrame,tonumber,math.pi,math.max,math.min,math.atan2,math.abs,pairs,ipairs,GetCursorPosition,GameTooltip

---sets up the toolbar frame and the widgets in it
function MDT:initToolbar(frame)
    db = MDT:GetDB()

    frame.toolbar = CreateFrame("Frame","MDTToolbarFrame",frame)
    frame.toolbar:SetFrameStrata("HIGH")
    frame.toolbar:SetFrameLevel(5)
    frame.toolbar.tex = frame.toolbar:CreateTexture(nil,"HIGH",nil,6)
    frame.toolbar.tex:SetAllPoints()
    frame.toolbar.tex:SetColorTexture(unpack(MDT.BackdropColor))
    frame.toolbar.toggleButton = CreateFrame("Button", nil, frame);
    frame.toolbar.toggleButton:SetFrameStrata("HIGH")
    frame.toolbar.toggleButton:SetFrameLevel(6)

    frame.toolbar.toggleButton:SetPoint("TOP",frame,"TOP")
    frame.toolbar.toggleButton:SetSize(32,11)
    frame.toolbar.toggleButton:SetNormalTexture("Interface\\AddOns\\DungeonTools\\Textures\\arrows")
    frame.toolbar.toggleButton:GetNormalTexture():SetTexCoord(0,1,0.65,1)

    frame.toolbar:Hide()
    frame.toolbar:SetScript("OnHide",function()
        MDT:UpdateSelectedToolbarTool(nil)
    end)

    frame.toolbar.toggleButton:SetScript("OnClick", function()
        if frame.toolbar:IsShown() then
            frame.toolbar:Hide()
            frame.toolbar.toggleButton:ClearAllPoints()
            frame.toolbar.toggleButton:SetPoint("TOP",frame,"TOP")
            frame.toolbar.toggleButton:GetNormalTexture():SetTexCoord(0,1,0.65,1)
            db.toolbarExpanded = false
        else
            frame.toolbar:Show()
            frame.toolbar.toggleButton:ClearAllPoints()
            frame.toolbar.toggleButton:SetPoint("TOP",frame.toolbar,"BOTTOM")
            frame.toolbar.toggleButton:GetNormalTexture():SetTexCoord(0,1,0,0.35)
            db.toolbarExpanded = true
        end
    end)

    frame.toolbar.widgetGroup = AceGUI:Create("SimpleGroup")
    frame.toolbar.widgetGroup.frame:ClearAllPoints()
    frame.toolbar.widgetGroup.frame:SetAllPoints(frame.toolbar)
    if not frame.toolbar.widgetGroup.frame.SetBackdrop then
        Mixin(frame.toolbar.widgetGroup.frame, BackdropTemplateMixin)
    end
    frame.toolbar.widgetGroup.frame:SetBackdropColor(0,0,0,0)
    --frame.toolbar.widgetGroup:SetWidth(350)
    --frame.toolbar.widgetGroup:SetHeight(15)
    --frame.toolbar.widgetGroup:SetPoint("TOP",frame.toolbar,"TOP",0,0)



    frame.toolbar.widgetGroup:SetLayout("Flow")
    frame.toolbar.widgetGroup.frame:SetFrameStrata("High")
    frame.toolbar.widgetGroup.frame:SetFrameLevel(7)

    MDT:FixAceGUIShowHide(frame.toolbar.widgetGroup,frame.toolbar)

    do
        --dirty hook to make widgetgroup show/hide
        local originalShow,originalHide = frame.Show,frame.Hide
        function frame:Show(...)
            if frame.toolbar:IsShown() then
                frame.toolbar.widgetGroup.frame:Show()
            end
            return originalShow(self,...)
        end
        function frame:Hide(...)
            frame.toolbar.widgetGroup.frame:Hide()
            return originalHide(self, ...)
        end
    end

    ---TOOLBAR WIDGETS
    local widgetWidth = 24
    local widgets = {}
    MDT.tempWidgets = widgets

    ---back
    local back = AceGUI:Create("Icon")
    back:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.5,0.75,0.5,0.75)
    back:SetCallback("OnClick",function (widget,callbackName)
        self:PresetObjectStepBack()
        if self.liveSessionActive then self:LiveSession_SendCommand("undo") end
    end)
    back.tooltipText = L["Undo"]
    local t = back.frame:CreateTexture(nil,"ARTWORK")
    back.frame:SetHighlightTexture(t)
    tinsert(widgets,back)

    ---forward
    local forward = AceGUI:Create("Icon")
    forward:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.75,1,0.5,0.75)
    forward:SetCallback("OnClick",function (widget,callbackName)
        self:PresetObjectStepForward()
        if self.liveSessionActive then self:LiveSession_SendCommand("redo") end
    end)
    forward.tooltipText = L["Redo"]
    tinsert(widgets,forward)

    ---colorPicker
    local colorPicker = AceGUI:Create("ColorPicker")
    --colorPicker:SetHasAlpha(true)
    colorPicker:SetColor(db.toolbar.color.r, db.toolbar.color.g, db.toolbar.color.b, db.toolbar.color.a)
    colorPicker:SetCallback("OnValueConfirmed",function(widget,callbackName,r, g, b, a)
        db.toolbar.color.r, db.toolbar.color.g, db.toolbar.color.b, db.toolbar.color.a  = r, g, b, a
        colorPicker:SetColor(r, g, b, a)
    end)
    colorPicker.tooltipText = L["Colorpicker"]
    tinsert(widgets,colorPicker)

    local sizeIndicator
    ---minus
    local minus = AceGUI:Create("Icon")
    minus:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0,0.25,0.5,0.75)
    minus:SetCallback("OnClick",function (widget,callbackName)
        db.toolbar.brushSize = db.toolbar.brushSize - 1
        if db.toolbar.brushSize < 1 then db.toolbar.brushSize = 1 end
        sizeIndicator:SetText(db.toolbar.brushSize)
    end)
    minus.tooltipText = L["Decrease Brush Size"]
    tinsert(widgets,minus)

    ---sizeIndicator
    sizeIndicator = AceGUI:Create("EditBox")
    sizeIndicator:DisableButton(true)
    sizeIndicator:SetMaxLetters(2)
    sizeIndicator.editbox:SetNumeric(true)
    sizeIndicator:SetText(db.toolbar.brushSize)
    local function updateBrushSize(size)
        db.toolbar.brushSize = size
    end
    sizeIndicator:SetCallback("OnEnterPressed",function (widget,callback,text)
        sizeIndicator:ClearFocus()
        local size = tonumber(text)
        updateBrushSize(size)
    end)
    sizeIndicator:SetCallback("OnTextChanged",function (widget,callback,text)
        local size = tonumber(text)
        updateBrushSize(size)
    end)
    --Enable mousewheel scrolling
    sizeIndicator.editbox:EnableMouseWheel(true)
    sizeIndicator.editbox:SetScript("OnMouseWheel", function(self, delta)
        local newSize = db.toolbar.brushSize+delta
        if newSize<1 then newSize = 1 end
        if newSize>99 then newSize = 99 end
        db.toolbar.brushSize = newSize
        sizeIndicator:SetText(db.toolbar.brushSize)
    end)
    sizeIndicator.tooltipText = L["Brush Size"]
    tinsert(widgets,sizeIndicator)

    ---plus
    local plus = AceGUI:Create("Icon")
    plus:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.25,0.5,0.5,0.75)
    plus:SetCallback("OnClick",function (widget,callbackName)
        db.toolbar.brushSize = db.toolbar.brushSize + 1
        sizeIndicator:SetText(db.toolbar.brushSize)
    end)
    plus.tooltipText = L["Increase Brush Size"]
    tinsert(widgets,plus)

    ---pencil
    local pencil = AceGUI:Create("Icon")
    pencil:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0,0.25,0,0.25)
    toolbarTools["pencil"] = pencil
    pencil:SetCallback("OnClick",function (widget,callbackName)
        if currentTool == "pencil" then MDT:UpdateSelectedToolbarTool() else MDT:UpdateSelectedToolbarTool("pencil") end
    end)
    pencil.tooltipText = L["Drawing: Freehand"]
    tinsert(widgets,pencil)

    ---line
    local line = AceGUI:Create("Icon")
    line:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0,0.25,0.75,1)
    toolbarTools["line"] = line
    line:SetCallback("OnClick",function (widget,callbackName)
        if currentTool == "line" then MDT:UpdateSelectedToolbarTool() else MDT:UpdateSelectedToolbarTool("line") end
    end)
    line.tooltipText = L["Drawing: Line"]
    tinsert(widgets,line)

    ---arrow
    local arrow = AceGUI:Create("Icon")
    arrow:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.25,0.5,0,0.25)
    toolbarTools["arrow"] = arrow
    arrow:SetCallback("OnClick",function (widget,callbackName)
        if currentTool == "arrow" then MDT:UpdateSelectedToolbarTool() else MDT:UpdateSelectedToolbarTool("arrow") end
    end)
    arrow.tooltipText = L["Drawing: Arrow"]
    tinsert(widgets,arrow)

    ---note
    local note = AceGUI:Create("Icon")
    note:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.75,1,0,0.25)
    toolbarTools["note"] = note
    note:SetCallback("OnClick",function (widget,callbackName)
        if currentTool == "note" then MDT:UpdateSelectedToolbarTool() else MDT:UpdateSelectedToolbarTool("note") end
    end)
    note.tooltipText = L["Insert Note"]
    tinsert(widgets,note)

    ---mover
    local mover = AceGUI:Create("Icon")
    mover:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.5,0.75,0,0.25)
    toolbarTools["mover"] = mover
    mover:SetCallback("OnClick",function (widget,callbackName)
        if currentTool == "mover" then MDT:UpdateSelectedToolbarTool() else MDT:UpdateSelectedToolbarTool("mover") end
    end)
    mover.tooltipText = L["Move Object"]
    tinsert(widgets,mover)

    ---cogwheel
    local cogwheel = AceGUI:Create("Icon")
    cogwheel:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0,0.25,0.25,0.5)
    cogwheel:SetCallback("OnClick",function (widget,callbackName)
        InterfaceOptionsFrame_OpenToCategory("DungeonTools")
        InterfaceOptionsFrame_OpenToCategory("DungeonTools")
        MDT:HideInterface()
    end)
    cogwheel.tooltipText = L["Settings"]
    --tinsert(widgets,cogwheel)

    ---eraser
    local eraser = AceGUI:Create("Icon")
    eraser:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.25,0.5,0.25,0.5)
    toolbarTools["eraser"] = eraser
    eraser:SetCallback("OnClick",function (widget,callbackName)
        if currentTool == "eraser" then MDT:UpdateSelectedToolbarTool() else MDT:UpdateSelectedToolbarTool("eraser") end
    end)
    eraser.tooltipText = L["Drawing: Eraser"]
    tinsert(widgets,eraser)

    ---delete
    local delete = AceGUI:Create("Icon")
    delete:SetImage("Interface\\AddOns\\DungeonTools\\Textures\\icons",0.25,0.5,0.75,1)
    delete:SetCallback("OnClick",function (widget,callbackName)
        local prompt = string.format(L["deleteAllDrawingsPrompt"],"\n","\n","\n")
        self:OpenConfirmationFrame(450,150,L["Delete ALL drawings"],L["Delete"],prompt, function()
            self:DeletePresetObjects()
            if self.liveSessionActive then self:LiveSession_SendCommand("deletePresetObjects") end
        end)
    end)
    delete.tooltipText = L["Delete ALL drawings"]
    tinsert(widgets,delete)

    for k,widget in ipairs(widgets) do
        widget:SetWidth(widgetWidth)
        if widget.type == "EditBox" then widget:SetWidth(30) end
        if widget.SetImageSize then widget:SetImageSize(20,20) end
        widget:SetCallback("OnEnter",function(widget,callbackName)
            MDT:ToggleToolbarTooltip(true,widget)
        end)
        widget:SetCallback("OnLeave",function()
            MDT:ToggleToolbarTooltip(false)
        end)
        frame.toolbar.widgetGroup:AddChild(widget)
    end

    frame.toolbar:SetSize(sizex, sizey)
    frame.toolbar:ClearAllPoints()
    frame.toolbar:SetPoint("TOP", frame, "TOP", 0, 0)

    MDT:CreateBrushPreview(frame)
    MDT:UpdateSelectedToolbarTool()

end

---TexturePool
local activeTextures = {}
local texturePool = {}
local notePoolCollection
local function getTexture()
    local size = tgetn(texturePool)
    if size == 0 then
        return MDT.main_frame.mapPanelFrame:CreateTexture(nil, "OVERLAY")
    else
        local tex = texturePool[size]
        tremove(texturePool, size)
        tex:SetRotation(0)
        tex:SetTexCoord(0, 1, 0, 1)
        tex:ClearAllPoints()
        tex.coords = nil
        tex.points = nil
        return tex
    end
end
local function releaseTexture(tex)
    tex:Hide()
    tinsert(texturePool,tex)
end

---ReleaseAllActiveTextures
function MDT:ReleaseAllActiveTextures()
    for k,tex in pairs(activeTextures) do
        releaseTexture(tex)
    end
    twipe(activeTextures)
    if notePoolCollection then notePoolCollection:ReleaseAll() end
end

---CreateBrushPreview
function MDT:CreateBrushPreview(frame)
    frame.brushPreview = CreateFrame("Frame","DungeonToolsBrushPreview",UIParent)
    frame.brushPreview:SetFrameStrata("HIGH")
    frame.brushPreview:SetFrameLevel(4)
    frame.brushPreview:SetSize(1, 1)
    frame.brushPreview.tex = frame.brushPreview:CreateTexture(nil, "OVERLAY")
    frame.brushPreview.tex:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\ring")
    frame.brushPreview.tex:SetAllPoints()
end

---EnableBrushPreview
function MDT:EnableBrushPreview(tool)
    local frame = MDT.main_frame
    if tool == "mover" then return end
    frame.brushPreview:Show()
    frame.brushPreview:SetScript("OnUpdate", function(self, tick)
        if MouseIsOver(MDTScrollFrame) and not MouseIsOver(MDTToolbarFrame) then
            local x,y = GetCursorPosition()
            x = x/UIParent:GetScale()
            y = y/UIParent:GetScale()
            self:ClearAllPoints()
            self:SetPoint("CENTER",UIParent,"BOTTOMLEFT",x,y)
            if tool == "eraser" then
                frame.brushPreview.tex:SetVertexColor(1,1,1,1)
            else
                frame.brushPreview.tex:SetVertexColor(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b,db.toolbar.color.a)
            end
            frame.brushPreview:SetSize(30,30)
            frame.brushPreview.tex:ClearAllPoints()
            frame.brushPreview.tex:SetAllPoints()
            frame.brushPreview.tex:Show()
        else
            frame.brushPreview.tex:Hide()
        end
    end)
end
---DisableBrushPreview
function MDT:DisableBrushPreview()
    local frame = MDT.main_frame
    frame.brushPreview:Hide()
    frame.brushPreview.tex:Hide()
    frame.brushPreview:SetScript("OnUpdate", nil)
end

---ToggleToolbarTooltip
function MDT:ToggleToolbarTooltip(show, widget)
    if not show then
        GameTooltip:Hide()
    else
        local yOffset = -1
        if widget.type == "EditBox" then yOffset = yOffset-1 end
        if widget.type == "ColorPicker" then yOffset = yOffset-3 end
        GameTooltip:SetOwner(widget.frame, "ANCHOR_BOTTOM",0,yOffset)
        GameTooltip:SetText(widget.tooltipText,1,1,1,1)
        GameTooltip:Show()
    end
end

---UpdateSelectedToolbarTool
---Called when a tool is selected/deselected
function MDT:UpdateSelectedToolbarTool(widgetName)
    local toolbar = MDT.main_frame.toolbar
    if not widgetName or (not toolbarTools[widgetName]) then
        if toolbar.highlight then toolbar.highlight:Hide() end
        MDT:RestoreScrollframeScripts()
        MDT:DisableBrushPreview()
        if drawingActive then
            if currentTool == "pencil" then MDT:StopPencilDrawing() end
            if currentTool == "arrow" then MDT:StopArrowDrawing() end
            if currentTool == "line" then MDT:StopLineDrawing() end
            if currentTool == "mover" then MDT:StopMovingDrawing() end
            if currentTool == "eraser" then MDT:StopEraserDrawing() end
        end
        currentTool = nil
        toolbar:SetScript("OnUpdate",nil)
        return
    end
    local widget = toolbarTools[widgetName]
    currentTool = widgetName
    toolbar.highlight = toolbar.highlight or toolbar:CreateTexture(nil,"HIGH",nil,7)
    toolbar.highlight:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\icons")
    toolbar.highlight:SetTexCoord(0.5,0.75,0.25,0.5)
    toolbar.highlight:SetSize(widget.frame:GetWidth(),widget.frame:GetWidth())
    toolbar.highlight:ClearAllPoints()
    toolbar.highlight:SetPoint("CENTER",widget.frame,"CENTER")
    MDT:OverrideScrollframeScripts()
    MDT:EnableBrushPreview(currentTool)
    toolbar.highlight:Show()
end

---OverrideScrollframeScripts
---Take control of the map scrollframe mouse event scripts
---Called when the user starts drawing on the map
function MDT:OverrideScrollframeScripts()
    local frame = MDT.main_frame
    frame.scrollFrame:SetScript("OnMouseDown", function(self,button)
        if button == "LeftButton" then
            if currentTool == "pencil" then MDT:StartPencilDrawing() end
            if currentTool == "arrow" then MDT:StartArrowDrawing() end
            if currentTool == "line" then MDT:StartLineDrawing() end
            if currentTool == "mover" then MDT:StartMovingObject() end
            if currentTool == "eraser" then MDT:StartEraserDrawing() end
        end
        if button == "RightButton" then
            local scrollFrame = MDT.main_frame.scrollFrame
            if scrollFrame.zoomedIn then
                scrollFrame.panning = true;
                scrollFrame.cursorX,scrollFrame.cursorY = GetCursorPosition()
            end
            scrollFrame.oldX = scrollFrame.cursorX
            scrollFrame.oldY = scrollFrame.cursorY
        end
    end)
    frame.scrollFrame:SetScript("OnMouseUp", function(self,button)
        if button == "LeftButton" then
            if currentTool == "pencil" then MDT:StopPencilDrawing() end
            if currentTool == "arrow" then MDT:StopArrowDrawing() end
            if currentTool == "line" then MDT:StopLineDrawing() end
            if currentTool == "mover" then MDT:StopMovingObject() end
            if currentTool == "eraser" then MDT:StopEraserDrawing() end
            if currentTool == "note" then MDT:StartNoteDrawing() end
        end
        if button == "RightButton" then
            local scrollFrame = MDT.main_frame.scrollFrame
            if scrollFrame.panning then scrollFrame.panning = false end
            --only ping if we didnt pan
            if scrollFrame.oldX==scrollFrame.cursorX or scrollFrame.oldY==scrollFrame.cursorY then
                local x,y = MDT:GetCursorPosition()
                MDT:PingMap(x,y)
                local sublevel = MDT:GetCurrentSubLevel()
                if MDT.liveSessionActive then MDT:LiveSession_SendPing(x,y,sublevel) end
            end
        end
    end)
    --make notes draggable
    if notePoolCollection then
        if currentTool == "mover" then
            for note,_ in pairs(notePoolCollection.pools.QuestPinTemplate.activeObjects) do
                note:SetMovable(true)
                note:RegisterForDrag("LeftButton")
                local xOffset,yOffset

                note:SetScript("OnMouseDown",function()
                    local currentPreset = MDT:GetCurrentPreset()
                    local x,y = MDT:GetCursorPosition()
                    local scale = MDT:GetScale()
                    x = x*(1/scale)
                    y = y*(1/scale)
                    local nx = currentPreset.objects[note.objectIndex].d[1]
                    local ny = currentPreset.objects[note.objectIndex].d[2]
                    xOffset = x-nx
                    yOffset = y-ny
                end)
                note:SetScript("OnDragStart", function()
                    note:StartMoving()
                end)
                note:SetScript("OnDragStop", function()
                    note:StopMovingOrSizing()
                    local x,y = MDT:GetCursorPosition()
                    local scale = MDT:GetScale()
                    x = x*(1/scale)
                    y = y*(1/scale)
                    local currentPreset = MDT:GetCurrentPreset()
                    currentPreset.objects[note.objectIndex].d[1]=x-xOffset
                    currentPreset.objects[note.objectIndex].d[2]=y-yOffset
                    if MDT.liveSessionActive then MDT:LiveSession_SendNoteCommand("move",note.objectIndex,x-xOffset,y-yOffset) end
                    MDT:DrawAllPresetObjects()
                end)
            end
        else
            for note,_ in pairs(notePoolCollection.pools.QuestPinTemplate.activeObjects) do
                note:SetMovable(false)
                note:RegisterForDrag()
            end
        end
    end
end

---RestoreScrollframeScripts
---Restore original functionality to the map scrollframe: Clicking on enemies, rightclick context menu
---Called when the user is done drawing on the map
function MDT:RestoreScrollframeScripts()
    local frame = MDT.main_frame
    frame.scrollFrame:SetScript("OnMouseDown", MDT.OnMouseDown)
    frame.scrollFrame:SetScript("OnMouseUp", MDT.OnMouseUp)
    --make notes not draggable
    if notePoolCollection then
        for note,_ in pairs(notePoolCollection.pools.QuestPinTemplate.activeObjects) do
            note:SetMovable(false)
            note:RegisterForDrag()
        end
    end
end

---returns cursor position relative to the map frame
function MDT:GetCursorPosition()
    local frame = MDT.main_frame
    local scrollFrame = frame.scrollFrame
    local relativeFrame = UIParent      --UIParent
    local mapPanelFrame = MDT.main_frame.mapPanelFrame
    local cursorX, cursorY = GetCursorPosition()
    local mapScale = mapPanelFrame:GetScale()
    local scrollH = scrollFrame:GetHorizontalScroll()
    local scrollV = scrollFrame:GetVerticalScroll()
    local frameX = (cursorX / relativeFrame:GetScale()) - scrollFrame:GetLeft()
    local frameY = scrollFrame:GetTop() - (cursorY / relativeFrame:GetScale())
    frameX=(frameX/mapScale)+scrollH
    frameY=(frameY/mapScale)+scrollV
    return frameX-1,-frameY
end

---GetHighestFrameLevelAtCursor
function MDT:GetHighestFrameLevelAtCursor()
    local currentSublevel = -8
    for k,v in pairs(activeTextures) do
        if MouseIsOver(v) and v:IsShown() and (not v.isOwn) then
            local _, sublevel = v:GetDrawLayer()
            currentSublevel = max(currentSublevel,sublevel+1)
        end
    end
    if currentSublevel > 7 then currentSublevel = 7 end
    return currentSublevel
end

local nobj
---StartArrowDrawing
function MDT:StartArrowDrawing()
    drawingActive = true
    local frame = MDT.main_frame
    local startx,starty = MDT:GetCursorPosition()
    local line = getTexture()
    line:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\Square_White")
    line:SetVertexColor(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b,db.toolbar.color.a)
    line:Show()
    local arrow = getTexture()
    arrow:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\triangle")
    arrow:SetVertexColor(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b,db.toolbar.color.a)
    line.isOwn = true
    arrow.isOwn = true
    tinsert(activeTextures,line)
    tinsert(activeTextures,arrow)
    local drawLayer = -8

    ---new object for storage
    ---d: size,lineFactor,sublevel,shown,colorstring,drawLayer,[smooth]
    ---l: x1,y1,x2,y2,...
    ---t: triangleroation
    nobj = { d={ db.toolbar.brushSize, 1, MDT:GetCurrentSubLevel(), true, MDT:RGBToHex(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b)}, l={}}
    nobj.l = { MDT:Round(startx,1), MDT:Round(starty,1)}
    nobj.t = {}
    local scale = MDT:GetScale()
    frame.toolbar:SetScript("OnUpdate", function(self, tick)
        if not MouseIsOver(MDTScrollFrame) then return end
        local x,y = MDT:GetCursorPosition()local currentDrawLayer = MDT:GetHighestFrameLevelAtCursor()
        drawLayer = max(drawLayer,currentDrawLayer)
        if x~= startx and y~=starty then
            DrawLine(line, MDT.main_frame.mapPanelTile1, startx, starty, x, y, (db.toolbar.brushSize*0.3)*scale, 1,"TOPLEFT")
            nobj.l[3] = MDT:Round(x,1)
            nobj.l[4] = MDT:Round(y,1)
        end
        --position arrow head
        arrow:Show()
        arrow:SetWidth(1*db.toolbar.brushSize*scale)
        arrow:SetHeight(1*db.toolbar.brushSize*scale)
        --calculate rotation
        local rotation = atan2(starty-y,startx-x)
        arrow:SetRotation(rotation+pi)
        arrow:ClearAllPoints()
        arrow:SetPoint("CENTER", MDT.main_frame.mapPanelTile1,"TOPLEFT",x,y)
        arrow:SetDrawLayer(objectDrawLayer, drawLayer)
        line:SetDrawLayer(objectDrawLayer, drawLayer)

        nobj.d[6] = drawLayer
        nobj.t[1] = rotation
    end)
end

---StopArrowDrawing
function MDT:StopArrowDrawing()
    local frame = MDT.main_frame
    MDT:StorePresetObject(nobj)
    if self.liveSessionActive then self:LiveSession_SendObject(nobj) end
    frame.toolbar:SetScript("OnUpdate",nil)
    for k,v in pairs(activeTextures) do
        v.isOwn = nil
    end
    drawingActive = false
end

local startx,starty,endx,endy
---StartLineDrawing
function MDT:StartLineDrawing()
    drawingActive = true
    local frame = MDT.main_frame
    startx,starty = MDT:GetCursorPosition()
    local line = getTexture()
    line:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\Square_White")
    line:SetVertexColor(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b,db.toolbar.color.a)
    line.isOwn = true
    tinsert(activeTextures,line)

    local circle1 = getTexture()
    tinsert(activeTextures,circle1)
    local circle2 = getTexture()
    tinsert(activeTextures,circle2)

    local drawLayer = -8
    ---new object
    ---d: size,lineFactor,sublevel,shown,colorstring,drawLayer,[smooth]
    ---l: x1,y1,x2,y2,...
    nobj = { d={ db.toolbar.brushSize, 1.1, MDT:GetCurrentSubLevel(), true, MDT:RGBToHex(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b), nil, true}, l={}}
    nobj.l = {}

    local scale = MDT:GetScale()
    frame.toolbar:SetScript("OnUpdate", function(self, tick)
        if not MouseIsOver(MDTScrollFrame) then return end
        local currentDrawLayer = MDT:GetHighestFrameLevelAtCursor()
        drawLayer = max(drawLayer,currentDrawLayer)
        endx,endy = MDT:GetCursorPosition()
        if endx~= startx and endy~=starty then
            DrawLine(line, MDT.main_frame.mapPanelTile1, startx, starty, endx, endy, (db.toolbar.brushSize*0.3)*1.1*scale, 1.00,"TOPLEFT")
            line:SetDrawLayer(objectDrawLayer,drawLayer)
            line:Show()
            MDT:DrawCircle(startx,starty,(db.toolbar.brushSize*0.3)*scale,db.toolbar.color,objectDrawLayer,drawLayer,true,nil,circle1,true)
            MDT:DrawCircle(endx,endy,(db.toolbar.brushSize*0.3)*scale,db.toolbar.color,objectDrawLayer,drawLayer,true,nil,circle2,true)
            nobj.d[6] = drawLayer
        end
    end)
end

---StopLineDrawing
function MDT:StopLineDrawing()
    local frame = MDT.main_frame
    frame.toolbar:SetScript("OnUpdate",nil)
    for k,v in pairs(activeTextures) do
        v.isOwn = nil
    end
    --split the line into multiple parts
    local d = math.sqrt((endx-startx)^2+(endy-starty)^2)
    local numSegments = d*2/(math.max(db.toolbar.brushSize,5))
    numSegments = math.max(numSegments,1)
    local x,y = startx,starty
    for i=1,numSegments do
        local t =  i/numSegments
        local newx = startx+(endx-startx)*t
        local newy = starty+(endy-starty)*t
        nobj.l[4*i-3] = MDT:Round(x,1)
        nobj.l[4*i-2] = MDT:Round(y,1)
        nobj.l[4*i-1] = MDT:Round(newx,1)
        nobj.l[4*i] = MDT:Round(newy,1)
        x,y = newx,newy
    end
    tinsert(nobj.l, MDT:Round(x,1))
    tinsert(nobj.l, MDT:Round(y,1))
    tinsert(nobj.l, MDT:Round(endx,1))
    tinsert(nobj.l, MDT:Round(endy,1))

    MDT:StorePresetObject(nobj)
    if self.liveSessionActive then self:LiveSession_SendObject(nobj) end
    drawingActive = false
    MDT:DrawAllPresetObjects()
end

local oldx,oldy
---StartPencilDrawing
---Starts the pencil drawing script, fired on mouse down with pencil tool selected
function MDT:StartPencilDrawing()
    drawingActive = true
    local frame = MDT.main_frame
    oldx = nil
    oldy = nil
    local layerSublevel = -8
    local thresholdDefault = 10

    ---new object
    ---d: size,lineFactor,sublevel,shown,colorstring,drawLayer,[smooth]
    ---l: x1,y1,x2,y2,...
    nobj = { d={ db.toolbar.brushSize, 1.1, MDT:GetCurrentSubLevel(), true, MDT:RGBToHex(db.toolbar.color.r,db.toolbar.color.g,db.toolbar.color.b), 0, true}, l={}}
    nobj.l = {}

    local lineIdx = 1
    local scale = MDT:GetScale()
    frame.toolbar:SetScript("OnUpdate", function(self, tick)
        if not MouseIsOver(MDTScrollFrame) then return end
        local currentDrawLayer = MDT:GetHighestFrameLevelAtCursor()
        layerSublevel = max(layerSublevel,currentDrawLayer)
        local x,y = MDT:GetCursorPosition()
        local mapScale = MDT.main_frame.mapPanelFrame:GetScale()
        local threshold = thresholdDefault * 1/mapScale
        if not oldx or not oldy then
            oldx,oldy = x,y
            return
        end
        if (oldx and abs(x-oldx)>threshold) or (oldy and abs(y-oldy)>threshold)  then
            MDT:DrawLine(oldx,oldy,x,y,(db.toolbar.brushSize*0.3)*scale,db.toolbar.color,true,objectDrawLayer,layerSublevel,nil,true)
            nobj.d[6] = layerSublevel
            nobj.l[lineIdx] = MDT:Round(oldx,1)
            nobj.l[lineIdx+1] = MDT:Round(oldy,1)
            nobj.l[lineIdx+2] = MDT:Round(x,1)
            nobj.l[lineIdx+3] = MDT:Round(y,1)
            lineIdx = lineIdx + 4
            oldx,oldy = x,y
        end
    end)
end

---StopPencilDrawing
---End the pencil drawing script, fired on mouse up with the pencil tool selected
function MDT:StopPencilDrawing()
    local frame = MDT.main_frame
    local x,y = MDT:GetCursorPosition()
    local layerSublevel = MDT:GetHighestFrameLevelAtCursor()
    local scale = MDT:GetScale()
    --finish line
    if x~=oldx or y~=oldy then
        MDT:DrawLine(oldx,oldy,x,y,(db.toolbar.brushSize*0.3)*scale,db.toolbar.color,true,objectDrawLayer,layerSublevel)
        --store it
        local size = 0
        for k,v in ipairs(nobj.l) do
            size = size+1
        end
        nobj.l[size+1] = MDT:Round(oldx,1)
        nobj.l[size+2] = MDT:Round(oldy,1)
        nobj.l[size+3] = MDT:Round(x,1)
        nobj.l[size+4] = MDT:Round(y,1)
    end
    frame.toolbar:SetScript("OnUpdate",nil)
    --clear own flags
    for k,v in pairs(activeTextures) do
        v.isOwn = nil
    end

    local lineCount = 0
    for _,_ in pairs(nobj.l) do
        lineCount = lineCount +1
    end
    if lineCount > 0 then
        --draw end circle, dont need to store it as we draw it when we restore the line from db
        MDT:DrawCircle(x,y,db.toolbar.brushSize*0.3*scale,db.toolbar.color,objectDrawLayer,layerSublevel)
        MDT:StorePresetObject(nobj)
        --nobj will be scaled after StorePresetObject so no need to rescale again
        if self.liveSessionActive then self:LiveSession_SendObject(nobj) end
    end

    drawingActive = false
end

---StartMovingObject
local objectIndex
local originalX,originalY
function MDT:StartMovingObject()
    --we have to redraw all objects first, as the objectIndex needs to be set on every texture
    MDT:DrawAllPresetObjects()
    drawingActive = true
    local frame = MDT.main_frame
    objectIndex = MDT:GetHighestPresetObjectIndexAtCursor()
    local startx,starty = MDT:GetCursorPosition()
    originalX,originalY = MDT:GetCursorPosition()
    frame.toolbar:SetScript("OnUpdate", function(self, tick)
        if not MouseIsOver(MDTScrollFrame) then return end
        local x,y = MDT:GetCursorPosition()
        if x~=startx or y ~=starty then
            for j,tex in pairs(activeTextures) do
                if tex.objectIndex == objectIndex then
                    for i=1,tex:GetNumPoints() do
                        local point,relativeTo,relativePoint,xOffset,yOffset = tex:GetPoint(i)
                        tex:SetPoint(point,relativeTo,relativePoint,xOffset+(x-startx),yOffset+(y-starty))
                    end
                end
            end
            startx,starty = MDT:GetCursorPosition()
        end
    end)
end

---HideAllPresetObjects
---Hide textures during rescaling
function MDT:HideAllPresetObjects()
    --drawings
    for _,tex in pairs(activeTextures) do
        tex:Hide()
    end
    --notes
    if  notePoolCollection then
        local notes = notePoolCollection.pools.QuestPinTemplate.activeObjects
        for note,_ in pairs(notes) do
            note:Hide()
        end
    end
end

---StopMovingDrawing
function MDT:StopMovingObject()
    local frame = MDT.main_frame
    frame.toolbar:SetScript("OnUpdate",nil)
    if objectIndex then
        local newX,newY = MDT:GetCursorPosition()
        MDT:UpdatePresetObjectOffsets(objectIndex,originalX-newX,originalY-newY)
        if self.liveSessionActive then self:LiveSession_SendObjectOffsets(objectIndex,originalX-newX,originalY-newY) end
    end
    objectIndex = nil
    drawingActive = false
end

---GetHighestPresetObjectIndexAtCursor
function MDT:GetHighestPresetObjectIndexAtCursor()
    local currentSublevel = -8
    local highestTexture
    for k,v in pairs(activeTextures) do
        if MouseIsOver(v) and v:IsShown() then
            local _, sublevel = v:GetDrawLayer()
            if sublevel>=currentSublevel then
                highestTexture = v
            end
            currentSublevel = max(currentSublevel,sublevel+1)
        end
    end
    if highestTexture then
        return highestTexture.objectIndex
    end
end

---StartEraserDrawing
local changedObjects = {}
function MDT:StartEraserDrawing()
    MDT:DrawAllPresetObjects()
    drawingActive = true
    local frame = MDT.main_frame
    local startx,starty
    local scale = MDT:GetScale()
    twipe(changedObjects)
    frame.toolbar:SetScript("OnUpdate", function(self, tick)
        if not MouseIsOver(MDTScrollFrame) then return end
        local x,y = MDT:GetCursorPosition()
        if x~=startx or y ~=starty then
            local highestObjectIdx = MDT:GetHighestPresetObjectIndexAtCursor()
            for j,tex in pairs(activeTextures) do
                if MouseIsOver(tex) and tex:IsShown() and tex.objectIndex == highestObjectIdx  then --tex.coords means this is a line
                    tex:Hide()
                    if tex.coords then
                        local x1,y1,x2,y2 = unpack(tex.coords)
                        --hide circle textures of lines
                        for k,v in pairs(activeTextures) do
                            if v.points then
                                if (v.points[1] == x1 and v.points[2] == y1) or (v.points[1] == x2 and v.points[2] == y2) then
                                    v:Hide()
                                end
                            end
                        end
                        --delete saved lines
                        local currentPreset = MDT:GetCurrentPreset()
                        for objectIndex,obj in pairs(currentPreset.objects) do
                            if objectIndex == highestObjectIdx then
                                for coordIdx,coord in pairs(obj.l) do
                                    if coord*scale == x1 and obj.l[coordIdx+1]*scale == y1 and obj.l[coordIdx+2]*scale == x2 and obj.l[coordIdx+3]*scale == y2 then
                                        for i=1,4 do tremove(obj.l,coordIdx) end
                                        changedObjects[objectIndex] = obj
                                        break
                                    end
                                end
                            end
                        end
                    end
                    break
                end
            end
            startx,starty = x,y
        end
    end)
end

---StopEraserDrawing
function MDT:StopEraserDrawing()
    local frame = MDT.main_frame
    frame.toolbar:SetScript("OnUpdate",nil)
    if self.liveSessionActive then self:LiveSession_SendUpdatedObjects(changedObjects) end
    MDT:DrawAllPresetObjects()
    drawingActive = false
end
---StartNoteDrawing
function MDT:StartNoteDrawing()
    --check if we have less than 25 notes
    if notePoolCollection and notePoolCollection.pools.QuestPinTemplate.numActiveObjects>24 then
        MDT:UpdateSelectedToolbarTool()
        return
    end
    ---new object for storage
    ---x,y,sublevel,shown,text,n=true
    local x,y = MDT:GetCursorPosition()
    nobj = {d={ x, y, MDT:GetCurrentSubLevel(), true, ""}}
    nobj.n = true
    MDT:StorePresetObject(nobj)
    if self.liveSessionActive then self:LiveSession_SendObject(nobj) end
    MDT:DrawAllPresetObjects()

    if not IsShiftKeyDown() then
        MDT:UpdateSelectedToolbarTool()
    end
end

---DrawCircle
function MDT:DrawCircle(x, y, size, color, layer, layerSublevel, isOwn, objectIndex, tex, noinsert, extrax, extray)
    local circle = tex or getTexture()
    if not layer then layer = objectDrawLayer end
    circle:SetDrawLayer(layer, layerSublevel)
    circle:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\Circle_White")
    circle:SetVertexColor(color.r,color.g,color.b,color.a)
    circle:SetWidth(1.1*size)
    circle:SetHeight(1.1*size)
    circle:ClearAllPoints()
    circle:SetPoint("CENTER", MDT.main_frame.mapPanelTile1,"TOPLEFT",x,y)
    circle:Show()
    circle.isOwn = isOwn
    circle.objectIndex = objectIndex
    circle.points = {x,y,extrax,extray}
    if not noinsert then
        tinsert(activeTextures,circle)
    end
end

---DrawLine
function MDT:DrawLine(x, y, a, b, size, color, smooth, layer, layerSublevel, lineFactor, isOwn, objectIndex)
    local line = getTexture()
    if not layer then layer = objectDrawLayer end
    line:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\Square_White")
    line:SetVertexColor(color.r,color.g,color.b,color.a)
    DrawLine(line, MDT.main_frame.mapPanelTile1, x, y, a, b, size, lineFactor and lineFactor or 1.1,"TOPLEFT")
    line:SetDrawLayer(layer, layerSublevel)
    line:Show()
    line.isOwn = isOwn
    line.objectIndex = objectIndex
    line.coords = {x,y,a,b}
    tinsert(activeTextures,line)
    if smooth == true  then
        MDT:DrawCircle(x,y,size,color,layer,layerSublevel,isOwn,objectIndex)
    end
end

---DrawTriangle
function MDT:DrawTriangle(x, y, rotation, size, color, layer, layerSublevel, isOwn, objectIndex)
    local triangle = getTexture()
    if not layer then layer = objectDrawLayer end
    triangle:SetTexture("Interface\\AddOns\\DungeonTools\\Textures\\triangle")
    triangle:SetVertexColor(color.r,color.g,color.b,color.a)
    triangle:Show()
    triangle:SetWidth(size)
    triangle:SetHeight(size)
    triangle:SetRotation(rotation+pi)
    triangle:ClearAllPoints()
    triangle:SetPoint("CENTER", MDT.main_frame.mapPanelTile1,"TOPLEFT",x,y)
    triangle:SetDrawLayer(layer, layerSublevel)
    triangle.isOwn = isOwn
    triangle.objectIndex = objectIndex
    tinsert(activeTextures,triangle)
end

local noteEditbox

--store text in nobj
local function updateNoteObjText(text,note)
    local currentPreset = MDT:GetCurrentPreset()
    currentPreset.objects[note.objectIndex].d[5]=text
    if MDT.liveSessionActive then MDT:LiveSession_SendNoteCommand("text",note.objectIndex,text) end
end
local function deleteNoteObj(note)
    local currentPreset = MDT:GetCurrentPreset()
    tremove(currentPreset.objects,note.objectIndex)
    if MDT.liveSessionActive then MDT:LiveSession_SendNoteCommand("delete",note.objectIndex,"0") end
    MDT:DrawAllPresetObjects()
end

local function makeNoteEditbox()
    local editbox = AceGUI:Create("SimpleGroup")
    editbox:SetWidth(240)
    editbox:SetHeight(120)
    editbox.frame:SetFrameStrata("HIGH")
    editbox.frame:SetFrameLevel(50)
    if not editbox.frame.SetBackdrop then
        Mixin(editbox.frame, BackdropTemplateMixin)
    end
    editbox.frame:SetBackdropColor(1,1,1,0)
    editbox:SetLayout("Flow")
    editbox.multiBox = AceGUI:Create("MultiLineEditBox")
    editbox.multiBox:SetLabel(L["Note Text:"])

    editbox.multiBox:SetCallback("OnEnterPressed",function(widget,callbackName,text)
        for note,_ in pairs(notePoolCollection.pools.QuestPinTemplate.activeObjects) do
            if note.noteIdx == editbox.noteIdx then
                note.tooltipText = text
                updateNoteObjText(text,note)
                break
            end
        end
        editbox.frame:Hide()
    end)

    editbox.multiBox:SetWidth(240)
    editbox.multiBox:SetHeight(120)
    editbox.multiBox.label:Hide()
    --[[ hiding the scrollbar messes up the whole editbox
    editbox.multiBox.scrollBar:Hide()
    editbox.multiBox.scrollBar:ClearAllPoints()
    editbox.multiBox.scrollBar:SetPoint("BOTTOM", editbox.multiBox.button, "TOP", 0, 16)
    editbox.multiBox.scrollBar.ScrollUpButton:SetPoint("BOTTOM", editbox.multiBox.scrollBar, "TOP",0,3)
    ]]
    editbox.frame:Hide()
    editbox:AddChild(editbox.multiBox)
    MDT:FixAceGUIShowHide(editbox,nil,nil,true)
    editbox.frame:SetScript("OnShow",function()
        hooksecurefunc(MDT, "MouseDownHook", function() editbox.frame:Hide() end)
        hooksecurefunc(MDT, "ZoomMap", function() editbox.frame:Hide() end)
    end)

    return editbox
end

local noteDropDown = L_Create_UIDropDownMenu("noteDropDown", nil)
local currentNote
local noteMenu = {}
do
    tinsert(noteMenu, {
        text = L["Edit"],
        notCheckable = 1,
        func = function()
            currentNote:OpenEditBox()
        end
    })
    tinsert(noteMenu, {
        text = " ",
        notClickable = 1,
        notCheckable = 1,
        func = nil
    })
    tinsert(noteMenu, {
        text = L["Delete"],
        notCheckable = 1,
        func = function()
            deleteNoteObj(currentNote)
        end
    })
    tinsert(noteMenu, {
        text = " ",
        notClickable = 1,
        notCheckable = 1,
        func = nil
    })
    tinsert(noteMenu, {
        text = L["Close"],
        notCheckable = 1,
        func = function()
            noteDropDown:Hide()
        end
    })
end

---DrawNote
function MDT:DrawNote(x, y, text, objectIndex)
    if not notePoolCollection then
        notePoolCollection = CreateFramePoolCollection()
        notePoolCollection:CreatePool("Button", MDT.main_frame.mapPanelFrame, "QuestPinTemplate")
    end
    local scale = MDT:GetScale()
    --setup
    local note = notePoolCollection:Acquire("QuestPinTemplate")
    note.noteIdx = notePoolCollection.pools.QuestPinTemplate.numActiveObjects
    note.objectIndex = objectIndex
    note:ClearAllPoints()
    note:SetPoint("CENTER", MDT.main_frame.mapPanelTile1,"TOPLEFT",x,y)
    note:SetSize(12*scale,12*scale)
    note.NormalTexture:SetSize(15*scale, 15*scale)
    note.PushedTexture:SetSize(15*scale, 15*scale)
    note.HighlightTexture:SetSize(15*scale, 15*scale)
    note.Display.Icon:SetSize(16*scale, 16*scale)
    note.NormalTexture:SetTexture("Interface/WorldMap/UI-QuestPoi-NumberIcons")
    note.PushedTexture:SetTexture("Interface/WorldMap/UI-QuestPoi-NumberIcons")
    note.HighlightTexture:SetTexture("Interface/WorldMap/UI-QuestPoi-NumberIcons")
    note.Display.Icon:SetTexture("Interface/WorldMap/UI-QuestPoi-NumberIcons")
    note.NormalTexture:SetTexCoord(0.500, 0.625, 0.375, 0.5)
    note.PushedTexture:SetTexCoord(0.375, 0.500, 0.375, 0.5)
    note.HighlightTexture:SetTexCoord(0.625, 0.750, 0.375, 0.5)
    note.Display.Icon:SetTexCoord(QuestPOI_CalculateNumericTexCoords(note.noteIdx, QUEST_POI_COLOR_BLACK ))
    note.Display.Icon:Show()
    note.tooltipText = text or ""

    note:RegisterForClicks("AnyUp")
    --click
    function note:OpenEditBox()
        if not noteEditbox then noteEditbox = makeNoteEditbox() end
        if noteEditbox.frame:IsShown() and noteEditbox.noteIdx == note.noteIdx then
            noteEditbox.frame:Hide()
        else
            noteEditbox.noteIdx = note.noteIdx
            noteEditbox:ClearAllPoints()
            noteEditbox.frame:SetPoint("TOPLEFT",note,"TOPRIGHT")
            noteEditbox.frame:Show()
            noteEditbox.multiBox:SetText(note.tooltipText)
            noteEditbox.multiBox.button:Enable()
        end
    end
    note:SetScript("OnClick",function(self,button,down)
        if button == "LeftButton" then
            L_CloseDropDownMenus()
            self:OpenEditBox()
        elseif button == "RightButton" then
            currentNote = note
            L_EasyMenu(noteMenu,noteDropDown, "cursor", 0 , -15, "MENU")
            if noteEditbox and noteEditbox.frame:IsShown() then
                noteEditbox.frame:Hide()
            end
        end
    end)
    note:SetScript("OnEnter",function()
        GameTooltip:SetOwner(UIParent, "ANCHOR_CURSOR")
        GameTooltip:AddLine(note.tooltipText , 1, 1, 1, 1)
        GameTooltip:Show()
    end)
    note:SetScript("OnLeave",function()
        GameTooltip:Hide()
    end)

    note:Show()
end