{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
-- |
-- Module      : Hasmin.Parser.Position
-- Copyright   : (c) 2017 Cristian Adrián Ontivero
-- License     : BSD3
-- Stability   : experimental
-- Portability : unknown
--
-- Parsers for CSS \<position> values.
--
-----------------------------------------------------------------------------
module Hasmin.Parser.Position where

import Control.Applicative ((<|>), optional)
import Data.Functor (($>))
import Data.Attoparsec.Text (Parser)
import qualified Data.Attoparsec.Text as A
import qualified Data.Text as T
import Control.Monad (mzero)

import Hasmin.Parser.Utils
import Hasmin.Parser.PercentageLength
import Hasmin.Types.Position
import Hasmin.Types.PercentageLength

-- | Parser for <https://drafts.csswg.org/css-values-3/#position \<position\>>.
position :: Parser Position
position = perLen <|> keyword
  where
    perLen = percentageLength >>= startsWithPL
    keyword =
        parserFromPairs [("left",   startsWith (Just PosLeft)   tb)
                        ,("right",  startsWith (Just PosRight)  tb)
                        ,("top",    startsWith (Just PosTop)    lr)
                        ,("bottom", startsWith (Just PosBottom) lr)
                        ,("center", startsWithCenter)]
    tb = (A.asciiCI "top"  $> Just PosTop,  A.asciiCI "bottom" $> Just PosBottom)
    lr = (A.asciiCI "left" $> Just PosLeft, A.asciiCI "right"  $> Just PosRight)

    startsWithPL :: PercentageLength -> Parser Position
    startsWithPL x = skipComments *>
        (followsWithPL <|> someKeyword <|> wasASinglePL)
      where
        pl = Just x
        followsWithPL = Position Nothing pl Nothing <$> (Just <$> percentageLength)
        wasASinglePL  = pure $ Position Nothing pl Nothing Nothing
        someKeyword   = do
            i <- ident
            case T.toCaseFold i of
              "center" -> pure $ Position Nothing pl (Just PosCenter) Nothing
              "top"    -> pure $ Position Nothing pl (Just PosTop) Nothing
              "bottom" -> pure $ Position Nothing pl (Just PosBottom) Nothing
              _        -> mzero

    maybePL :: Parser (Maybe PercentageLength)
    maybePL = optional percentageLength

    startsWithCenter :: Parser Position
    startsWithCenter = skipComments *>
        (followsWithPL <|> followsWithAKeyword <|> pure (posTillNow Nothing Nothing))
      where
        followsWithPL = (posTillNow Nothing . Just) <$> percentageLength
        followsWithAKeyword = do
            i <- ident <* skipComments
            let f x = posTillNow (Just x) <$> maybePL
            case T.toCaseFold i of
              "left"   -> f PosLeft
              "right"  -> f PosRight
              "top"    -> f PosTop
              "bottom" -> f PosBottom
              "center" -> pure $ posTillNow (Just PosCenter) Nothing
              _        -> mzero
        posTillNow = Position (Just PosCenter) Nothing

    -- Used for the cases when a position starts with the X axis (left and right
    -- keywords) or Y axis (top and bottom)
    startsWith :: Maybe PosKeyword
               -> (Parser (Maybe PosKeyword), Parser (Maybe PosKeyword))
               -> Parser Position
    startsWith x (p1, p2) = do
        _  <- skipComments
        pl <- optional (percentageLength <* skipComments)
        let endsWithCenter = Position x pl <$> center <*> pure Nothing
            endsWithKeywordAndMaybePL = Position x pl <$> posKeyword <*> maybePL
            endsWithPL = pure $ Position x Nothing Nothing pl
        endsWithCenter <|> endsWithKeywordAndMaybePL <|> endsWithPL
      where
        posKeyword = (p1 <|> p2)  <* skipComments
        center = A.asciiCI "center" $> Just PosCenter