-- Implicit CAD. Copyright (C) 2012, Christopher Olah (chris@colah.ca)
-- Released under the GNU GPL, see LICENSE

module Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs) where

import Graphics.Implicit.Definitions
import Graphics.Implicit.Export.Render.Definitions
import GHC.Exts (groupWith)
import Data.List (sortBy)
import Data.VectorSpace 

cleanLoopsFromSegs :: [Polyline] -> [Polyline]
cleanLoopsFromSegs =
	map reducePolyline
	. joinSegs
	. filter polylineNotNull


joinSegs :: [Polyline] -> [Polyline]
joinSegs [] = []
joinSegs (present:remaining) =
	let
		findNext ((p3:ps):segs) = if p3 == last present then (Just (p3:ps), segs) else
			if last ps == last present then (Just (reverse $ p3:ps), segs) else
			case findNext segs of (res1,res2) -> (res1,(p3:ps):res2)
		findNext [] = (Nothing, [])
	in
		case findNext remaining of
			(Nothing, _) -> present:(joinSegs remaining)
			(Just match, others) -> joinSegs $ (present ++ tail match): others

reducePolyline ((x1,y1):(x2,y2):(x3,y3):others) = 
	if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):(x3,y3):others) else
	if abs ( (y2-y1)/(x2-x1) - (y3-y1)/(x3-x1) ) < 0.0001 
	   || ( (x2-x1) == 0 && (x3-x1) == 0 && (y2-y1)*(y3-y1) > 0)
	then reducePolyline ((x1,y1):(x3,y3):others)
	else (x1,y1) : reducePolyline ((x2,y2):(x3,y3):others)
reducePolyline ((x1,y1):(x2,y2):others) = 
	if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):others) else (x1,y1):(x2,y2):others
reducePolyline l = l

polylineNotNull (a:l) = not (null l)
polylineNotNull [] = False



{-cleanLoopsFromSegs = 
	connectPolys
	-- . joinSegs
	. filter (not . degeneratePoly)
		
polylinesFromSegsOnGrid = undefined

degeneratePoly [] = True
degeneratePoly [a,b] = a == b
degeneratePoly _ = False

data SegOrPoly = Seg (ℝ2) ℝ ℝ2 -- Basis, shift, interval
               | Poly [ℝ2]

isSeg (Seg _ _ _) = True
isSeg _ = False

toSegOrPoly :: Polyline -> SegOrPoly
toSegOrPoly [a, b] = Seg v (a⋅vp) (a⋅v, b⋅v)
	where
		v@(va, vb) = normalized (b ^-^ a)
		vp = (-vb, va)
toSegOrPoly ps = Poly ps

fromSegOrPoly :: SegOrPoly -> Polyline
fromSegOrPoly (Seg v@(va,vb) s (a,b)) = [a*^v ^+^ t, b*^v ^+^ t]
	where t = s*^(-vb, va)
fromSegOrPoly (Poly ps) = ps

joinSegs :: [Polyline] -> [Polyline]
joinSegs = map fromSegOrPoly . joinSegs' . map toSegOrPoly

joinSegs' :: [SegOrPoly] -> [SegOrPoly]
joinSegs' segsOrPolys = polys ++ concat (map joinAligned aligned) where
	polys = filter (not.isSeg) segsOrPolys
	segs  = filter isSeg segsOrPolys
	aligned = groupWith (\(Seg basis p _) -> (basis,p)) segs

joinAligned segs@((Seg b z _):_) = mergeAdjacent orderedSegs where
	orderedSegs = sortBy (\(Seg _ _ (a1,_)) (Seg _ _ (b1,_)) -> compare a1 b1) segs
	mergeAdjacent (pres@(Seg _ _ (x1a,x2a)) : next@(Seg _ _ (x1b,x2b)) : others) =
		if x2a == x1b
		then mergeAdjacent ((Seg b z (x1a,x2b)): others)
		else pres : mergeAdjacent (next : others)
	mergeAdjacent a = a
joinAligned [] = []

connectPolys :: [Polyline] -> [Polyline]
connectPolys [] = []
connectPolys (present:remaining) =
	let
		findNext (ps@(p:_):segs) = 
			if p == last present
			then (Just ps, segs)
			else (a, ps:b) where (a,b) =  findNext segs
		findNext [] = (Nothing, [])
	in
		case findNext remaining of
			(Nothing, _) -> present:(connectPolys remaining)
			(Just match, others) -> connectPolys $ (present ++ tail match): others

-}