{-
  Schema
-}

module Schema where

--import Char
import Control.Monad
import qualified Data.Map as M
import qualified Text.ParserCombinators.Parsec as P
--import Data.Either
import Data.Maybe
import Data.Monoid

import qualified Utils as U

type Column = String 
type Delimiter = Char

data Table  = Table {getName :: String, getPath :: FilePath, getDlmt :: Delimiter, getCols :: [Column]}
              deriving (Show)

{-
delimiterMap :: Map Char Char
delimiterMap = fromList 
               [('c',','),
                ('t','\t'),
                ('s',' '),
                ('l','~')]
-}

prettyPrintTables :: [Maybe Table] -> IO ()
prettyPrintTables [] = return ()
prettyPrintTables (Nothing :ts) = putStrLn "Not a table."
prettyPrintTables ((Just t):ts) = do 
  putStrLn $ (getName t) ++ " : " ++ (show.getCols$t)
  prettyPrintTables ts

type Schema = M.Map String Table

prettyPrintSchema :: Schema -> IO ()
prettyPrintSchema schema = prettyPrintTables $ map Just (M.elems schema)

{-
Schema file format:

<tablename>;<absolutefilepath>;<delimitercharacter>;<column_0>,<column_1>,...,<column_n-1>

tablename can be alphanumeric, but can't start with a digit
aboslutefilepath is self-explanitory
delimitercharacter is coded, t for tab, c for comma, s for space, l for tilde
finally, comma separated columns, with same rules as tablename
-}


tablename :: P.Parser String
tablename = U.object

abspath :: P.Parser FilePath
abspath = do
  root <- P.char '/'
  rest <- P.many (P.letter 
            P.<|> P.digit 
            P.<|> P.char '/' 
            P.<|> P.char '-' 
            P.<|> P.char '_' 
            P.<|> P.char '.')
  return (root:rest)
  P.<?> "absoulute file path"

delimiter :: P.Parser Delimiter
delimiter = P.anyChar

comment :: P.Parser ()
comment = do
  P.char '#'
  P.skipMany (P.noneOf "\n")
  P.<?> "comment"

eol :: P.Parser ()
eol = do
  P.oneOf "\n"
  return ()
  P.<?> "end of line"

item :: P.Parser (String,Table)
item = do
  tn <- tablename
  P.char ';'
  path <- abspath
  P.char ';'
  dlmt <- delimiter
  P.char ';'
  cols <- U.csv
  P.manyTill P.anyChar ( P.try eol P.<|> P.try comment P.<|> P.eof)
  return (tn,Table tn path dlmt cols)
  P.<?> "item"

line :: P.Parser (Maybe (String,Table))
line = do
  P.skipMany P.space
  P.try (comment >> return Nothing) P.<|> (item >>= return . Just)

fileParser :: P.Parser [(String,Table)]
fileParser = do
  lines <- P.many line
  return (catMaybes lines)

{-
readSchema1 :: FilePath -> IO (Either P.ParseError Schema)
readSchema1 schemaPath = 
    P.parseFromFile fileParser schemaPath >>= 
    return . fmap (foldr (uncurry M.insert) M.empty)
-}

readSchema :: FilePath -> IO Schema
readSchema schemaPath = do
  result <- P.parseFromFile fileParser schemaPath
  return $ case result of
             Left err -> M.empty
             Right ts -> foldr (uncurry M.insert) M.empty ts

{- test instances -}

sampleSchema = readSchema "/DBlimited/sampleschema.txt"
sampleTable = Table "person" "/DBlimited/sample/person.csv" ',' ["id","name","address","telephone","dob"]