content: import Control.Monad (forM_)
  import Data.List
  
  import Test.HUnit
  
  import qualified Portage.Dependency as P
  import qualified Portage.Dependency.Normalize as PN
  import qualified Portage.PackageId  as P
  import qualified Portage.Use        as P
  import qualified RunTests as RT
  
  tests :: Test
  tests = TestList [ TestLabel "normalize_in_use_and_top" test_normalize_in_use_and_top
                   ]
  
  pnm :: P.PackageName
  pnm = P.mkPackageName "dev-haskell" "mtl"
  pnp :: P.PackageName
  pnp = P.mkPackageName "dev-haskell" "parsec"
  pnq :: P.PackageName
  pnq = P.mkPackageName "dev-haskell" "quickcheck"
  
  def_attr :: P.DAttr
  def_attr = P.DAttr P.AnySlot []
  
  p_v :: [Int] -> P.Version
  p_v v = P.Version { P.versionNumber   = v
                    , P.versionChar     = Nothing
                    , P.versionSuffix   = []
                    , P.versionRevision = 0
                    }
  
  d_all :: [P.Dependency] -> P.Dependency
  d_all = P.DependAllOf
  d_any :: [P.Dependency] -> P.Dependency
  d_any = P.DependAnyOf
  
  d_ge :: P.PackageName -> [Int] -> P.Dependency
  d_ge pn v = P.DependAtom $ P.Atom pn
                     (P.DRange (P.NonstrictLB $ p_v v) P.InfinityB)
                     def_attr
  
  d_p :: String -> P.Dependency
  d_p pn = P.DependAtom $ P.Atom (P.mkPackageName "c" pn)
                  (P.DRange P.ZeroB P.InfinityB)
                  def_attr
  
  d_use :: String -> P.Dependency -> P.Dependency
  d_use u d = P.mkUseDependency (True, P.Use u) d
  
  d_nuse :: String -> P.Dependency -> P.Dependency
  d_nuse u d = P.mkUseDependency (False, P.Use u) d
  
  test_normalize_in_use_and_top :: Test
  test_normalize_in_use_and_top = TestCase $ do
      let deps  = [ ( d_all [ d_ge pnm [1,0]
                            , d_use "foo" (d_all [ d_ge pnm [1,0] -- duplicate
                                                 , d_ge pnp [2,1]
                                                 ])
                            ]
                    , [ ">=dev-haskell/mtl-1.0"
                      , "foo? ( >=dev-haskell/parsec-2.1 )"
                      ]
                    )
                  , ( d_all [ d_ge pnm [1,0]
                            , d_use "foo" (d_any [ d_ge pnm [1,0] -- already satisfied
                                                 , d_ge pnp [2,1]
                                                 ])
                            ]
                    , [ ">=dev-haskell/mtl-1.0" ]
                    )
                  , ( d_any [ d_all [ d_ge pnm [1,0] -- common subdep
                                    , d_ge pnq [1,2]
                                    ]
                            , d_all [ d_ge pnm [1,0]
                                    , d_ge pnp [3,1]
                                    ]
                            ]
                    , [ ">=dev-haskell/mtl-1.0"
                      , "|| ( >=dev-haskell/parsec-3.1"
                      , "     >=dev-haskell/quickcheck-1.2 )"
                      ]
                    )
                  , ( d_all [ d_use "foo" $ d_use "bar" $ d_ge pnm [1,0]
                            , d_use "bar" $ d_use "foo" $ d_ge pnm [1,0]
                            ]
                    , [ "bar? ( foo? ( >=dev-haskell/mtl-1.0 ) )" ]
                    )
                  , ( d_all [ d_use "foo" $ d_ge pnm [1,0]
                            , d_use "foo" $ d_ge pnp [3,1]
                            ]
                    , [ "foo? ( >=dev-haskell/mtl-1.0"
                      , "       >=dev-haskell/parsec-3.1 )"
                      ]
                    )
                  , ( d_all [ d_use  "a" $ d_use "b" $ d_ge pnm [1,0]
                            , d_nuse "a" $ d_use "b" $ d_ge pnm [1,0]
                            ]
                    , [ "b? ( >=dev-haskell/mtl-1.0 )"
                      ]
                    )
                  -- 'test?' is special
                  , ( d_use "a" $ d_use "test" $ d_ge pnm [1,0]
                    , [ "test? ( a? ( >=dev-haskell/mtl-1.0 ) )"
                      ]
                    )
                  , -- pop context for complementary depends, like
                    --   a? ( x y a ) !a? ( x y na )
                    -- leads to
                    --   x y a? ( a ) !a? ( na )
                    ( d_all [ d_use  "a" $ d_all $ map d_p [ "x", "y", "a" ]
                            , d_nuse "a" $ d_all $ map d_p [ "x", "y", "na" ]
                            ]
                    , [ "c/x"
                      , "c/y"
                      , "a? ( c/a )"
                      , "!a? ( c/na )"
                      ]
                    )
                  , -- push stricter context into less strict
                    ( d_all [ d_ge pnm [2,0]
                            , d_use "a" $ d_ge pnm [1,0]
                            ]
                    ,
                      [ ">=dev-haskell/mtl-2.0" ]
                    )
                  , -- propagate use guarded depend into deeply nested one
                    ( d_all [ d_use  "a"    $             d_all $ map d_p [ "x" ]
                            , d_use  "test" $ d_use "a" $ d_all $ map d_p [ "x", "t" ]
                            ]
                    , [ "test? ( a? ( c/t ) )"
                      , "a? ( c/x )"
                      ]
                    )
                  , -- lift nested use context for complementary depends
                    --   a? ( b? ( y ) ) b? ( x )
                    ( d_all [ d_use  "a" $ d_use "b" $ d_all $ map d_p [ "x", "y" ]
                            , d_nuse "a" $ d_use "b" $ d_p "x"
                            ]
                    , [ "a? ( b? ( c/y ) )"
                      , "b? ( c/x )"
                      ]
                    )
                  , -- more advanced lift of complementary deps
                    -- a? ( b? ( x y ) )
                    -- !a? ( b? ( y z ) )
                    ( d_all [ d_use   "a" $ d_use "b" $ d_all $ map d_p [ "x", "y" ]
                            , d_nuse  "a" $ d_use "b" $ d_all $ map d_p [ "y", "z" ]
                            ]
                    , [ "a? ( b? ( c/x ) )"
                      , "!a? ( b? ( c/z ) )"
                      , "b? ( c/y )"
                      ]
                    )
                  , -- completely expanded set of USEs
                    -- a? ( b? ( c? ( x y z ) )
                    -- a? ( b? ( !c? ( x y ) )
                    -- a? ( !b? ( c? ( x z ) )
                    -- a? ( !b? ( !c? ( x ) )
                    --
                    -- !a? ( b? ( c? ( y z ) )
                    -- !a? ( b? ( !c? ( y ) )
                    -- !a? ( !b? ( c? ( z ) )
                    -- !a? ( !b? ( !c? ( ) )
                    ( d_all [ d_use   "a" $ d_use  "b" $ d_use  "c" $ d_all $ map d_p [ "x", "y", "z" ]
                            , d_use   "a" $ d_use  "b" $ d_nuse "c" $ d_all $ map d_p [ "x", "y" ]
                            , d_use   "a" $ d_nuse "b" $ d_use  "c" $ d_all $ map d_p [ "x", "z" ]
                            , d_use   "a" $ d_nuse "b" $ d_nuse "c" $ d_all $ map d_p [ "x" ]
                            , d_nuse  "a" $ d_use  "b" $ d_use  "c" $ d_all $ map d_p [ "y", "z" ]
                            , d_nuse  "a" $ d_use  "b" $ d_nuse "c" $ d_all $ map d_p [ "y" ]
                            , d_nuse  "a" $ d_nuse "b" $ d_use  "c" $ d_all $ map d_p [ "z" ]
                            , d_nuse  "a" $ d_nuse "b" $ d_nuse "c" $ d_all $ map d_p [ ]
                            ]
                    , [ "a? ( c/x )"
                      , "b? ( c/y )"
                      , "c? ( c/z )"
                      ]
                    )
                  , -- pop simple common subdepend
                    -- a?  ( b? ( d ) )
                    -- !a? ( b? ( d ) )
                    ( d_all [ d_use   "a" $ d_use  "b" $ d_p "d"
                            , d_nuse  "a" $ d_use  "b" $ d_p "d"
                            ]
                    , [ "b? ( c/d )"
                      ]
                    )
                  , -- pop '|| ( some thing )' depend
                    ( let any_part = d_any $ map d_p ["a", "b"] in
                      d_all [ d_use  "u" $ d_all [ any_part , d_p "z"]
                            , d_nuse "u" $         any_part
                            ]
                    , [ "|| ( c/a"
                      , "     c/b )"
                      , "u? ( c/z )"
                      ]
                    )
                  , -- simplify slightly more complex counterguard
                    --   v? ( c/d )
                    --   u? ( !v? ( c/d ) )
                    -- to
                    --   v? ( c/d )
                    --   u? ( c/d )
                    ( d_all [               d_use  "v" $ d_p "d"
                            , d_use   "u" $ d_nuse "v" $ d_p "d"
                            ]
                    , [ "u? ( c/d )"
                      , "v? ( c/d )"
                      ]
                    )
  
                  , --   ffi? ( c/d c/e )
                    --   !ffi ( !gmp ( c/d c/e ) )
                    --   gmp? ( c/d c/e )
                    -- to
                    --   ( c/d c/e )
                    ( let de = d_all [ d_p "d" , d_p "e" ]
                      in d_all [ d_use "ffi" de
                               , d_nuse "ffi" $ d_nuse "gmp" $ de
                               , d_use  "gmp" $ de
                               ]
                    , [ "c/d"
                      , "c/e"
                      ]
                    )
  
                  {- TODO: another popular case
                  , -- simplify even more complex counterguard
                    --   u? ( c/d )
                    --   !u? ( v? ( c/d ) )
                    -- to
                    --   u? ( c/d )
                    --   v? ( c/d )
                    ( d_all [               d_use "u" $ d_p "d"
                            , d_nuse  "u" $ d_use "v" $ d_p "d"
                            ]
                    , [ "u? ( c/d )"
                      , "v? ( c/d )"
                      ]
                    )
                   -}
                  ]
      forM_ deps $ \(d, expected) ->
          let actual = P.dep2str 0 $ PN.normalize_depend d
          in assertEqual ("expecting matching result for " ++ show d)
                         (intercalate "\n" expected)
                         actual
  
  main :: IO ()
  main = RT.run_tests tests
  