local needToUpdateMap = true
local lastAngle = -999
local lastUnitX, lastUnitY = -999, -999
local lastScale = -999
local lastMapFileName = -999
local lastForceCounter = 100

local lastPlayerPosX = -1
local lastPlayerPosY = -1
local lastActivePointX = -1
local lastActivePointY = -1
local lastActivePointM = -1
local lastActivePointF = -1

local Math = GUIUtils.Math

local currentMapUId = lastMapUId

local showWorldQuestsOnMapsWithoutPlayer = true

local lastEnabled = -1

local DGV = DugisGuideViewer
local LuaUtils = LuaUtils
if not DGV then return end

local GPS = DGV:RegisterModule("GPSArrowModule")
local HBDMigrate = LibStub("HereBeDragons-Migrate-Dugis")

if GPS then
	GPS.visualMapOverlays = {}
end

--Map dimensions when scale is 1
local nativeMapWidthPx = 224
local nativeMapHeightPx = 146

GPS.Options = {
	initialWidth = 150,
	--initialHeight --height is calculated automatically.
	--initialX = 450, --not used anymore. The position is the same as old arrow
	--initialY = -25, --not used anymore. The position is the same as old arrow
	initialZoom = 4,
	zoomMax = 80,
	zoomMin = 0.3,
	borderPath = "Interface/BattlefieldFrame/UI-BattlefieldMinimap-Border.blp", --Original
	arrowPath = "Interface\\AddOns\\DugisGuideViewerZ\\Artwork\\arrow_map.tga",
	playerArrowSize = function()
		local normalizedFactor = ((DGV:GetDB(DGV_GPS_ARROW_SIZE) or DGV:GetDefaultValue(DGV_GPS_ARROW_SIZE)) - 1) / 9
		return  25 + 50 * normalizedFactor
	end,
	--partyArrowSize = 8, --not used
	--raidArrowSize = 8, --not used
	opacity = 0.2,
	darkBackgroundAlpha = 0.5,
	locked = false,
	--showPlayers = true,
	oldArrowThesholdDistance = 210 - (30 * Minimap:GetZoom()),
	autoZoom = function()
		return DGV:UserSetting(DGV_GPS_ARROW_AUTOZOOM)
	end,
	autoZoomDuration_sec = 2,
	autoZoomDistanceFromEdge = 20,
	removeFog = true,
	minimapShowTreshold = 0.7, --bigger => zoom needs to be bigger more to see minimap
	minimapArea = 4, --bigger => bigger minimap area is shown => performance is lower. If minimapShowTreshold will be lower then this parameter should be increased. Integers only.
};

--Returns map Id (format 8.0) that should be used for GPS map
--Determines currently displayed map id for GPS map
function GPS.GetMapIdForGPSMap()
	return DGV:GetDisplayedOrPlayerMapId()
end

local lastMapUId = GPS.GetMapIdForGPSMap()

function GPS.GetScaleYdFactor()
	return (DGV:GetMapDimensionCached(GPS.GetMapIdForGPSMap())/500) / GPS.Scale()
end

function GPS.UpdateParentAnchor()
	if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) and not DugisArrowGlobal:IsEnabled3DArrow() then
		GPSArrowScroll:ClearAllPoints()
		GPSArrowScroll:SetPoint("CENTER", DGV.wayframe, 0, 0)
	else
		local x = GPSArrowScroll:GetLeft()
		local y = GPSArrowScroll:GetTop()
		GPSArrowScroll:ClearAllPoints()
		if x and y then
			y = GetScreenHeight() - GPSArrowScroll:GetTop()
			GPSArrowScroll:SetPoint("TOPLEFT", UIParent, x, -y) 
		else
			GPSArrowScroll:SetPoint("CENTER", UIParent, 0, 0) 
		end
	end
end

local GetWorldMapSize_cache = {}
GPS.GetWorldMapSize = function()
	mapId = GPS.GetMapIdForGPSMap()

	if WorldMapFrame:GetMapID() then
		GetWorldMapSize_cache[WorldMapFrame:GetMapID()] = {WorldMapFrame.ScrollContainer.Child:GetSize()}
	end

	if GetWorldMapSize_cache[mapId] then
		return unpack(GetWorldMapSize_cache[mapId])
	end

	return WorldMapFrame.ScrollContainer.Child:GetSize()
end

function GPS.RefreshAndCenter()
	lastUnitX = -1
	GPS.CenterPlayerOnMap()
end

function GPS.UpdateArrowSize()
	GPSArrowIcon.Texture:SetWidth(GPS.Options.playerArrowSize())
	GPSArrowIcon.Texture:SetHeight(GPS.Options.playerArrowSize())	
end

local poiTable = {}
GPS.TaskPOIArray = {}

local lastSize = 10
function GPS.UpdateSize()
	local normalizedFactor = ((DGV:GetDB(DGV_GPS_MAPS_SIZE) or DGV:GetDefaultValue(DGV_GPS_MAPS_SIZE)) - 1) / 9
	
	GPS.Options.initialWidth = 150 * (normalizedFactor + 0.5)
	GPS.Options.initialHeight = GPS.Options.initialWidth * (nativeMapHeightPx / nativeMapWidthPx)
	GPS.SetSize(GPS.Options.initialWidth, GPS.Options.initialHeight)	
	
	GPS.RefreshAndCenter()
	
	LuaUtils:Delay(1, function()
		lastPlayerPosX = -1
	end)
	
	if (DGV:GetDB(DGV_GPS_MAPS_SIZE) or DGV:GetDefaultValue(DGV_GPS_MAPS_SIZE)) > lastSize then
		GPS.FixTopPosition()
		GPS.PlaceButtonsOutideMapIfNeeded()
	end
	
	lastSize = (DGV:GetDB(DGV_GPS_MAPS_SIZE) or DGV:GetDefaultValue(DGV_GPS_MAPS_SIZE))
	
	GPS.UpdateBorder()
    
    if DugisArrowGlobal then
        DugisArrowGlobal.UpdateProgressBarPosition()
    end
end

function GPS.GetMapOverlaysFactor()
	return nativeMapWidthPx / (4*256) 
end

local BATTLEFIELD_TAB_SHOW_DELAY = 0.2;
local BATTLEFIELD_TAB_FADE_TIME = 0.15;
local DEFAULT_BATTLEFIELD_TAB_ALPHA = 0.75;
local DEFAULT_POI_ICON_SIZE = 22;
local BATTLEFIELD_MINIMAP_UPDATE_RATE = 0.1;
local NUM_BATTLEFIELDMAP_POIS = 50;

local BG_VEHICLES = {};

GPS.ShouldLoad = function()
	return true
end

function GPS.CreateBigTextures()
	if not GPSArrow.bigTextures then
		GPSArrow.bigTextures = {}
	end
	
	local i = 1
	for y = 1, 10 do
		for x = 1, 15 do
			if not GPSArrow.bigTextures["GPSArrowBig"..i] then
				local texture = GPSArrow:CreateTexture("GPSArrowBig"..i, "BACKGROUND");
				GPSArrow.bigTextures["GPSArrowBig"..i] = texture
			end
			i = i + 1
		end
	end

	GPS.UpdateBigTexturesPositions()
end

function GPS.UpdateBigTexturesPositions()
	local dX = 0
	local dY = 0
	local w = 1
	local h = 1
	
	if GPS.GetMapIdForGPSMap() == 947 then
		dX = 0
		dY = 4
		w = 0.98
		h = 0.9
	end

	local bigTileWidth = w * 14.933333333 --(GPSArrow1:GetWidth() * 4) / 15
	local bigTileHeight = h * 16.8 --(GPSArrow1:GetHeight() * 3) / 10
	
	local i = 1
	for y = 1, 10 do
		for x = 1, 15 do
			local texture = GPSArrow.bigTextures["GPSArrowBig"..i]
			if texture then
				texture:ClearAllPoints()
				texture:SetWidth(bigTileWidth)
				texture:SetHeight(bigTileHeight)
				texture:SetPoint("TOPLEFT", GPSArrow, (x-1) * bigTileWidth + dX , -((y-1) * bigTileHeight) + dY )
			end
			i = i + 1
		end
	end
end

