module LineHelpers exposing (..)

-- Maybe should just be doubles or nums

import Debug

fromJust : Maybe a -> a

fromJust x = case x of

Just y -> y

Nothing -> Debug.crash "error: fromJust Nothing"

toPointString : List (number, number) -> String

toPointString xs =

case xs of

(x,y) :: ys -> (toString x) ++ "," ++ (toString y) ++ " " ++ (toPointString ys)

_ -> ""

crossProd : (number,number,number) -> (number,number,number) -> (number,number,number)

crossProd (a,b,c) (d,e,f) = (b * f - c * e, c * d - a * f, a * e - b * d)

type alias PointListH number = List (number,number,number)

type alias LineListH number = List (number,number,number)

-- gives the mapping function the list and the list shifted by 1

neighbormap f a = let a_ = fromJust (List.tail a) in List.map2 f a a_

crossNeighbor = neighbormap crossProd

norm a b = sqrt (a * a + b * b)

shiftLine delta (a,b,c) = (a,b, (norm a b) * delta + c)

connectingLines = crossNeighbor

shiftLines delta = List.map (shiftLine delta)

intersections = crossNeighbor

-- nearly striaght lines will find their intersection at infinity.

-- maybe filter out a lower threshold on c

-- keep first and last point

last xs = let l = List.length xs in fromJust (List.head (List.drop (l - 1) xs))

timestep : Float -> List (Float,Float, Float) -> List (Float,Float, Float)

timestep delta points = let

firstpoint = fromJust (List.head points)

lastpoint = last points

connectlines = connectingLines points

newlines = shiftLines delta connectlines

newpoints = intersections newlines

filterednewpoints = List.filter (\(a,b,c) -> (abs c) > 0.01) newpoints

normpoints = List.map normalize filterednewpoints

result = firstpoint :: (normpoints ++ [lastpoint])

resample = List.map (maxfunc (List.map dehomogenize result)) initx

--result2 = removeoutoforder (-100000, 0,00) result

in List.map homogenize (zip initx resample)

homogenize (a,b) = (a,b,1)

dehomogenize (a,b,c) = (a / c, b / c)

normalize = dehomogenize >> homogenize

zip = List.map2 (,)

initx = List.map (toFloat >>((*) 4.5)) (List.range -200 400)

--inity = List.map (\x -> x * x / 50) initx

--inity = List.map (\x -> 300 + x * x / -50) initx

--inity = List.map (\x -> 25 * sin (x / 20) + 250) initx

inity = List.map (\x -> 25 * sin (x / 20) + 250 + 15 * sin (x/13)) initx

initxy = zip initx inity

initxyh = List.map homogenize initxy

iterate n f x = if n == 0 then [] else (f x) :: iterate (n - 1) f (f x)

paths = (List.map << List.map) dehomogenize (iterate 60 (timestep 5.0) initxyh)

colors = List.concat (List.repeat (List.length paths) ["red", "blue", "yellow"] )

removeoutoforder prev xs = case xs of

y :: ys -> if prev < y then (y :: removeoutoforder y ys) else removeoutoforder prev ys

_ -> []

neighborzip a = let a_ = fromJust (List.tail a) in zip a a_

linearinterp x ((x1,y1), (x2,y2)) = (y1 * (x2 - x) + y2 * (x - x1)) / (x2 - x1)

maxfunc : List (Float, Float) -> Float -> Float

maxfunc points x = let

pairs = neighborzip points

filterfunc ((x1,y1), (x2,y2)) = (xor (x < x1) (x < x2))

candidates = List.filter filterfunc pairs

yvals = List.map (linearinterp x) candidates in Maybe.withDefault 100 (List.maximum yvals)