{-|
Module      : Data.List.Scroll
Description : Reordering an item within a list
Copyright   : (c) Felpe Santa-Cruz, 2021
License     : BSD3
Maintainer  : fesanmar@gmail.com
Stability   : stable
Portability : POSIX

This module provides functions for
relocate an item within a list.
-}
module Data.List.Scroll
    ( up
    , down
    , deleteByIndex
    ) where

import Data.Tuple.Extra ( second )

{-|
  The 'up' function moves an element 'n' positions to 
  the beginning of a list. If the index provided is out 
  of range, the list is returned without any modification.
  On the other hand, extra steps will be ignored.

  Some examples are given below:

  >>> up 2 2 ["one", "two", "three"]
  ["three", "one", "two"]

  >>> up 4 1 ["one", "two", "three"]
  ["one", "two", "three"]

  >>> up 2 3 ["one", "two", "three"]
  ["three", "one", "two"]
-}
up :: Int -> Int -> [a] -> [a]
up :: Int -> Int -> [a] -> [a]
up Int
index Int
steps [a]
ls
  | (Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0) [Int
steps, Int
index, [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
ls Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
index] = [a]
ls
  | Bool
otherwise = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
prev [a]
ls [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ ([a]
ls [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
index) a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
tailNoTarget
  where prev :: Int
prev = Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
steps
        tailNoTarget :: [a]
tailNoTarget = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop Int
prev ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
deleteByIndex Int
index [a]
ls

{-|
  The 'down' function moves an element 'n' positions to 
  the end of a list. If the index provided is out of range,
  the list is returned without any modification. On the other 
  hand, extra steps will be ignored.

  Some examples are given below:

  >>> down 0 1 ["one", "two", "three"]
  ["two", "one", "three"]

  >>> down 4 1 ["one", "two", "three"]
  ["one", "two", "three"]

  >>> down 0 4 ["one", "two", "three"]
  ["two", "three", "one"]
-}
down :: Int -> Int -> [a] -> [a]
down :: Int -> Int -> [a] -> [a]
down Int
index Int
steps [a]
ls = [a] -> [a]
forall a. [a] -> [a]
reverse ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> [a] -> [a]
forall a. Int -> Int -> [a] -> [a]
up Int
reverseIndex Int
steps ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ [a] -> [a]
forall a. [a] -> [a]
reverse [a]
ls
  where reverseIndex :: Int
reverseIndex = [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
ls Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1

{-|
  The 'deleteByIndex' function removes an element
  from a list by its position within it. For example,

  >>> deleteByIndex 1 ["one", "two", "three"]
  ["one", "three"]

  If index is out of range, the list will be returned
  with no changes. For example:

  >>> deleteByIndex 3 ["one", "two", "three"]
  ["one", "two", "three"]
-}
deleteByIndex :: Int -> [a] -> [a]
deleteByIndex :: Int -> [a] -> [a]
deleteByIndex Int
index [a]
ls
  | Int
index Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = [a]
ls
  | Bool
otherwise = ([a] -> [a] -> [a]) -> ([a], [a]) -> [a]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
(++) (([a], [a]) -> [a])
-> (([a], [a]) -> ([a], [a])) -> ([a], [a]) -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> [a]) -> ([a], [a]) -> ([a], [a])
forall b b' a. (b -> b') -> (a, b) -> (a, b')
second (Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop Int
1) (([a], [a]) -> [a]) -> ([a], [a]) -> [a]
forall a b. (a -> b) -> a -> b
$ Int -> [a] -> ([a], [a])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
index [a]
ls