{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Algorithms.Geometry.LowerEnvelope.LowerEnvSpec where

import qualified Algorithms.Geometry.LowerEnvelope.DualCH as DualCH
import           Control.Lens
import           Data.Eq.Approximate
import           Data.Ext
import           Data.Geometry
import           Data.Geometry.Ipe
import           Data.Geometry.Line
import           Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import           Data.Maybe (mapMaybe)
import           Data.Proxy
import           Data.Ratio
import           Data.Semigroup
import           Data.Vinyl.CoRec
import           GHC.TypeLits
import           Test.Hspec
import           Util

-- import Algorithms.Geometry.LowerEnvelope.Types

spec :: Spec
spec = testCases "test/Algorithms/Geometry/LowerEnvelope/manual.ipe"

testCases    :: FilePath -> Spec
testCases fp = (runIO $ readInput fp) >>= \case
    Left e    -> it "reading Smallest enclosing disk file" $
                   expectationFailure $ "Failed to read ipe file " ++ show e
    Right tcs -> mapM_ toSpec tcs


type Approx r = AbsolutelyApproximateValue (Proxy 3) r



data TestCase r = TestCase { _lines    :: NonEmpty (Line 2 r :+ ())
                           , _color    :: Maybe (IpeColor r)
                           , _solution :: [Point 2 (Approx r)]
                           }
                  deriving (Show,Eq)


readInput    :: FilePath -> IO (Either ConversionError [TestCase Rational])
readInput fp = fmap f <$> readSinglePageFile fp
  where
    f page = [ let c = lookup' $ NonEmpty.head lSet
               in TestCase ((\l -> l^.core.to supportingLine :+ ()) <$> lSet)
                           c
                           (solutionOf c)
             | lSet <- mapMaybe NonEmpty.nonEmpty $ byStrokeColour segs
             ]
      where
        segs :: [LineSegment 2 () Rational :+ IpeAttributes Path Rational]
        segs = page^..content.traverse._withAttrs _IpePath _asLineSegment
        pts  = page^..content.traverse._IpeUse

        solutionOf c = [ AbsolutelyApproximateValue <$> p^.core.symbolPoint
                       | p <- pts, lookup' p == c
                       ]



lookup' (_ :+ ats) = lookupAttr (Proxy :: Proxy Stroke) ats


toSpec                    :: (Fractional r, Ord r, Show r) => TestCase r -> Spec
toSpec (TestCase ls c sol) = it ("testing the " <> show c <> " set") $
  (map (approx . (^.core))
   . DualCH.vertices . DualCH.lowerEnvelope $ ls) `shouldBe` sol


approx = fmap AbsolutelyApproximateValue

-- shouldApprox       :: forall f r. ( Functor f, Eq (f (Approx r))
--                                   , Show (f (Approx r))
--                                   )
--                    => f r -> f r -> Expectation
-- a `shouldApprox` b = a' `shouldBe` b'
--   where
--     a' :: f (Approx r)
--     a' = AbsolutelyApproximateValue <$> a
--     b' :: f (Approx r)
--     b' = AbsolutelyApproximateValue <$> b



instance KnownNat n => AbsoluteTolerance (Proxy n) where
  absoluteToleranceOf = toleranceFromKnownNat . getAbsoluteTolerance

toleranceFromKnownNat   :: (Fractional r, KnownNat n) => proxy n -> r
toleranceFromKnownNat p = recip . fromInteger $ (10 :: Integer) ^ (natVal p)

instance KnownNat n => RelativeTolerance (Proxy n) where
  relativeToleranceOf = toleranceFromKnownNat . getRelativeTolerance

instance KnownNat n => ZeroTolerance (Proxy n) where
  zeroToleranceOf = toleranceFromKnownNat . getZeroTolerance

--     where
--       f   :: Fractional r => AbsolutelyApproximateValue (Proxy n) r -> r
--       f _ = recip $ (fromInteger 10) ^^ (natVal (Proxy :: Proxy n))