{-# LANGUAGE OverloadedStrings #-}
module Text.TOML where

import qualified Data.Attoparsec.ByteString.Char8 as A
import qualified Data.ByteString.Char8 as B
import Data.List ( foldl', groupBy )
import Data.Either ( rights )
import Data.Map ( Map )
import qualified Data.Map as M

import Text.TOML.Parser
import Text.TOML.Value


parse :: B.ByteString -> Maybe TOML
parse bs = process `fmap` parse' bs

parse' bs = (A.maybeResult $ A.feed (A.parse document bs) "")

process :: [Token] -> TOML
process ts = go (group ts) tempty
  where
    go []             m = m
    go ((ks, kvs):gs) m = go gs (okalter ks kvs m)

    okalter :: [B.ByteString] -> [(B.ByteString, TOMLV)] -> TOML -> TOML
    okalter []     kvs t = insertMany kvs t
    okalter (k:ks) kvs t = liftT (M.alter (Just . f) (B.unpack k)) t
      where f Nothing   = liftTV (okalter ks kvs) (Left tempty)
            f (Just t') = liftTV (okalter ks kvs) t'

    insertMany :: [(B.ByteString, TOMLV)] -> TOML -> TOML
    insertMany kvs m = foldl' (flip $ uncurry tinsert) m kvs'
      where kvs' = [(B.unpack k, Right v) | (k, v) <- kvs]

-- NB: groupBy will never produce an empty group.
group ts = alternate $ (map omg) $ (groupBy right ts)
  where 
    omg ls@((Left l):_)  = Left l
    omg rs@((Right _):_) = Right (rights rs)
    -- Only key-value pairs are grouped together
    right (Right _) (Right _) = True
    right _         _         = False

    -- If the token list starts with a Right, then there are key-value pairs that
    -- don't belong to a keygroup. Assign that one the 'empty' keygroup, and match
    -- pairs. If the token list starts with a right, then there are no "global"
    -- key-value pairs, and it's ok to straight zip the partition.
    --
    alternate                          []  = []
    alternate ((Left l)              : []) = (l , []) : []
    alternate ((Right r)             : gs) = ([], r ) : (alternate gs)
    alternate ((Left l ) : (Right r) : gs) = (l , r ) : (alternate gs)
    alternate ((Left l1) : (Left l2) : gs) = (l1, []) : (alternate $ (Left l2) : gs)