Imports System.Collections.ObjectModel
Imports ThinkGeo.Core
Imports ThinkGeo.UI.WinForms

Public Class CustomTextStyle_2
  Inherits TextStyle
  Private msBaseKey As String = Guid.NewGuid.ToString
  Private moLabelCoords As New Dictionary(Of String, LabelPosition)
  Private moColumnValues As New Dictionary(Of String, String)
  Private Class LabelPosition
    Public WorldPosition As Vertex
    Public XOffsetPixel As Single = 0
    Public YOffsetPixel As Single = 0
  End Class

  Public Enum EnumTextSymbolType
    Polygon = 1
    Line = 2
    Point = 3
  End Enum
  Public Property TextSymbolType As EnumTextSymbolType
  Private moMap As MapView
  Public Sub New(sTextColumnName As String, oGeoFont As GeoFont, oBrush As GeoSolidBrush, oMap As MapView, eTextSymbolType As EnumTextSymbolType)
    MyBase.New(sTextColumnName, oGeoFont, oBrush)
    moMap = oMap
    TextSymbolType = eTextSymbolType
  End Sub

  Protected Overrides Sub DrawCore(oFeatures As IEnumerable(Of Feature), canvas As GeoCanvas, labelsInThisLayer As Collection(Of SimpleCandidate), labelsInAllLayers As Collection(Of SimpleCandidate))
    Dim oLabelingCandidates As Collection(Of LabelingCandidate)
    Dim oPointsLayer As InMemoryFeatureLayer = Nothing
    Dim oPoint As PointShape = Nothing
    Dim oScreenPoint As ScreenPointF
    Dim oaScreenPoints(0) As ScreenPointF
    Dim oInfo As LabelInformation
    Dim oOffsetScreenPoint As ScreenPointF

    'MyBase.DrawCore(oFeatures, canvas, labelsInThisLayer, labelsInAllLayers)
    'Return

    If Me.SplineType = SplineType.ForceSplining OrElse Me.SplineType = SplineType.StandardSplining Then
      '***** We do not currently support the repositioning of labels that are set to
      '***** spline with the line they are labeling.
      MyBase.DrawCore(oFeatures, canvas, labelsInAllLayers, labelsInAllLayers)
      Return
    End If

    oFeatures = GetLabeledFeatures(CType(oFeatures, Collection(Of Feature)), canvas, labelsInThisLayer, labelsInAllLayers)

    For Each oFeature As Feature In oFeatures
      'oLabelingCandidates = GetLabelingCandidateCore(oFeature, canvas)
      oLabelingCandidates = CType(oFeature.Tag, Collection(Of LabelingCandidate))
      'Dim i As Integer = 1
      For Each oCandidate As CustomLabelingCandidate In oLabelingCandidates
        Dim sKey As String = msBaseKey + "_" + oFeature.Id + "_" + oCandidate.LabelingId.ToString
        'Dim sKey As String = msBaseKey + "_" + i.ToString
        oInfo = oCandidate.LabelInformation(0)
        If moLabelCoords.ContainsKey(sKey) Then
          oOffsetScreenPoint = MapUtil.ToScreenCoordinate(canvas.CurrentWorldExtent, moLabelCoords(sKey).WorldPosition.X, moLabelCoords(sKey).WorldPosition.Y, canvas.Width, canvas.Height)
          oScreenPoint = New ScreenPointF(CSng(oInfo.PositionInScreenCoordinates.X), CSng(oInfo.PositionInScreenCoordinates.Y))
          'If moLabelCoords(sKey).XOffsetPixel = 0 Then
          '***** The offset has not yet been calculated, so do it.
          moLabelCoords(sKey).XOffsetPixel = CSng(oInfo.PositionInScreenCoordinates.X) - oOffsetScreenPoint.X
          moLabelCoords(sKey).YOffsetPixel = CSng(oInfo.PositionInScreenCoordinates.Y) - oOffsetScreenPoint.Y
          'End If
          Select Case TextSymbolType
            '***** If the label is for a point or line then it's world coordinates will change based on the zoom level in order to keep
            '***** it the same distance in pixels from the feature being labelled. So instead of just using the world coordinates for
            '***** positioning the label, and offset is computed. Note that it only has to be calculated once for each time the label is
            '***** repositioned.
            Case EnumTextSymbolType.Point, EnumTextSymbolType.Line
              oaScreenPoints(0) = oScreenPoint
              '***** Create the point and draw the label using the offset calculated above. The PointShape (oPoint) will be
              '***** added to the EditShapesLayer of the CustomLabelEditingOverlay on the map.
              oPoint = MapUtil.ToWorldCoordinate(canvas.CurrentWorldExtent, oScreenPoint.X - moLabelCoords(sKey).XOffsetPixel, oScreenPoint.Y - moLabelCoords(sKey).YOffsetPixel, canvas.Width, canvas.Height)
              '***** Cayzu #12951
              '***** Added the call to DrawMask, which draws the rectangle around the label, if one has been specified.
              '***** SET 07/21/2020
              DrawMask(oCandidate, canvas, labelsInThisLayer, labelsInAllLayers)
              canvas.DrawText(oInfo.Text, Me.Font, Me.TextBrush, Me.HaloPen, oaScreenPoints, DrawingLevel.LabelLevel, -moLabelCoords(sKey).XOffsetPixel, -moLabelCoords(sKey).YOffsetPixel, DrawingTextAlignment.Default, CSng(oInfo.RotationAngle))
            Case EnumTextSymbolType.Polygon
              '***** Create the point and draw the label without the offset calculated above. The PointShape (oPoint) will be
              '***** added to the EditShapesLayer of the CustomLabelEditingOverlay on the map.
              oaScreenPoints(0) = oOffsetScreenPoint
              oPoint = New PointShape(moLabelCoords(sKey).WorldPosition.X, moLabelCoords(sKey).WorldPosition.Y)
              'if Not CheckOverlapping(oCandidate, canvas, labelsInThisLayer, labelsInAllLayers) Then
              '***** Cayzu #12951
              '***** Added the call to DrawMask, which draws the rectangle around the label, if one has been specified.
              '***** SET 07/21/2020
              DrawMask(oCandidate, canvas, labelsInThisLayer, labelsInAllLayers)
              canvas.DrawText(oInfo.Text, Me.Font, Me.TextBrush, Me.HaloPen, oaScreenPoints, DrawingLevel.LabelLevel, 0, 0, DrawingTextAlignment.Center, CSng(oInfo.RotationAngle))
              'End If
          End Select
        Else
          '***** The label has not been relocated, so just draw it in its default location.
          '***** Note that if a pixel offset has been set by the user that the coordinates from the
          '***** LabelInfo object (oInfo) will already be adjusted for that.
          '***** SET 08/09/2019
          oScreenPoint = New ScreenPointF(CSng(oInfo.PositionInScreenCoordinates.X), CSng(oInfo.PositionInScreenCoordinates.Y))
          oaScreenPoints(0) = oScreenPoint
          oPoint = MapUtil.ToWorldCoordinate(canvas.CurrentWorldExtent, CSng(oInfo.PositionInScreenCoordinates.X), CSng(oInfo.PositionInScreenCoordinates.Y), canvas.Width, canvas.Height)
          canvas.DrawText(oInfo.Text, Me.Font, Me.TextBrush, Me.HaloPen, oaScreenPoints, DrawingLevel.LabelLevel, 0, 0, DrawingTextAlignment.Right, CSng(oInfo.RotationAngle))
          'End If
        End If
        oPoint.Tag = oInfo.RotationAngle
        oPoint.Id = sKey
      Next oCandidate
    Next oFeature


  End Sub
  ''' <summary>
  ''' Gets a collection of features for which labels should be drawn given the 
  ''' duplicate/partial/overlapping labelling rules on the class.
  ''' Needed because this class draws the labels 'manually'. Taken from ThinkGeo forum.
  ''' SET 11/07/2019
  ''' </summary>
  ''' <param name="oAllFeatures"></param>
  ''' <param name="oCanvas"></param>
  ''' <param name="labelsInThisLayer"></param>
  ''' <param name="labelsInAllLayers"></param>
  ''' <returns></returns>
  Public Function GetLabeledFeatures(ByVal oAllFeatures As Collection(Of Feature), ByVal oCanvas As GeoCanvas, ByVal labelsInThisLayer As Collection(Of SimpleCandidate), ByVal labelsInAllLayers As Collection(Of SimpleCandidate)) As Collection(Of Feature)
    'Dim oUnfilteredFeatures As Collection(Of Feature) = MyBase.FilterFeatures(oAllFeatures, oCanvas)
    Dim oUnfilteredFeatures As System.Collections.Generic.IEnumerable(Of Feature) = MyBase.FilterFeatures(oAllFeatures, oCanvas)
    Dim oReturnFeatures As New Collection(Of Feature)
    Dim oCanvasScreenExtent As RectangleShape = Nothing

    If SuppressPartialLabels Then
      '***** Get the screen extent
      'oCanvasScreenExtent = ConvertToScreenShape(New Feature(oCanvas.CurrentWorldExtent), oCanvas).GetBoundingBox()
      oCanvasScreenExtent = MapUtil.ToScreenCoordinate(New Feature(oCanvas.CurrentWorldExtent), oCanvas.CurrentWorldExtent, oCanvas.Width, oCanvas.Height)
    End If

    For Each oFeature As Feature In oUnfilteredFeatures
      'If Not oFeature.Intersects(oCanvas.CurrentWorldExtent.GetFeature) Then
      '  Continue For
      'End If
      oFeature.Tag = New Collection(Of LabelingCandidate)
      '***** Get the labeling candidates for this feature
      Dim oRawLabelingCandidates As Collection(Of LabelingCandidate) = GetLabelingCandidateCore(oFeature, Me.TextContent, oCanvas, Me.Font, 0, 0, 0)

      oRawLabelingCandidates = GetLabelingCandidates(oFeature, Me.TextContent, oCanvas, Me.Font, XOffsetInPixel, YOffsetInPixel, RotationAngle)

      Dim oLabelingCandidates As New Collection(Of CustomLabelingCandidate)
      Dim iLabelId As Integer = 1

      '***** Create a collecttion of CustomLabelingCandidates which save an Id
      '***** for each label
      For Each oLabelingCandidate As LabelingCandidate In oRawLabelingCandidates
        Dim sKey As String = msBaseKey + "_" + oFeature.Id + "_" + iLabelId.ToString
        If moLabelCoords.ContainsKey(sKey) Then
          Dim oPointFeature As New Feature(New PointShape(moLabelCoords(sKey).WorldPosition.X, moLabelCoords(sKey).WorldPosition.X), oFeature.ColumnValues)
          Dim o = GetLabelingCandidates(oPointFeature, Me.TextColumnName, oCanvas, Me.Font, 0, 0, 0)
          o(0).LabelInformation(0) = oLabelingCandidate.LabelInformation(0)
          oLabelingCandidates.Add(New CustomLabelingCandidate(o(0), iLabelId))
        Else
          oLabelingCandidates.Add(New CustomLabelingCandidate(oLabelingCandidate, iLabelId))
        End If
        iLabelId += 1
      Next oLabelingCandidate

      For Each oLabelingCandidate As CustomLabelingCandidate In oLabelingCandidates
        Dim sKey As String = msBaseKey + "_" + oFeature.Id + "_" + oLabelingCandidate.LabelingId.ToString
        If CheckDuplicate(oLabelingCandidate, oCanvas, labelsInThisLayer, labelsInAllLayers) Then
          '***** It would be a duplicate so don't add it
          Continue For
        End If

        'If Not moLabelCoords.ContainsKey(sKey) AndAlso CheckOverlapping(oLabelingCandidate, oCanvas, labelsInThisLayer, labelsInAllLayers) Then
        'Dim labelsInAllLayersIndex As New NetTopologySuite.Index.Quadtree.Quadtree(Of SimpleCandidate)
        'If CheckOverlapping(oLabelingCandidate, oCanvas, Me.Font, 0, 0, 0, labelsInAllLayersIndex) Then
        '  '***** It would overlap another label so don't add it.
        '  Continue For
        'End If

        Dim simpleCandidate As SimpleCandidate = New SimpleCandidate(oLabelingCandidate.OriginalText, EnvelopeFromExtent(oLabelingCandidate.ScreenArea.GetBoundingBox))

        If labelsInAllLayers IsNot Nothing Then
          '***** Didn't get thrown out of the loop, so add to the collection. The collection accumulates SimpleCandidates
          '***** that are used in future checks.
          labelsInAllLayers.Add(simpleCandidate)
        End If

        If labelsInThisLayer IsNot Nothing Then
          '***** Didn't get thrown out of the loop, so add to the collection. The collection accumulates SimpleCandidates
          '***** that are used in future checks.
          labelsInThisLayer.Add(simpleCandidate)
        End If

        If SuppressPartialLabels Then
          '***** We are suppressing partial labels so only add the feature if the 
          '***** labelling candidate fits inside the screen area.
          If Not oCanvasScreenExtent.Contains(oLabelingCandidate.ScreenArea) Then
            'oReturnFeatures.Add(oFeature)
            CType(oFeature.Tag, Collection(Of LabelingCandidate)).Add(oLabelingCandidate)
          End If
        Else
          '***** Not suppressing partial labels, so just add the feature.
          'oReturnFeatures.Add(oFeature)
          CType(oFeature.Tag, Collection(Of LabelingCandidate)).Add(oLabelingCandidate)
        End If
      Next oLabelingCandidate
      oReturnFeatures.Add(oFeature)
    Next oFeature

    Return oReturnFeatures
  End Function
  Private Function EnvelopeFromExtent(oExtent As RectangleShape) As NetTopologySuite.Geometries.Envelope
    Return New NetTopologySuite.Geometries.Envelope(oExtent.MinX, oExtent.MaxX, oExtent.MinY, oExtent.MaxY)
  End Function

End Class
