-----------------------------------------------------------------------------
-- |
-- Module      :  Data.List
-- Copyright   :  (c) 2020 EMQ Technologies Co., Ltd.
-- License     :  BSD-style (see the LICENSE file)
--
-- Maintainer  :  Feng Lee, feng@emqx.io
--                Yang M, yangm@emqx.io
-- Stability   :  experimental
-- Portability :  portable
--
-- The List datatype.
--
-----------------------------------------------------------------------------
module Data.List where

import Data.Maybe (Maybe)
import Foreign (ffi1, ffi2, ffi3)

-----------------------------------------------------------------------------
-- Basic functions
-----------------------------------------------------------------------------

infixl 6 append as ++

append :: forall a. [a] -> [a] -> [a]
append = ffi2 :lists :append

-- infixr 6 cons as :

-- | Cons a list.
foreign import cons :: forall a. a -> [a] -> [a]

-- | Return the first element of a list.
foreign import head :: forall a. [a] -> a

-- | Return the last element of a list.
last :: forall a. [a] -> a
last = ffi1 :lists :last

-- | Return the elements after the head of a list.
foreign import tail :: forall a. [a] -> [a]

foreign import init :: forall a. [a] -> [a]

-- | Decompose a list into its head and tail.
foreign import uncons :: forall a. [a] -> Maybe (a, [a])

-- | Return the length/size of the list.
length :: forall a. [a] -> Integer
length = ffi1 :erlang :length

-- | Reverse the list.
reverse :: forall a. [a] -> [a]
reverse = ffi1 :lists :reverse

-- | Concat
concat :: forall a. [[a]] -> [a]
concat = ffi1 :lists :concat

filter :: forall a. (a -> Boolean) -> [a] -> [a]
filter = ffi2 :lists :filter

replicate :: forall a. Integer -> a -> [a]
replicate = ffi2 :lists :duplicate

-----------------------------------------------------------------------------
-- Build lists
-----------------------------------------------------------------------------

-- foreign import scanl :: forall a b. (b -> a -> b) -> b -> [a] -> [b]

-- foreign import scanr :: forall a b. (a -> b -> b) -> b -> [a] -> [b]

-----------------------------------------------------------------------------
-- Split and Sublists
-----------------------------------------------------------------------------

foreign import take :: forall a. Integer -> [a] -> [a]

takeWhile :: forall a. (a -> Boolean) -> [a] -> [a]
takeWhile = ffi2 :lists :takeWhile

foreign import drop :: forall a. Integer -> [a] -> [a]

-- | Extract a sublist by a start and end index.
foreign import slice :: forall a. Integer -> Integer -> [a] -> [a]

-- | An alias for `splitWith` function.
span :: forall a. (a -> Boolean) -> [a] -> ([a], [a])
span = splitWith

-- | Split List into two lists according to Pred.
splitWith :: forall a. (a -> Boolean) -> [a] -> ([a], [a])
splitWith = ffi2 :lists :splitwith

splitAt :: forall a. Integer -> [a] -> ([a], [a])
splitAt = ffi2 :lists :split

dropWhile :: forall a. (a -> Boolean) -> [a] -> [a]
dropWhile = ffi2 :lists :dropWhile

-----------------------------------------------------------------------------
-- Sort lists
-----------------------------------------------------------------------------

sort :: forall a. [a] -> [a]
sort = ffi1 :lists :sort

keysort :: forall a b. Integer -> [(a, b)] -> [(a, b)]
keysort = ffi2 :lists :keysort

usort :: forall a. [a] -> [a]
usort = ffi1 :lists :usort

ukeysort :: forall a b. Integer -> [(a, b)] -> [(a, b)]
ukeysort = ffi2 :lists :ukeysort

-----------------------------------------------------------------------------
-- Zip and unzip lists
-----------------------------------------------------------------------------

zip :: forall a b. [a] -> [b] -> [(a, b)]
zip = ffi2 :lists :zip

zip3 :: forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 = ffi3 :lists :zip3

foreign import zipWith :: forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]

foreign import zipWith3 :: forall a b c d. (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]

unzip :: forall a b. [(a, b)] -> ([a], [b])
unzip = ffi1 :lists :unzip

unzip3 :: forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 = ffi1 :lists :unzip3

member :: forall a. a -> [a] -> Boolean
member = ffi2 :lists :member