function GPS:Initialize()
	
	lastSize = DGV:GetDB(DGV_GPS_MAPS_SIZE) or DGV:GetDefaultValue(DGV_GPS_MAPS_SIZE)
	
	GPS.Unload = function()
		if GPSArrowScroll then
			GPSArrowScroll:Hide()
			GPSArrowTab:SetAlpha(0)
		end
	end
	
	function GPS.Scale()
		return (GPSArrow.scale or 1 ) --*UIParent:GetScale()
	end

	function GPS.UpdatePlayerArrow(force)
		local shouldUpdate = false
		if force then
			shouldUpdate = true
			lastForceCounter = 100
		end
		
		if lastForceCounter > 0 then
			shouldUpdate = true
			lastForceCounter = lastForceCounter - 1
		end
		
		local angleRadians = LuaUtils.GetPlayerFacing_dugi()
		GPSArrowIcon.Texture:SetRotation(angleRadians)
		
		local angle = 0
        
        local mapId = GPS.GetMapIdForGPSMap()
        
        if not mapId then
            return
        end
		
		local unitX, unitY = DGV:GetPlayerPositionOnMap(mapId)
		
		local info = DGV.GetMapInfo(mapId)
		
		if info then
			local mapFileName = info.name
		end
		
		if angle ~= lastAngle or GPSArrow.scale ~= lastScale or unitX ~= lastUnitX or  unitY ~= lastUnitY or lastMapFileName~=mapFileName then
			lastForceCounter = 100
			shouldUpdate = true
		end
		
		if shouldUpdate then
			GPS.CenterPlayerOnMap()
		end
		
		lastAngle = angle
		lastUnitX, lastUnitY = unitX, unitY
		lastScale = GPSArrow.scale
		lastMapFileName = mapFileName
	end

	function GPS.CenterPlayerOnMap()
		local unitX, unitY = DGV:GetPlayerPositionOnMap(GPS.GetMapIdForGPSMap())
		
		if (unitX == 0 and unitY == 0) or DGV:IsPointOutOfTheMap(unitX, unitY) then
			unitX, unitY = 0.5, 0.5
			GPSArrowIcon:SetAlpha(0)
		else
			GPSArrowIcon:SetAlpha(1)
		end
		
		if lastUnitX == unitX and lastUnitY == unitY then
			--Position was not changed
			return
		end
		
		lastUnitX, lastUnitY = unitX, unitY
		if unitX == 0 and unitY == 0 or DGV:IsPointOutOfTheMap(unitX, unitY) then
			unitX, unitY = 0.5, 0.5
		end
		
		GPS.MoveMapToPoint(unitX, unitY)
	end

	function GPS.OnMouseWheel(self, delta) 
		if delta > 0 then
			 GPSArrow.scale = GPSArrow.scale * 1.1
		else
			GPSArrow.scale = GPSArrow.scale * 0.9
		end
		
		local maxZoomFactor = GPS.Options.zoomMax * (DGV:GetMapDimensionCached(GPS.GetMapIdForGPSMap())/5000)
		
		if  GPSArrow.scale > maxZoomFactor then
			GPSArrow.scale = maxZoomFactor
		end
		
		if  GPSArrow.scale < GPS.Options.zoomMin then
			GPSArrow.scale = GPS.Options.zoomMin
		end

		GPS.Zoom(GPSArrow.scale)
		GPS.RefreshAndCenter()
	end

	function GPS.OnShow(self)
		--SetMapToCurrentZone();
		GPS.Update();
		GPS.UpdateOpacity();
		GPS.UpdateBorder();
		GPS.RemoveFogForNewMaps()
		
		GPS.UpdateFrameSize()
		LuaUtils:Delay(2, function()
			GPS.UpdateFrameSize()
			GPS.Zoom(GPSArrow.scale)
		end)	
	end

	function GPS.OnHide(self)
		GPS.ClearTextures();
	end

	function GPS.UpdateTitlePosition()
		local bordX = GPSArrowBorder:GetLeft()
		local bordW = GPSArrowBorder:GetWidth()
		local textW = GPS.title:GetWidth()
		
		GPS.title:ClearAllPoints()
		
		if bordX and bordW and textW then
			if (bordW * 0.5 + bordX) < (textW * 0.5) then
				GPS.title:SetPoint("TOPLEFT", GPSArrowScroll, "BOTTOMLEFT", -bordX, -10)
				return 
			end
			
			if bordW < textW and (textW * 0.5 + bordX + bordW * 0.5) > GetScreenWidth() then
				local x = GetScreenWidth() - (bordX + bordW)
				GPS.title:SetPoint("TOPRIGHT", GPSArrowScroll, "BOTTOMRIGHT", x, -10)
				return 
			end
		end
		
		GPS.title:SetPoint("TOP", GPSArrowScroll, "BOTTOM", 0, -10)
	end
	
	function GPS.RefreshBorder()
		local factor = GPSArrowScroll:GetWidth() / 150
		
		GPSArrowBorder:SetWidth(GPSArrowScroll:GetWidth() + 7)
		GPSArrowBorder:SetHeight(GPSArrowScroll:GetHeight()+ 9)
		GPSArrowBackground:SetWidth(GPSArrowScroll:GetWidth() + 17 * factor)
		GPSArrowBackground:SetHeight(GPSArrowScroll:GetHeight() + 15 * factor)
		
		GPSArrowBorder:ClearAllPoints()
		GPSArrowBorder:SetPoint("TOP", GPSArrowScroll, 0, 6)
		
		GPS.UpdateTitlePosition()
	end

	function GPS.SetSize(w, h)
		GPSArrowScroll:SetWidth(w)
		GPSArrowScroll:SetHeight(h)
		GPS.RefreshBorder()
	end

	function GPS.UpdateWaypointSizeOnMinimapBattle()
		if DugisArrowGlobal and DugisArrowGlobal.waypoints then
			for i = 1, #DugisArrowGlobal.waypoints do
				local point = DugisArrowGlobal.waypoints[i]
				if point and point.GPSArrowPoint then
					local baseSize = 16

					if point.isCircularTexture then
						baseSize = 36
					end

                    point:UpdateSize()
				end
			end
		end
	end
    
	local gpsOverlays = {}
    
    --Only for big maps
	function GPS.RemoveFogForNewMaps()
		--Hidding all textures
		for _, texture in pairs(gpsOverlays) do
			texture:Hide()
		end
		
		local mapID = GPS.GetMapIdForGPSMap()
		
		local exploredMapTextures = C_MapExplorationInfo.GetExploredMapTextures(mapID);
		--WorldMapFrame.zoomLevels check to avoid lua error
		if exploredMapTextures and WorldMapFrame.ScrollContainer.zoomLevels then
			local  layerIndex = WorldMapFrame.ScrollContainer:GetCurrentLayerIndex();
			local layers = {}
			if mapID then
				layers = C_Map.GetMapArtLayers(mapID);
			end
			if not layers then
				return
			end
			local layerInfo = layers[layerIndex];
			
			if not layerInfo then
				return
			end
			
			local TILE_SIZE_WIDTH = layerInfo.tileWidth;
			local TILE_SIZE_HEIGHT = layerInfo.tileHeight;

			
			local layerW = layerInfo.layerWidth;
			local layerH = layerInfo.layerHeight;

			local textureIndex = 1
			
			for i, exploredTextureInfo in ipairs(exploredMapTextures) do
				local numTexturesWide = ceil(exploredTextureInfo.textureWidth/TILE_SIZE_WIDTH);
				local numTexturesTall = ceil(exploredTextureInfo.textureHeight/TILE_SIZE_HEIGHT);
				local texturePixelWidth, textureFileWidth, texturePixelHeight, textureFileHeight;
				for j = 1, numTexturesTall do
					if ( j < numTexturesTall ) then
						texturePixelHeight = TILE_SIZE_HEIGHT;
						textureFileHeight = TILE_SIZE_HEIGHT;
					else
						texturePixelHeight = mod(exploredTextureInfo.textureHeight, TILE_SIZE_HEIGHT);
						if ( texturePixelHeight == 0 ) then
							texturePixelHeight = TILE_SIZE_HEIGHT;
						end
						textureFileHeight = 16;
						while(textureFileHeight < texturePixelHeight) do
							textureFileHeight = textureFileHeight * 2;
						end
					end
					
					for k = 1, numTexturesWide do
						local texture = gpsOverlays[textureIndex] 
						
						if not texture then
							texture = GPSArrow:CreateTexture(nil, "OVERLAY");
							gpsOverlays[textureIndex] = texture
						end
						
						if ( k < numTexturesWide ) then
							texturePixelWidth = TILE_SIZE_WIDTH;
							textureFileWidth = TILE_SIZE_WIDTH;
						else
							texturePixelWidth = mod(exploredTextureInfo.textureWidth, TILE_SIZE_WIDTH);
							if ( texturePixelWidth == 0 ) then
								texturePixelWidth = TILE_SIZE_WIDTH;
							end
							textureFileWidth = 16;
							while(textureFileWidth < texturePixelWidth) do
								textureFileWidth = textureFileWidth * 2;
							end
						end
	
						local mapW, mapH
						mapW = nativeMapWidthPx
						mapH = nativeMapHeightPx
						
						local worldMapWidth, worldMapHeight = GPS.GetWorldMapSize()
						
						local wRatio = (DGV:IsBigMap(mapID) and 1 or 0.978) * nativeMapWidthPx/worldMapWidth
						local hRatio = (DGV:IsBigMap(mapID) and 1.15 or 1) * nativeMapHeightPx/worldMapHeight
						
						texture:SetWidth(texturePixelWidth * wRatio)
						texture:SetHeight(texturePixelHeight * hRatio) 
						
						local xRatio1 = texturePixelWidth/textureFileWidth
						local yRatio1 = texturePixelHeight/textureFileHeight
						
						texture:SetTexCoord(0, xRatio1, 0, yRatio1);
						local x, y = exploredTextureInfo.offsetX + (TILE_SIZE_WIDTH * (k-1)), -(exploredTextureInfo.offsetY + (TILE_SIZE_HEIGHT * (j - 1)))
			
						local normalizedX, normalizedY = x/layerW, y/layerH
						
						local x1, y1 = exploredTextureInfo.offsetX + (TILE_SIZE_WIDTH * (k-1)), -(exploredTextureInfo.offsetY + (TILE_SIZE_HEIGHT * (j - 1)))	
						texture:SetPoint("TOPLEFT", x1 *wRatio , y1 * hRatio);
                        local index = ((j - 1) * numTexturesWide) + k

						local id = exploredTextureInfo.fileDataIDs[index]
						texture:SetTexture(id);
						
						textureIndex = textureIndex + 1
						
						texture:Show();
                        
						 
                        if not DGV.IsExploredId(mapID, id) then
                            texture:SetVertexColor(0.5, 0.5, 0.5)
                            texture:SetDrawLayer("OVERLAY" , 1)
                        else
                            texture:SetVertexColor(1, 1, 1)
                            texture:SetDrawLayer("OVERLAY" , 2)
                        end
					end
				end
			end
		end	
	end

	--Scale of the internal map (internal map dimentions)
	function GPS.Zoom(scale)
		GPSArrow.scale = scale
		
		--Removing fog
		if overlayTexturesGPS then
			local factor = GPS.GetMapOverlaysFactor()
			for _, texture in pairs(overlayTexturesGPS) do
				if type(texture) == "table" then
					texture:ClearAllPoints()
					if texture.orgX then
						texture:SetPoint("TOPLEFT", GPSArrow, texture.orgX * factor, texture.orgY * factor)
						texture:SetWidth(texture.orgW * factor)
						texture:SetHeight(texture.orgH * factor)
					end
					
					if not GPS.Options.removeFog then
						texture:Hide()
					end
				end
			end
		end		
		
		GPSArrow:SetScale(scale)
		
		GPSArrowBackground.Texture:SetAllPoints(GPSArrowScroll)
		
		GPSArrowBorder:ClearAllPoints()
		GPSArrowScroll:SetClampedToScreen(true)
		
		GPS.RefreshBorder()
		
		GPS.UpdateWaypointSizeOnMinimapBattle()
		
		GPSArrowScroll:UpdateScrollChildRect()
		GPS.UpdatePlayerArrow(true)
		
		GPSArrow:Show()
		
		GPS.UpdateMinimapAlpha()

		for _, pin in pairs(GPS.questsPool) do
			GUIUtils.Quests:SetupQuestSize(pin, 30 / scale, 35 / scale)
		end
	end


	--     0,0 ....1......2......>
	--        .
	--        .
	--        1
	--        .
	--        .
	--        2
	--        .
	--        v

	function GPS.LocalMapPoint2ScaledPixelMapPoint(nx, ny)
		local mapW = GPSArrow:GetWidth() * GPS.Scale()
		local mapH = GPSArrow:GetHeight() * GPS.Scale()
		return nx * mapW, ny * mapH
	end

	function GPS.MoveMapToPoint(nx, ny)
		local scaledMapPointPixelX, scaledMapPointPixelY = GPS.LocalMapPoint2ScaledPixelMapPoint(nx , ny )
		
		local scrollBoxW = GPSArrowScroll:GetWidth()
		local scrollBoxH = GPSArrowScroll:GetHeight()
		
		local newScrollX = (scrollBoxW * 0.5) - scaledMapPointPixelX
		local newScrollY = (scrollBoxH * 0.5) - scaledMapPointPixelY
		
		GPSArrowScroll:SetHorizontalScroll(-newScrollX / GPS.Scale())
		GPSArrowScroll:SetVerticalScroll(-newScrollY / GPS.Scale())
	end

	local lastIndoors = IsIndoors()
	function GPS.CheckIndoorsChange() 
		if lastIndoors ~= IsIndoors()then
			GPS.UpdateMinimapTextures()
			GPS.UpdateMinimapAlpha()
		end
		lastIndoors = IsIndoors()
	end
	
	
	function GPS.OnEvent(self, event, ...)
		currentMapUId = GPS.GetMapIdForGPSMap()
	
		if ( event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED" or event == "ZONE_CHANGED_NEW_AREA" or event == "NEW_WMO_CHUNK" ) then
			if GPSArrow:IsShown() and GPSArrow:IsVisible()   then
				if ( not WorldMapFrame:IsShown() ) then
					LuaUtils:DugiSetMapToCurrentZone()
					GPS.Update();
				end
			end
			
			GPS.CheckIndoorsChange() 
			GPS.UpdateBigTexturesPositions()
			LuaUtils:Delay(2, function()
				GPS.RemoveFogForNewMaps()
			end)
		elseif ( event == "WORLD_MAP_UPDATE" ) then
			if ( GPSArrow:IsVisible() ) then
				GPS.Update();
			end
			
			if DGV.OnMapChangeUpdateGPSArrow then
				DGV:OnMapChangeUpdateGPSArrow( )
			end
			
			--local c, z, m, f = GetCurrentMapContinent(), GetCurrentMapZone(), (), DGV.GetCurrentMapDungeonLevel()
			
			GPS.RemoveFogForNewMaps()
			if lastMapUId ~= currentMapUId then
			
				
				GPS.UpdateMinimapTextures()
				GPSArrowMinimap:SetAlpha(0)
				GPSArrowMinimap.alpha = 0
				
				GPS.ZoomMapTo0()
				
				GPS.zoneChanged = true
				
				GPS:OnEvent("ZONE_CHANGED")
				
				GPS.UpdateVisibility()
			else
				GPS.CheckIndoorsChange() 
			end
			
			lastMapUId = currentMapUId
			
			GPS.UpdateOpacity()
			
			
		elseif event == "MINIMAP_UPDATE_ZOOM" then
			GPS.CheckIndoorsChange() 
		elseif event == "ZONE_CHANGED_INDOORS" then
			GPS.CheckIndoorsChange() 
		end
	end
	
	function GPS.ZoomMapTo0()
		local playerX, playerY = DGV:GetPlayerPositionOnMap(GPS.GetMapIdForGPSMap())
		
		local mapW_yd = DGV:GetMapDimensionCached(GPS.GetMapIdForGPSMap())
		
		if ((playerX == 0 and playerY == 0) or DGV:IsPointOutOfTheMap(playerX, playerY)) or mapW_yd == 1 then
			GPS.Zoom(1)
			GPS.RefreshAndCenter()
			return true
		end
	end
	
	local currentZooming = nil
	function GPS.ZoomSmoothly(newScale, duration, onEnd, breakFunction, targetChanged)
		local initM, initF = GPS.GetMapIdForGPSMap()
		
		if GPS.zooming then
			if targetChanged then
			    currentZooming.shouldBeCanceled = true
				GPS.zooming = false
			else
				return
			end
		end
		
		GPS.zooming = true
		duration = duration or GPS.Options.autoZoomDuration_sec
		local currentZoom = GPSArrow.scale
		local diff = newScale - currentZoom
		currentZooming = LuaUtils:ProcessInTime(duration, function(value)
			value = LuaUtils.inOutQuad(value, 0, 1)
			GPS.Zoom(currentZoom + (diff * value))
			GPS.RefreshAndCenter()
			
			local currentM = GPS.GetMapIdForGPSMap()
			
			if currentM ~= initM then
				GPS.zooming = false
				GPS.ZoomMapTo0()
				return "break"
			end
			
		end, function()  
			if onEnd then
				onEnd()
			end
			GPS.zooming = false
			currentZooming = nil
		end)
	end
	
	function GPS.CurrentDistanceToEdges(dx_yd, dy_yd)
		local mapW_yd, mapH_yd = DGV:GetMapDimensionCached(GPS.GetMapIdForGPSMap())
		
		--For scale = 1
		local viewportW_yd = (GPS.Options.initialWidth / nativeMapWidthPx) * mapW_yd
		local viewportH_yd = (GPS.Options.initialHeight / nativeMapHeightPx) * mapH_yd
		
		--For current scale
		local viewportWs_yd = viewportW_yd / GPSArrow.scale
		local viewportHs_yd = viewportH_yd / GPSArrow.scale
		
		local distanceXtoEdge_yd = viewportWs_yd * 0.5 - math.abs(dx_yd)
		local distanceYtoEdge_yd = viewportHs_yd * 0.5 - math.abs(dy_yd)
		
		return distanceXtoEdge_yd, distanceYtoEdge_yd
	end
	
	function GPS.ResolveScale(dx_yd, dy_yd, distanceXtoEdge_yd, distanceYtoEdge_yd)
		local mapW_yd, mapH_yd = DGV:GetMapDimensionCached(GPS.GetMapIdForGPSMap())
		
		--For scale = 1
		local viewportW_yd = (GPS.Options.initialWidth / nativeMapWidthPx) * mapW_yd
		local viewportH_yd = (GPS.Options.initialHeight / nativeMapHeightPx) * mapH_yd
		
		local viewportWs_yd = (distanceXtoEdge_yd + math.abs(dx_yd))/0.5 
		local viewportHs_yd = (distanceYtoEdge_yd + math.abs(dy_yd))/0.5 
		
		--For current scale
		local sx = viewportW_yd /viewportWs_yd
		local sy = viewportH_yd /viewportHs_yd
		
		return sx, sy
	end
	
	function GPS.CalculatedAutoZoomDistanceFromEdge()
		local mapW_yd = DGV:GetMapDimensionCached(GPS.GetMapIdForGPSMap())
		--Limiting to 6000 to prevent useless very high zoom in on large maps
		if mapW_yd > 6000 then
			mapW_yd = 6000
		end
		
		if mapW_yd == 0 then
			mapW_yd = 1000
		end
		
		return GPS.Options.autoZoomDistanceFromEdge / (mapW_yd / 1000)
	end
		
	--Current distances
	local dist_yd, dx_yd, dy_yd, pointM, pointF
	function GPS.UpdateMode(force, onlyMode, onWaypointChanged)
		GPS.Options.oldArrowThesholdDistance = 210 - (30 * Minimap:GetZoom())
		
		local duration = 0.7
		if onWaypointChanged then
			duration = 0
		end
		
		if (dist_yd and dist_yd > GPS.Options.oldArrowThesholdDistance and DGV:UserSetting(DGV_ENABLED_GPS_ARROW))
			and not DGV.wayframe.button:IsShown()  then
			GPS.SetMode("GPS-arrow", force, duration)
		else
			GPS.SetMode("old-arrow", force, duration)
		end
		
		if onlyMode then
			return
		end
		
		-------AUTOZOOM-------
		if not GPS.Options.autoZoom() or not GPS.ShuldBeGPSShown() then --should be off if Zone Map is not active or visible. 
			return
		end
  
		local DugisArrow = DugisArrowGlobal
  
		local playerX, playerY = DGV:GetPlayerPositionOnMap(GPS.GetMapIdForGPSMap())
		
		local active_point = DugisArrow:GetActivePoint()
		
		if active_point == nil 
		or (lastPlayerPosX == playerX and lastPlayerPosY == playerY
		and active_point.x == lastActivePointX  and active_point.y == lastActivePointY  and active_point.m == lastActivePointM  and active_point.f == lastActivePointF)
		then
			--Player position didnt change and active point didn't change
			return
		end
		
		local targetChanged = (active_point.x ~= lastActivePointX  
		or active_point.y ~= lastActivePointY  or active_point.m ~= lastActivePointM  
		or active_point.f ~= lastActivePointF)
		
		lastPlayerPosX, lastPlayerPosY = playerX, playerY
		lastActivePointX, lastActivePointY, lastActivePointM, lastActivePointF = active_point.x, active_point.y, active_point.m, active_point.f
		
		if dist_yd then
			local DugisArrow = DugisArrowGlobal
			
			local pointX, pointY = DugisArrow:GetActivePoint().x * nativeMapWidthPx, DugisArrow:GetActivePoint().y * nativeMapHeightPx
			local playerXOrg, playerYOrg = DGV:GetPlayerPositionOnMap(GPS.GetMapIdForGPSMap())
			
			if not playerXOrg then
				return
			end
			
			local playerX, playerY = playerXOrg * nativeMapWidthPx, playerYOrg * nativeMapHeightPx
			
			local viewportW, viewportH = GPSArrowScroll:GetWidth(), GPSArrowScroll:GetHeight()
			
			local needZoomOut, needZoomIn
			local distanceXtoEdge_yd, distanceYtoEdge_yd = GPS.CurrentDistanceToEdges(dx_yd, dy_yd)
			
			local m = GPS.GetMapIdForGPSMap()
			local mapW_yd, mapH_yd = DGV:GetMapDimension(m, f)
			
			--Player is on some different map so let him zoom
			if  (playerX == 0 and playerY ==  0) or DGV:IsPointOutOfTheMap(playerXOrg, playerYOrg) then
			
				return
			end
			
			local mapSizeFactor = mapW_yd / 900
			local distanceWaypointToEdgesMin = GPS.CalculatedAutoZoomDistanceFromEdge() * mapSizeFactor
			local distanceWaypointToEdgesMax = distanceWaypointToEdgesMin + GPS.CalculatedAutoZoomDistanceFromEdge() * mapSizeFactor
			
			local neededZoomChange = 1
			local needReZoom = false
			
			if distanceXtoEdge_yd < distanceWaypointToEdgesMin or distanceYtoEdge_yd < distanceWaypointToEdgesMin 
			or (math.abs(distanceXtoEdge_yd) > distanceWaypointToEdgesMax and math.abs(distanceYtoEdge_yd) > distanceWaypointToEdgesMax)
			then
				--Average
				local distanceWaypointToEdges = (distanceWaypointToEdgesMin + distanceWaypointToEdgesMax) * 0.5
				local sX, sY = GPS.ResolveScale(dx_yd, dy_yd, distanceWaypointToEdges, distanceWaypointToEdges)
				needReZoom = true
				
				if sX < sY then
					neededZoomChange = sX
				else
					neededZoomChange = sY
				end
				
				if neededZoomChange <= 0.5 then
					return
				end
			end
			
			if needReZoom then
				GPS.ZoomSmoothly(neededZoomChange, GPS.Options.autoZoomDuration_sec, nil, nil, targetChanged)
			end
		end
	end	
		
	function GPS.DistanceChanged(dist_yd_, dx_yd_, dy_yd_, pointM_, pointF_)
		dist_yd, dx_yd, dy_yd, pointM, pointF = dist_yd_, dx_yd_, dy_yd_, pointM_, pointF_
		GPS.UpdateMode()
	end	
	
	function GPS.SetMode(mode, force, duration)
		
		if duration == nil then
			duration = 0.7
		end
		
		if GPS.currentMode == mode and mode == "GPS-arrow" and GPSArrowScroll:GetAlpha() ~= 0 and not force then
			return
		end
		
		if GPS.currentMode == mode and mode == "old-arrow" and DGV.wayframe:GetAlpha() ~= 0 and not force then
			return
		end
		
		GPS.currentMode = mode
		
		local initialWayframeAlpha = DGV.wayframe:GetAlpha()
		local initialGPSAlpha = GPSArrowScroll:GetAlpha()
		
		if mode == "GPS-arrow" then
			LuaUtils:ProcessInTime(duration, function(value)
				if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
					DGV.wayframe:SetAlpha(initialWayframeAlpha * (1-value))
					local delta = 1 - initialGPSAlpha
					GPSArrowScroll:SetAlpha(initialGPSAlpha + delta * value)
				end

			end, function()
				GPS.RefreshBorder()
			end)
		end
		
		if mode == "old-arrow" then
			LuaUtils:ProcessInTime(duration, function(value)
				local delta = 1 - initialWayframeAlpha
				if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
					DGV.wayframe:SetAlpha(initialWayframeAlpha + delta * value)
					GPSArrowScroll:SetAlpha(initialGPSAlpha * (1-value))
				end
			end, function()
				GPS.RefreshBorder()
			end)
		end		

		GPS.OnModeChange()
	end
	
	function GPS.OnModeChange()
		if GPS.currentMode == "GPS-arrow" then
			GPSArrow:EnableMouse(true)
			GPSArrowTab:EnableMouse(true)
		end

		if GPS.currentMode == "old-arrow" then
			GPSArrow:EnableMouse(false)
			GPSArrowTab:EnableMouse(false)
		end

		if not DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
			GPSArrow:EnableMouse(true)
			GPSArrowTab:EnableMouse(true)
		end
	end

	function GPS.PlaceOutisideMap(frameToBePlaced, dX, dY, margin)
		if frameToBePlaced then
			return
		end	
		local screenW, screenH = GetScreenWidth(), GetScreenHeight()
		local onOffX, onOffY = frameToBePlaced:GetLeft(), frameToBePlaced:GetTop()
		local mapX, mapY = GPSArrowScroll:GetLeft(), GPSArrowScroll:GetTop()
        
        if not mapX or not mapY then
            return
        end
		
		--Map box 
		local mW = GPSArrowScroll:GetWidth()
		local mH = GPSArrowScroll:GetHeight()
		
		if onOffX 
		and onOffX + margin > mapX 
		and (onOffX - margin) < (mapX + mW)
		and onOffY - margin < mapY 
		and (onOffY + margin) > (mapY - mH)
		then
			frameToBePlaced:ClearAllPoints()
			frameToBePlaced:SetPoint("TOPLEFT", UIParent, mapX + mW + dX, -(screenH - mapY) + dY)
		end
	end
	

	
	function GPS.FixTopPosition()
		if not GPSArrowTab:GetTop() then
			return
		end
	
		local top = GetScreenHeight() - GPSArrowTab:GetTop()
		if top < -10 then
			if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
				local point, relativeTo, relativePoint, xOfs, yOfs = GPSArrowScroll:GetPoint(1)
				local arrowTop = GetScreenHeight() - DGV.wayframe:GetTop()
				local diff = GPSArrowScroll:GetHeight() * 0.5 - arrowTop
				GPSArrowScroll:ClearAllPoints()
				GPSArrowScroll:SetPoint(point, relativeTo, relativePoint, xOfs, -diff)
			else
				local point, relativeTo, relativePoint, xOfs, yOfs = GPSArrowScroll:GetPoint(1)
				yOfs = -20
				GPSArrowScroll:ClearAllPoints()
				GPSArrowScroll:SetPoint(point, relativeTo, relativePoint, xOfs, yOfs)
			end
		end
	end
	
	function GPS.PlaceButtonsOutideMapIfNeeded()
		GPS.PlaceOutisideMap(DugisOnOffButton, 20, 5, 20)
		GPS.PlaceOutisideMap(DugisSecureQuestButton, 25, -69, 25)
		if DGV.Modules.Target then
			GPS.PlaceOutisideMap(DGV.Modules.Target.Frame, 25, -35, 25)
		end
	end

	
	function GPS.HasSomeWaypoints()
		return DugisArrowGlobal and DugisArrowGlobal.waypoints and #DugisArrowGlobal.waypoints > 0
	end
	
	function GPS.ShuldBeGPSShown()
		return (GPS.HasSomeWaypoints() or not DGV:UserSetting(DGV_GPS_AUTO_HIDE)) and not DugisGuideUser.PetBattleOn == true
		and DGV:UserSetting(DGV_ENABLED_GPS_ARROW) and not DugisArrowGlobal:IsFirstWaypointAnItem() 
		--Please uncomment line below if you want to hide Zone Map on "World" map
		--and GPS.GetMapIdForGPSMap() ~= 946
	end
	
	function GPS.UpdateMerged(resetPosition)
		if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
			GPS.UpdateParentAnchor()
			GPS.title:SetAlpha(1)
			GPS.tta:SetAlpha(1)
			GPS.status:SetAlpha(1)
			GPS.PlaceButtonsOutideMapIfNeeded()
		else
			DGV.wayframe:SetAlpha(1)
			GPSArrowScroll:ClearAllPoints()
			local w = GPSArrowScroll:GetWidth()
			local x, y =  DGV.wayframe:GetLeft(), GetScreenHeight() - GPSArrowScroll:GetTop()
			
			if resetPosition then
				y = GetScreenHeight() - DGV.wayframe:GetTop() - 25
			end
			
			local xShift =  DGV.wayframe:GetWidth() + 80
			
			if x >  GetScreenWidth() * 0.5 then
				xShift = -w - 80
			end
			
			GPSArrowScroll:SetPoint("TOPLEFT", UIParent,"TOPLEFT" , x + xShift, -y)
			GPSArrowScroll:SetAlpha(1)
			GPS.title:SetAlpha(0)
			GPS.tta:SetAlpha(0)
			GPS.status:SetAlpha(0)
		end
		
		GPS.FixTopPosition()
		
		GPS.UpdateMode(true)
		GPS.UpdateParentAnchor()
	end
	
	function GPS.WaypointsChanged()
		GPS.UpdateMode(true, true, true)
		
		if not GPS.HasSomeWaypoints() then
			lastActivePointX = nil
		end

		GPS.UpdateWaypointSizeOnMinimapBattle()
	end
	
	function GPS.UpdateBorder()
		LuaUtils:Delay(0.3, function()
		    GPSArrowBorder:SetBackdrop({edgeFile = DGV:GetGPSBorderPath(), tile = false, tileSize = 32, edgeSize = 16})
		end)
	end
	
	function GPS.UpdateVisibility()
		GPS.UpdateBorder()
		LuaUtils:Delay(0.2, function()
			if GPS.ShuldBeGPSShown() then
				if not GPS.HasSomeWaypoints() and GPSArrow.scale ~= 1 then
					if not GPS.HasSomeWaypoints() and (DugisArrowGlobal.waypoints or DugisArrowGlobal.waypointsRemoved) then
						GPS.Zoom((DGV:GetDB(DGV_GPS_MAPS_SIZE) or DGV:GetDefaultValue(DGV_GPS_MAPS_SIZE)) * 0.10 + 0.25)
						GPS.RefreshAndCenter()
						
						if  GPSArrowScroll:GetAlpha() ~= 1  then
							GPSArrowScroll:Show()
							LuaUtils:ProcessInTime(1, function(value)
								GPSArrowScroll:SetAlpha(value)
							end)		
						end
					end
				end
			
				GPSArrowScroll:Show()
			else
				GPSArrowScroll:Hide()
				GPSArrowTab:SetAlpha(0)
			end
			
			if not GPS.HasSomeWaypoints() then
				GPS.title:Hide()
				GPS.tta:Hide()
				GPS.status:Hide()
			else
				GPS.title:Show()
				GPS.tta:Show()
				GPS.status:Show()
			end
			
			if lastEnabled ~= DGV:UserSetting(DGV_ENABLED_GPS_ARROW) 
			and (DGV:UserSetting(DGV_ENABLED_GPS_ARROW)) then
				--Move buttons outside map box
				LuaUtils:Delay(0.5, function()
					GPS.PlaceButtonsOutideMapIfNeeded()
				end)
			end
			
			lastEnabled = DGV:UserSetting(DGV_ENABLED_GPS_ARROW)
			
			DugisArrowGlobal.UpdateProgressBarPosition()
		end)
	end	
	
	--Updates 
	function GPS.Update()
		-- Fill in map tiles
        local mapId = GPS.GetMapIdForGPSMap()
        
        if not mapId then
            return
        end
        
		local mapInfo = DGV.GetMapInfo(mapId)
		
		if not mapInfo then
			return
		end
		
		local textureIds = C_Map.GetMapArtLayerTextures(mapId, 1)
		local isBigMap = DGV:IsBigMap(mapId)
		
		local i = 1
		local textute = _G["GPSArrow"..i]
		while textute do
			if isBigMap then
				textute:Hide()
			else
				textute:Show()
			end
			
			textute:SetTexture(textureIds[i]);
			i = i + 1
			textute = _G["GPSArrow"..i]
		end	
		
		local i = 1
		local textute = _G["GPSArrowBig"..i]
		while textute do
			if isBigMap then
				textute:Show()
			else
				textute:Hide()
			end
		
			textute:SetTexture(textureIds[i]);
			i = i + 1
			textute = _G["GPSArrowBig"..i]
		end
	

		-- Setup the POI's
		local iconSize = DEFAULT_POI_ICON_SIZE * GetBattlefieldMapIconScale();
		
		needToUpdateMap = true
		GPS.UpdatePlayerArrow()
	end

	GPS.questsPool = {}

	function GPS:UpdateQuestPOIs(isInThread)
		local tempTable = {}

		local WMT = DGV.Modules.WorldMapTracking

		if not WMT then
			return
		end

		for _, pin in pairs(GPS.questsPool) do 
			pin:Hide()
		end
	
		local mapID = GPS.GetMapIdForGPSMap()
		if not mapID or not DGV.IsQuestsTrackingEnabled() then
			return;
		end

		local questsOnMap = DGV.GetAllActiveQuests()
		
		for i, questInfo in pairs(questsOnMap) do
			LuaUtils:RestIfNeeded(isInThread)
			local questId = questInfo.questId
			local x, y, mapID_ = WMT:GetQuestPosition(questId, GPS.GetMapIdForGPSMap())

			if x then
				if mapID_ == mapID then
					x, y = Math:AvoidCircleOverlaping(x, y, questId, tempTable) 
					
					local pin = GPS.questsPool[i]

					if not pin then
						pin = CreateFrame("FRAME", nil, GPSArrowOverlayFrame, "QuestPinTemplate")
						GPS.questsPool[i] = pin
					end

					pin:SetScript("OnMouseDown", function(self)
						DGV.OnQuestPOIClick(self.questId)
					end)	

					local mW = GPSArrowOverlayFrame:GetWidth()
					local mH = GPSArrowOverlayFrame:GetHeight()

					pin:ClearAllPoints()
					pin:SetPoint("CENTER", GPSArrowOverlayFrame, "TOPLEFT", x * mW, -y * mH)
					pin:Show()
			
					pin.questId = questId;
				
					local isSuperTracked = questId == DGV.superTrackedQuestID;
				
					pin:SetFrameLevel(5001)
					pin:SetFrameStrata("TOOLTIP")
				
					GUIUtils.Quests:SetupQuestButton(pin, isSuperTracked, 30, 35, 
					isSuperTracked and "yellow" or "brown")

					GUIUtils.Quests:SetupQuestSize(pin, 30 / GPS.Scale(), 35 / GPS.Scale())
				end
			end
		end
	end

	function GPS.ClearTextures()
		for i=1, GPSArrow:GetAttribute("NUM_BATTLEFIELDMAP_OVERLAYS") do
			_G["GPSArrowOverlay"..i]:SetTexture(nil);
		end
	end

	local lastMouseOverCheck = GetTime()
	local hovering = false
	local lastMap = -1
	function GPS.OnUpdate(self, elapsed)
		
		local bestMap = GPS.GetMapIdForGPSMap()
		if lastMap ~= bestMap then
			GPS.OnEvent(nil, "WORLD_MAP_UPDATE")
			GPS:UpdateQuestPOIs()
		end
		lastMap = bestMap
	
		if needToUpdateMap then
			GPS.UpdatePlayerArrow()
		end
		
		if (GetTime() - lastMouseOverCheck) > 0.5 and not hovering then
			if GPSArrowScroll:IsMouseOver() and GPSArrowScroll:IsVisible() and GPSArrowScroll:GetAlpha() == 1 then
				
				if not (GPSArrowTab:GetAlpha() == 1) then
					hovering = true
					LuaUtils:ProcessInTime(0.2, function(value)
                        if not GPSArrowScroll:IsVisible() then
                            value = 0
                        end
						GPSArrowTab:SetAlpha(value)
					end, function()
						hovering = false
					end)
				end
			else
				if not GPSArrowTab:IsMouseOver() and not GPS.isDragging then
					GPSArrowTab:SetAlpha(0)
				end
			end
			lastMouseOverCheck = GetTime()
		end
		
		
		-- Throttle updates
		if ( GPSArrow.updateTimer < 0 ) then
			GPSArrow.updateTimer = BATTLEFIELD_MINIMAP_UPDATE_RATE;
		else
			GPSArrow.updateTimer = GPSArrow.updateTimer - elapsed;
			return;
		end
		GPSArrow.resizing = false
		
		GPS.UpdatePlayerArrow()
	end

	function GPS.GetMapsOpacity()
		return DGV:GetDB(DGV_GPS_MAPS_OPACITY) or DGV:GetDefaultValue(DGV_GPS_MAPS_OPACITY)
	end
	
	function GPS.SetMapsAlpha(value)
	
		for i=1, 500 do
			local frame = _G["GPSArrow"..i]
			if frame then
				frame:SetAlpha(value)
			else
				break;
			end
		end
		
		for i=1, 500 do
			local frame = _G["GPSArrowBig"..i]
			if frame then
				frame:SetAlpha(value)
			else
				break;
			end
		end
		
		if overlayTexturesGPS then
			for _, texture in pairs(overlayTexturesGPS) do
				if type(texture) == "table" then
					texture:SetAlpha(value);
				end
			end	
		end	
		
		if gpsOverlays then
			for _, texture in pairs(gpsOverlays) do
				if type(texture) == "table" then
					texture:SetAlpha(value);
				end
			end
		end	
		
		if GPSArrow:GetAttribute("NUM_BATTLEFIELDMAP_OVERLAYS") then
			for i=1, GPSArrow:GetAttribute("NUM_BATTLEFIELDMAP_OVERLAYS") do
				_G["GPSArrowOverlay"..i]:SetAlpha(mapsAlpha);
			end
		end
		
	end
	
	function GPS.UpdateOpacity()
		local bordersAlpha = DGV:GetDB(DGV_GPS_BORDER_OPACITY) or DGV:GetDefaultValue(DGV_GPS_BORDER_OPACITY)
		local mapsAlpha = GPS.GetMapsOpacity()
		
		
		local darkBackgroundAlpha = GPS.Options.darkBackgroundAlpha
		
		if mapsAlpha < darkBackgroundAlpha then
			darkBackgroundAlpha = mapsAlpha
		end
		
		GPSArrowBackground:SetAlpha(darkBackgroundAlpha)
		
		GPSArrowOptions.mapsAlpha = mapsAlpha
		
		GPSArrowBorder:SetAlpha(bordersAlpha);
		
		if not GPSArrowMinimap or GPSArrowMinimap:GetAlpha() == 0 then
			GPS.SetMapsAlpha(mapsAlpha)
		end
	end

	local GPSMinimapTextures = {}
    
    --Some maps are dungeons (Enum.UIMapType.Dungeon) but have terrain map
    function GPS.HasDungeonMapATerrain(uiMapID)
        if uiMapID == 627 then --Dalaran
            return true
        end
    end
 
	function GPS.UpdateMinimapTextures()
		if not GPSArrowMinimap then
			GPSArrowMinimap = CreateFrame("Frame", "GPSArrowMinimap", GPSArrow)
			GPSArrowMinimap:SetPoint("TOPLEFT",GPSArrow, 0,0)
			GPSArrowMinimap:SetWidth(225)
			GPSArrowMinimap:SetHeight(225)
			GPSArrowMinimap:SetAlpha(GPS.GetMapsOpacity())
			GPSArrowMinimap.alpha = GPS.GetMapsOpacity()
		end
		
		local TERRAIN_MAGIC =  1600/3
		local TERRAIN_PATH = 'world/minimaps/%s/map%02d_%02d'
		local UNDERWATER_PATH = 'world/minimaps/%s/noliquid_map%02d_%02d'
		
		local uiMapID = GPS.GetMapIdForGPSMap()
        
        if not uiMapID then
            return
        end
        
		GPSArrowMinimap.isDisplayed = false
		local noTerrainMap = ((DGV.GetMapInfo(uiMapID).mapType == Enum.UIMapType.Micro 
        or DGV.GetMapInfo(uiMapID).mapType  == Enum.UIMapType.Dungeon))
        and not GPS.HasDungeonMapATerrain(uiMapID)
		
		local floorNum, dBRx, dBRy, dTLx, dTLy = DGV:GetCurrentMapDungeonLevel_dugiDetails(uiMapID)
        
        floorNum = floorNum or 0
		
		if not noTerrainMap and not IsIndoors() then
			local terrainMapID = DGV:GetAreaMapInfo_dugi(uiMapID) or -1
			
			local _, TLx, TLy, BRx, BRy = DGV:GetCurrentMapZone_dugi(uiMapID)
			
			if DGV:DungeonUsesTerrainMap_dugi(uiMapID) then
				floorNum = floorNum - 1
			end
			
			if floorNum > 0 then
				TLx, TLy, BRx, BRy = dTLx, dTLy, dBRx, dBRy
			end
			
			if TLx 
			and TLx ~= 0 
			and DGV.TERRAIN_MAPS[terrainMapID]
			and uiMapID ~= 905 --Argus
			and uiMapID ~= 994 --Argus
			and uiMapID ~= 203 --Vashj'ir
			and (floorNum ~= 0 or TLx ~=0 or  TLy ~=0 or  BRx ~=0 or BRy ~=0)
			then
				local areaWidth, areaHeight = abs(BRx-TLx), abs(BRy-TLy)	
				
				local sizeBaseW = 219
				local sizeBaseH = sizeBaseW * (668 / 1002)
				
				local tileSize = sizeBaseW/areaWidth*TERRAIN_MAGIC
				local iTileX, fTileX = math.modf(32-TLx/TERRAIN_MAGIC)
				local iTileY, fTileY = math.modf(32-TLy/TERRAIN_MAGIC)
				local tileOffsetX, tileOffsetY = (TLx-((iTileX-32)*-TERRAIN_MAGIC))/areaWidth, (TLy-((iTileY-32)*-TERRAIN_MAGIC))/areaHeight
				local numTilesX, numTilesY = ceil((areaWidth+fTileX*TERRAIN_MAGIC)/TERRAIN_MAGIC), ceil((areaHeight+fTileY*TERRAIN_MAGIC)/TERRAIN_MAGIC)
				
				local n = 0
				for y=1,numTilesY do
					for x=1,numTilesX do
					
						local playerIn = false
						
						local unitX, unitY = DGV:GetPlayerPositionOnMap(GPS.GetMapIdForGPSMap())
					
						if ((unitX == 0 or unitX == nil) and (unitY == 0 or unitY == nil)) or DGV:IsPointOutOfTheMap(unitX, unitY) then
							unitX, unitY = 0.5, 0.5
						end
					
						local currentNormXMin = (x - 1 - GPS.Options.minimapArea)/numTilesX
						local currentNormXMax = (x + GPS.Options.minimapArea)/numTilesX
						
						local currentNormYMin = (y - 1 - GPS.Options.minimapArea)/numTilesY
						local currentNormYMax = (y + GPS.Options.minimapArea)/numTilesY
						
						if unitX >= currentNormXMin and unitX <= currentNormXMax
						and unitY >= currentNormYMin and unitY <= currentNormYMax then
							playerIn = true
						end
					
						if playerIn then
							n = n + 1
							if not GPSMinimapTextures[n] then
								GPSMinimapTextures[n] = GPSArrowMinimap:CreateTexture(nil, "BACKGROUND", nil)
								GPSMinimapTextures[n]:SetAlpha(1)
							end
							local texture = GPSMinimapTextures[n]
							
							local textureWidth, textureHeight = tileSize, tileSize
							local textureOffsetX, textureOffsetY = tileOffsetX*sizeBaseW+tileSize*(x-1), -tileOffsetY*sizeBaseH-tileSize*(y-1)
							local left, right, top, bottom = 0, 1, 0, 1
							if numTilesX == 1 and numTilesY == 1 then -- only 1 tile.. trim from all sides
								textureWidth, textureHeight = sizeBaseW, sizeBaseH
								textureOffsetX, textureOffsetY = 0, 0
								left = fTileX
								top = fTileY
								right = (sizeBaseW+fTileX*tileSize)/tileSize
								bottom = (sizeBaseH+fTileY*tileSize)/tileSize
							elseif numTilesX == 1 then -- only 1 tile wide, trim from left and right
								textureWidth = sizeBaseW
								textureOffsetX = 0
								left = fTileX
								right = (sizeBaseW+fTileX*tileSize)/tileSize
							elseif numTilesY == 1 then -- only 1 tile tall, trim from top and bottom
								textureHeight = sizeBaseH
								textureOffsetY = 0
								top = fTileY
								bottom = (sizeBaseH+fTileY*tileSize)/tileSize
							elseif y == 1 and x == 1 then -- top left corner, trim from top and left
								textureWidth, textureHeight = tileSize - fTileX*tileSize, tileSize - fTileY*tileSize
								textureOffsetX, textureOffsetY = 0, 0
								left = fTileX
								top = fTileY
							elseif y == 1 and x == numTilesX then -- top right corner
								textureWidth, textureHeight = sizeBaseW - textureOffsetX, tileSize - fTileY*tileSize
								textureOffsetY = 0
								top = fTileY
								right = textureWidth/tileSize
							elseif y == numTilesY and x == numTilesX then -- bottom right corner
								textureWidth, textureHeight = sizeBaseW - textureOffsetX, textureOffsetY + sizeBaseH
								right = textureWidth/tileSize
								bottom = textureHeight/tileSize
							elseif y == numTilesY and x == 1 then -- bottom left corner
								textureWidth, textureHeight = tileSize - fTileX*tileSize, textureOffsetY + sizeBaseH
								textureOffsetX = 0
								left = fTileX
								bottom = textureHeight/tileSize
							elseif y == 1 then -- top row
								textureHeight = tileSize - fTileY*tileSize
								textureOffsetY = 0
								top = fTileY
							elseif x == numTilesX then -- right column
								textureWidth = sizeBaseW - textureOffsetX
								right = textureWidth/tileSize
							elseif y == numTilesY then -- bottom row
								textureHeight = textureOffsetY + sizeBaseH
								bottom = textureHeight/tileSize
							elseif x == 1 then -- left column
								textureWidth = tileSize - fTileX*tileSize
								textureOffsetX = 0
								left = fTileX
							end
							
							texture:SetTexCoord(left, right, top, bottom)
							texture:SetPoint('TOPLEFT', textureOffsetX, textureOffsetY)
							
							local paf = (
                            uiMapID == 201 
                            or uiMapID == 204 
                            or uiMapID == 205
                            ) and UNDERWATER_PATH or TERRAIN_PATH
							local texturePath = paf:format(DGV.TERRAIN_MAPS[terrainMapID], iTileX+(x-1), iTileY+(y-1))
							texture:SetTexture(texturePath, false)
							texture:SetSize(textureWidth, textureHeight)
							texture:Show()
						end
					end
				end
				
				for i=n+1,#GPSMinimapTextures do
					GPSMinimapTextures[i]:Hide()
				end
				
				GPSArrowMinimap:Show()
				GPSArrowMinimap.isDisplayed = true
				return
			else
				GPSArrowMinimap:Hide()
				return
			end
		end
		
		for i=1,#GPSMinimapTextures do
			GPSMinimapTextures[i]:Hide()
		end
	end

	local UpdateMinimapAlpha_awaiting = false
	function GPS.UpdateMinimapAlpha()
		if not GPSArrowMinimap or UpdateMinimapAlpha_awaiting then
			return
		end
		
		if not DGV:UserSetting(DGV_GPS_MINIMAP_TERRAIN_DETAIL) or not GPSArrowMinimap.isDisplayed then
			GPS.SetMapsAlpha(GPS.GetMapsOpacity())
			GPSArrowMinimap:SetAlpha(0)
			GPSArrowMinimap.alpha = 0
			GPSArrowMinimap:Hide()
			return
		else
			GPSArrowMinimap:Show()
		end
		
		--Fading in progress
		if GPS.fading then
			LuaUtils:Delay(1, function()
				UpdateMinimapAlpha_awaiting = false
				GPS.UpdateMinimapAlpha()
			end)
			UpdateMinimapAlpha_awaiting = true
			return
		end
		
		local factor = GPS.GetScaleYdFactor()
		
		local newAlpha = 0
		local oldAlpha = GPSArrowMinimap.alpha
		local shouldFade = false
		if factor < GPS.Options.minimapShowTreshold then
			newAlpha = GPS.GetMapsOpacity()
		end
		
		if newAlpha ~= oldAlpha then
			GPS.fading = true
			GPS.UpdateParentAnchor()
			LuaUtils:ProcessInTime(0.5, function(value)
				if GPS.zoneChanged then
					GPS.fading = false
					GPS.zoneChanged = false
					return "break"
				end 
			
				local val = oldAlpha + value * (newAlpha - oldAlpha)
				GPSArrowMinimap:SetAlpha(val)
				GPSArrowMinimap.alpha = val
				
				if newAlpha > 0 then
					GPS.SetMapsAlpha((1-value) * GPS.GetMapsOpacity())
				else
					GPS.SetMapsAlpha(value * GPS.GetMapsOpacity())
				end
				
			end, function()  
				GPSArrowMinimap:SetAlpha(newAlpha)
				GPSArrowMinimap.alpha = newAlpha
				GPS.zoneChanged = false
				GPS.fading = false
			end)	
		end
	end

	GPS.OnMouseDown = function(clickedTab)
		if DugisArrowGlobal:GetSetting("gps_locked") then
			return
		end  

		if clickedTab and not GPS.ShuldBeGPSShown() then
			return
		end

		if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW)  then
			if DugisArrowGlobal:IsEnabled3DArrow() then
				GPSArrowScroll:StartMoving()
			else
				DugisArrowFrame:StartMoving()   
			end
		else
			GPSArrowScroll:StartMoving()  
		end
		GPS.isDragging = true
	end

	GPS.OnMouseUp = function()
		if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
			if DugisArrowGlobal:IsEnabled3DArrow()  then
				GPSArrowScroll:StopMovingOrSizing() 
			else
				DugisArrowFrame:StopMovingOrSizing()   
			end

			GPS.UpdateTitlePosition()
			DugisGuideViewer.Modules.DugisArrow.OnActionButtonDragStop(true)  
		else
			GPSArrowScroll:StopMovingOrSizing()  
		end
		GPS.FixTopPosition()
		GPS.isDragging = false
	end
	
	GPS.UpdateFrameSize = function()
		if DGV:IsBigMap(GPS.GetMapIdForGPSMap()) then
			local bigTileWidth = (GPSArrow1:GetWidth() * 4) 
			local bigTileHeight = (GPSArrow1:GetHeight() * 3) 
			GPSArrow:SetSize( bigTileWidth,  bigTileHeight ) --* 0.8
		else
			local worldMapWidth, worldMapHeight = WorldMapFrame.ScrollContainer.Child:GetSize()
			local tileSize = GPSArrow1:GetWidth()
			local worldTileSizeW = 256 - DGV:GetMapWidthCorrection(GPS.GetMapIdForGPSMap())
			local worldTileSizeH = 256
			GPSArrow:SetSize(worldMapWidth / worldTileSizeW * tileSize, worldMapHeight / worldTileSizeH * tileSize)
		end
	end
	
	GPS.Load = function()
		lastUnitY = -1
		GPS.loaded = true
		lastForceCounter = 100
		
		lastEnabled = DGV:UserSetting(DGV_ENABLED_GPS_ARROW)
		
		if not GPSArrow.registeredEvents then
			GPSArrow:SetAttribute("NUM_BATTLEFIELDMAP_OVERLAYS",0);
			GPSArrow:RegisterEvent("PLAYER_ENTERING_WORLD");
			GPSArrow:RegisterEvent("ZONE_CHANGED");
			GPSArrow:RegisterEvent("ZONE_CHANGED_NEW_AREA");
			
			--todo: find replacement
			--GPSArrow:RegisterEvent("WORLD_MAP_UPDATE");
			GPSArrow:RegisterEvent("NEW_WMO_CHUNK");
			GPSArrow:RegisterEvent("MINIMAP_UPDATE_ZOOM");
			GPSArrow:RegisterEvent("ZONE_CHANGED_INDOORS");
			
			GPSArrow:SetScript("OnShow", GPS.OnShow)
			GPSArrow:SetScript("OnHide", GPS.OnHide)
			GPSArrow:SetScript("OnEvent", GPS.OnEvent)
			GPSArrow:SetScript("OnUpdate", GPS.OnUpdate)
			
			hooksecurefunc(WorldMapFrame, "OnMapChanged", function()
				if GPSArrowOptions then
					GPS.OnEvent(nil, "WORLD_MAP_UPDATE")
				end

				GPS:UpdateQuestPOIs()
			end)
			
			GPSArrow.registeredEvents = true
		end
		
		GPSArrow.updateTimer = 0;
		
		if not GPSArrowOverlayFrame then
			CreateFrame("FRAME", "GPSArrowOverlayFrame", GPSArrow)
			GPSArrow.map_overlay = GPSArrowOverlayFrame
			
			GPSArrow.map_overlay:SetAllPoints()
			GPSArrow.map_overlay:EnableMouse(false)
			GPSArrow.map_overlay:SetScript("OnUpdate", function()
				GPS.UpdatePlayerArrow()
			end)
		end
		
		--Fixing oryginal Zoom Map player position
		
		if not WorldMapFrame:IsShown() then
			WorldMapFrame:Show()
			WorldMapFrame:Hide()
		end

		if ( not GPSArrowOptions ) then
			GPSArrowOptions = GPS.Options;
		end

		GPS.UpdateOpacity();
		
		GPSArrowBorder:ClearAllPoints()
		
		if not GPSArrowScroll then
			CreateFrame("ScrollFrame", "GPSArrowScroll", UIParent)
			
			GPSArrowScroll:SetSize(300, 200)
			GPSArrowScroll:SetScrollChild(GPSArrow)            
			
			GPSArrowScroll:UpdateScrollChildRect()
			GPSArrowScroll:SetVerticalScroll(0)
			GPSArrowScroll:SetHorizontalScroll(0)
			
			GPSArrowScroll:EnableMouseWheel(true)
			
			GPSArrowScroll:HookScript("OnMouseWheel", GPS.OnMouseWheel)
			
			GPS.UpdateParentAnchor()
			
			GPS.UpdateBorder()
			
		end
		
		if not GPS.status then
			GPS.status = GPSArrowScroll:CreateFontString("OVERLAY", nil, "GameFontNormalSmall")
			GPS.tta = GPSArrowScroll:CreateFontString("OVERLAY", nil, "GameFontNormalSmall")
			GPS.title = GPSArrowScroll:CreateFontString("OVERLAY", nil, "GameFontHighlightSmall")
			
			hooksecurefunc(GPS.title, "SetText", function()
				GPS.UpdateTitlePosition()
			end)
		end

		GPS.status:SetText("")
		GPS.tta:SetText("")
		GPS.title:SetText("")

		if DGV:UserSetting(DGV_GPS_MERGE_WITH_DUGI_ARROW) then
			GPSArrowScroll:SetAlpha(0)
			DGV.wayframe:SetAlpha(0)
		else
			GPS.title:SetAlpha(0)
			GPS.tta:SetAlpha(0)
			GPS.status:SetAlpha(0)
		end

		GPSArrow:EnableMouseWheel(false)
		
		GPSArrow:SetScript("OnMouseDown", function(_, button)
			if button == "LeftButton" then
				GPS.OnMouseDown(false)
			end
		end)
		
		GPSArrow:SetScript("OnMouseUp", function(_, button)
			if button == "RightButton" then
				DugisArrowGlobal.WayFrame_OnClick()
			end

			if button == "LeftButton" then
				GPS.OnMouseUp(false)
			end
		end)

		GPSArrow:EnableMouse(true)

		GPSArrowBorder:SetParent(GPSArrowScroll)
		GPSArrowBackground:SetParent(GPSArrowScroll)
	 
		GPSArrowBorder:SetFrameStrata(GPSArrowScroll:GetFrameStrata())
		
		GPSArrowBackground:SetFrameStrata(GPSArrowScroll:GetFrameStrata())
		GPSArrowBackground:SetFrameLevel(GPSArrowScroll:GetFrameLevel()-1)
		
		GPS.Zoom(GPS.Options.initialZoom)
	
		GPSArrowIcon.Texture:SetTexture(GPS.Options.borderPath)
		GPSArrowIcon:SetFrameStrata(GPSArrowUnitPositionFrame:GetFrameStrata())
		GPSArrowIcon:SetFrameLevel(GPSArrowUnitPositionFrame:GetFrameLevel() + 1)
		GPSArrowIcon.Texture:SetTexture(GPS.Options.arrowPath)
		GPS.UpdateArrowSize()

		GPSArrowIcon:SetParent(GPSArrowScroll)
	
		GPSArrowTab:EnableMouse(true)
		GPSArrowScroll:SetMovable(true)
		
		GPSArrowTab:SetScript("OnMouseDown", function() 
			GPS.OnMouseDown(true)
		end)
			
		GPSArrowTab:SetScript("OnMouseUp", function() 
			GPS.OnMouseUp(true)
		end)
	
		GPS.UpdateSize()
		
		DugisGuideViewer.Modules.GPSArrowModule.CreateBigTextures()
	
		GPSArrowScroll:Hide()
		GPSArrowTab:SetAlpha(0)

---------------------------------
----------- WOW Classic:---------
---------------------------------  
--GPSArrowBlob was not created because of lack of API. Todo: Find replacement  		
--		GPSArrowBlob:Show()
		GPSArrowIcon:Show()
		
		GPS.UpdatePlayerArrow(true)
		
		GPS.UpdateVisibility()
		
		GPSArrowTab:SetPoint("TOPLEFT", "GPSArrowScroll", "TOPLEFT", -4, 32);
		
		if not GPSArrowMinimap then
			GPS.UpdateMinimapTextures()
		end
		
		GPS.UpdateFrameSize()
		
	
	end
end

function DGV:GetMapWidthCorrection(mapId)
	if mapId then
		mapId = tonumber(mapId) 
		
		--Easeter kingdoms except few maps
		if mapId <= 1438 and mapId >= 1416 and mapId ~= 1421 then
			return 6
		end
		
		--Kalimdor
		if (mapId <= 1452 and mapId >= 1440) or mapId == 1412 or mapId == 1413 or mapId == 1411 then
			return 6
		end
	end

	return 0
end
