{-# LANGUAGE LambdaCase      #-}
{-# LANGUAGE MultiWayIf      #-}
{-# LANGUAGE RecordWildCards #-}
module Neovim.Plugin.ClassesSpec
    where

import           Neovim
import           Neovim.Plugin.Classes

import           Test.Hspec
import           Test.QuickCheck


newtype RandomCommandArguments = RCA { getRandomCommandArguments :: CommandArguments }
    deriving (Eq, Ord, Show, Read)


instance Arbitrary RandomCommandArguments where
    arbitrary = do
        bang <- arbitrary
        range <- arbitrary
        count <- arbitrary
        register <- fmap (fmap getNonEmpty) arbitrary
        return . RCA $ CommandArguments{..}


newtype RandomCommandOption = RCO { getRandomCommandOption :: CommandOption }
    deriving (Eq, Ord, Show, Read)


instance Arbitrary RandomCommandOption where
    arbitrary = do
        a <- choose (0,5) :: Gen Int
        o <- case a of
            -- XXX Most constructor arguments are not tested anyway, so they are
            --     hardcoded for now.
            0 -> CmdSync <$> elements [Sync, Async]
            1 -> return CmdRegister
            2 -> return $ CmdNargs ""
            3 -> CmdRange <$> elements [CurrentLine, WholeFile, RangeCount 1]
            4 -> CmdCount <$> arbitrary
            5 -> return CmdBang
        return $ RCO o


newtype RandomCommandOptions = RCOs { getRandomCommandOptions :: CommandOptions }
    deriving (Eq, Ord, Show, Read)


instance Arbitrary RandomCommandOptions where
    arbitrary = do
        l <- choose (0,20)
        (RCOs . mkCommandOptions . map getRandomCommandOption) <$> vectorOf l arbitrary


spec :: Spec
spec = do
  describe "Deserializing ans serializing" $ do
    it "should be id for CommandArguments" . property $ do
      \args -> (fromObject . toObject . getRandomCommandArguments) args
        `shouldBe` Right (getRandomCommandArguments args)

  describe "If a sync option is set for commands" $ do
    let isSyncOption = \case
          CmdSync _ -> True
          _         -> False
    it "must be at the head of the list" . property $ do
      \(RCOs opts) -> any isSyncOption (getCommandOptions opts) ==> do
               length (filter isSyncOption (getCommandOptions opts)) `shouldBe` 1
               head (getCommandOptions opts) `shouldSatisfy` isSyncOption

