module Data.Geometry.PlanarSubdivision.Merge( merge
                                            , mergeWith
                                            , mergeAllWith
                                            , embedAsHoleIn
                                            , embedAsHolesIn
                                            ) where
import           Algorithms.DivideAndConquer
import           Control.Lens hiding (holes)
import           Data.Ext
import           Data.Geometry.PlanarSubdivision.Basic
import           Data.Geometry.PlanarSubdivision.Raw
import           Data.Geometry.Point
import           Data.Geometry.Polygon
import           Data.PlanarGraph.Dart
import           Data.PlaneGraph ( Dart, VertexId(..), FaceId(..)
                                , VertexId', FaceId'
                                )
import qualified Data.PlaneGraph as PG
import           Data.Semigroup.Foldable
import qualified Data.Vector as V
import           Unsafe.Coerce (unsafeCoerce)
embedAsHolesIn      :: forall t s h v e f r. (Foldable1 t, Functor t)
                    => t (PlanarSubdivision h v e f r) 
                    -> (t f -> f -> f) 
                    -> FaceId' s 
                    -> PlanarSubdivision s v e f r 
                    -> PlanarSubdivision s v e f r
embedAsHolesIn hs f = embedAsHoleIn ph' g
  where
    
    ph' = mergeAllWith const hs
    
    g _ = f (fmap (\h -> h^.dataOf (outerFaceId h)) hs)
embedAsHoleIn           :: forall s h v e f r.
                           PlanarSubdivision h v e f r 
                        -> (f -> f -> f) 
                        -> FaceId' s 
                        -> PlanarSubdivision s v e f r 
                        -> PlanarSubdivision s v e f r
embedAsHoleIn ph' f i ps = mergeWith' mergeFaces ps ph
  where
    
    ph :: PlanarSubdivision s v e f r
    ph = unsafeCoerce ph'
      
      
      
    mergeFaces fs1 fs2 = update fs1 i (V.head fs2) <> V.tail fs2
    update fs (FaceId (VertexId j)) h2 = let FaceData hs' x' = h2^.faceDataVal
                                             g (FaceData hs x) = FaceData (hs' <> hs) (f x' x)
                                         in fs&ix j.faceDataVal %~ g
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
mergeAllWith   :: Foldable1 t
               => (f -> f -> f)
               -> t (PlanarSubdivision s v e f r)
               -> PlanarSubdivision s v e f r
mergeAllWith f = divideAndConquer1With (mergeWith f) id . toNonEmpty
merge :: PlanarSubdivision s v e f r -> PlanarSubdivision s v e f r -> PlanarSubdivision s v e f r
merge = mergeWith const
mergeWith   :: (f -> f -> f) 
            -> PlanarSubdivision s v e f r
            -> PlanarSubdivision s v e f r
            -> PlanarSubdivision s v e f r
mergeWith f = mergeWith' (mergeFaceData f)
mergeWith'  :: (V.Vector (RawFace s f) -> V.Vector (RawFace s f) -> V.Vector (RawFace s f))
             
            -> PlanarSubdivision s v e f r
            -> PlanarSubdivision s v e f r
            -> PlanarSubdivision s v e f r
mergeWith' mergeFaces p1 p2 = PlanarSubdivision cs vd rd rf
  where
    
    p2' = shift (numComponents p1) (numVertices p1) (numDarts p1 `div` 2) (numFaces p1 - 1) p2
        
        
        
        
        
    cs = p1^.components <> p2'^.components
    vd = p1^.rawVertexData <> p2'^.rawVertexData
    rd = p1^.rawDartData <> p2'^.rawDartData
    rf = (p1^.rawFaceData) `mergeFaces` (p2'^.rawFaceData)
mergeFaceData           :: (f -> f -> f)
                        -> V.Vector (RawFace s f)
                        -> V.Vector (RawFace s f)
                        -> V.Vector (RawFace s f)
mergeFaceData f vs1 vs2 = V.cons h ts
  where
    ts = V.tail vs1 <> V.tail vs2
    h  = let FaceData hs1 x1 = vs1^.to V.head.faceDataVal
             FaceData hs2 x2 = vs2^.to V.head.faceDataVal
         in RawFace Nothing $ FaceData (hs1 <> hs2) (f x1 x2)
shift                                             :: forall s v e f r.
                                                     Int -> Int -> Int -> Int
                                                  -> PlanarSubdivision s v e f r
                                                  -> PlanarSubdivision s v e f r
shift nc nv nd nf (PlanarSubdivision cs vd rd rf) = PlanarSubdivision cs' vd' rd' rf'
  where
    cs' = (\pg -> pg&PG.vertexData.traverse  %~ incV
                    &PG.rawDartData.traverse %~ incD
                    &PG.faceData.traverse    %~ incFi
          ) <$> cs
    vd' = (\(Raw ci i x)      -> Raw (incC ci) i x)                    <$> vd
    rd' = (\(Raw ci i x)      -> Raw (incC ci) i x)                    <$> rd
    rf' = (\(RawFace fidx fd) -> RawFace (incFIdx <$> fidx) (incF fd)) <$> rf
    incC                 :: ComponentId s -> ComponentId s
    incC (ComponentId i) = ComponentId $ i + nc
    incV              :: VertexId' s -> VertexId' s
    incV (VertexId i) = VertexId $ i + nv
    incD                  :: Dart s -> Dart s
    incD (Dart (Arc a) p) = Dart (Arc $ a + nd) p
    incFIdx (ci,fi) = (incC ci, fi)
      
      
    incF                 :: FaceData (Dart s) f -> FaceData (Dart s) f
    incF (FaceData hs f) = FaceData (incD <$> hs) f
    incFi                       :: FaceId' s -> FaceId' s
    incFi (FaceId (VertexId i)) = FaceId . VertexId $ i + nf
data Test = Test
data Id a = Id a
triangle1 :: PlanarSubdivision Test () () Int Rational
triangle1 = (\pg -> fromSimplePolygon (Id Test) pg 1 0)
          $ trianglePG1
trianglePG1 = fromPoints . map ext $ [origin, Point2 200 0, Point2 200 200]
triangle2 :: PlanarSubdivision Test () () Int Rational
triangle2 = (\pg -> fromSimplePolygon (Id Test) pg 2 0)
          $ trianglePG2
trianglePG2 = fromPoints . map ext $ [Point2 0 30, Point2 10 30, Point2 10 40]
triangle4 :: PlanarSubdivision Test () () Int Rational
triangle4 = (\pg -> fromSimplePolygon (Id Test) pg 1 0)
          $ trianglePG4
trianglePG4 = fromPoints . map ext $ [Point2 400 400, Point2 600 400, Point2 600 600]
triangle3 :: PlanarSubdivision Test () () Int Rational
triangle3 = (\pg -> fromSimplePolygon (Id Test) pg 3 0)
          $ trianglePG3
trianglePG3 = fromPoints . map ext $ [Point2 401 530, Point2 410 530, Point2 410 540]
myPS = embedAsHoleIn triangle2 const (mkFI 1) triangle1
       `merge`
       embedAsHoleIn triangle3 const (mkFI 1) triangle4
mkFI :: Int -> FaceId' Test
mkFI  = FaceId . VertexId