{-# LANGUAGE TypeSynonymInstances #-}

module Read where

import qualified Data.ByteString.Char8 as C
import Data.ByteString.Lex.Fractional (readDecimal)
import qualified Vector as V
import Data.Array.Repa as R hiding (map)

type SparseVector a = [(a, Double)]
type Response = Double
type Sample a = (SparseVector a, Response)
type FullArray = Array U DIM1 Double
type SampleFull=(FullArray,Response)


-- | On error handling
-- Error handling is done by Maybe. There are pros and cons
-- of any approach: Maybe, Either, Excpetion. e.g. facing 
-- error, Maybe returns Nothing, which is un-informative about 
-- which part of which line has the wrong format. More ref:
-- http://book.realworldhaskell.org/read/error-handling.html
-- http://blog.ezyang.com/2011/08/8-ways-to-report-errors-in-haskell-revisited/

class Reads a where
  read :: C.ByteString -> Maybe [Sample a]
  read = sequence . map readSample . C.lines 

  readSample :: C.ByteString -> Maybe (Sample a)
  readSample str = fmap ((,)) feas <*> response
    where 
      tokens = C.words str
      feas = sequence . map readPair . tail $ tokens
      response = readDouble' . head $ tokens


  readPair :: C.ByteString -> Maybe (a, Double)
  readPair str = let xs = C.split ':' str
                    in if length xs /= 2 
                        then Nothing
                        else let (x:y:_) = xs
                              in fmap (,) (readId x) <*> readDouble' y
  
  readId :: C.ByteString -> Maybe a

-- | On TypeSynonymInstances
-- http://stackoverflow.com/a/2125769/1311956

readFull :: C.ByteString -> Maybe [SampleFull]
readFull = sequence . map readSampleFull . C.lines 

readSampleFull:: C.ByteString -> Maybe (SampleFull)
readSampleFull str = do
    let tokens = C.words str
    let feas = listToUnboxedArray . map readDouble' . tail $ tokens
    response <- readDouble' . head $ tokens
    return (feas,response)

listToUnboxedArray :: [Maybe Double] -> Array U DIM1 Double
listToUnboxedArray values = fromListUnboxed (Z :. length values) (map handleMaybe values)
  where
    handleMaybe :: Maybe Double -> Double
    handleMaybe (Just x) = x
    handleMaybe Nothing  = 0.0  -- Choose a default value for Nothing

instance Reads Int where
  readId = readInt'
instance Reads C.ByteString where
  readId = Just . id
  
readDouble' :: C.ByteString -> Maybe Double
readDouble' bs =
  case C.uncons bs of
    Just ('-', rest) -> fmap negate (fmap fst (readDecimal rest))
    _ -> fmap fst (readDecimal bs)

readInt' :: C.ByteString -> Maybe Int
readInt' str = case C.readInt str of 
                Nothing -> Nothing
                Just (x, y) -> if C.null y then Just x else Nothing

dimSample :: [Sample Int] -> Int
dimSample = foldl (\x y -> max x . V.dim $ y ) 0 . fst . unzip 









