{-|
Module      : HNum.CSV
Description : Simple CSV Library for HNumeric
CopyRight   : (c) Tae Geun Kim, 2018
License     : BSD3
Maintainer  : edeftg@gmail.com
Stability   : Experimental
-}
module HNum.CSV where

import           HNum.Vector

-----------------------------------------------
-- Declaration
-----------------------------------------------
-- | Type Aliases for convenience
type Header = [String]

-- | DataFrame structure to write csv
data DataFrame a = DataFrame { header :: Header
                             , dat :: Matrix a
                             } deriving (Show, Eq)

-- | dataframe constructor
dataframe :: Header -> Matrix a -> DataFrame a
dataframe h m | length h == row m = DataFrame h m
              | otherwise         = error "Length of Header != Length of Data"
  where n = length m `div` length h

-- | dataframe from vectors
fromVectors :: Header -> [Vector a] -> DataFrame a
fromVectors h vs = dataframe h (matrix vs') where vs' = map toList vs

-- | Extract Vector in DataFrame
(#) :: Eq a => DataFrame a -> String -> Vector a
df # hd | hd `notElem` header df = error "No specific header in dataFrame"
        | otherwise              = vec $ bd !! i
 where
  i  = hd `fd` header df
  bd = matForm $ transpose $ dat df

instance Functor DataFrame where
  fmap f df = df { dat = fmap f (dat df) }

instance Convertable DataFrame where
  readInt df = read <$> df :: DataFrame Int
  readInteger df = read <$> df :: DataFrame Integer
  readDouble df = read <$> df :: DataFrame Double

-----------------------------------------------
-- Write to CSV
-----------------------------------------------
-- | Class to write csv file
class Functor f => Writable f where
  -- | Object to String (Different to Show)
  toString :: Show a => f a -> String
  -- | Write as CSV
  writeCSV :: Show a => String -> f a -> IO ()

instance Writable Vector where
  toString v = foldr (\x y -> x ++ "\n" ++ y) "" (show <$> v)
  writeCSV title v = writeFile title (toString v)

instance Writable Matrix where
  toString m = foldr ((\x y -> x ++ "\n" ++ y) . cm) "" m1
    where m1 = matForm (show <$> m)
  writeCSV title m = writeFile title (toString m)

instance Writable DataFrame where
  toString (DataFrame h m) = h' ++ "\n" ++ m'
    where h' = cm h
          m' = toString (transpose m)
  writeCSV title df = writeFile title (toString df)

-----------------------------------------------
-- Read from CSV
-----------------------------------------------
-- | From CSV to DataFrame
readCSV :: String -> IO (DataFrame String)
readCSV filepath = do
  r <- readFile filepath
  let dat = splitWith '\n' (rmQuot r)
      hd  = splitWith ',' (head dat)
      bd  = map (splitWith ',') (tail dat)
  return DataFrame {header = hd, dat = matrix bd}

-----------------------------------------------
-- Backend Function
-----------------------------------------------
-- | For Convenient 
cm :: [String] -> String
cm [] = []
cm (x : xs) | null xs   = x
            | otherwise = x ++ "," ++ cm xs

-- | Split With Seperator
splitWith :: Char -> String -> [String]
splitWith _ [] = []
splitWith sep str | null temp'' = [temp]
                  | otherwise   = temp : splitWith sep temp'
 where
  temp   = takeWhile (/= sep) str
  temp'' = dropWhile (/= sep) str
  temp'  = tail temp''

-- | Remove Quotation Symbol
rmQuot :: String -> String
rmQuot [] = []
rmQuot x | null temp = clean
         | otherwise = clean ++ rmQuot clean'
 where
  clean  = takeWhile (/= '"') x
  temp   = dropWhile (/= '"') x
  clean' = tail temp