-- ------------------------------------------------------------

{- |
   Module     : GenBlocks
   Copyright  : Copyright (C) 2005 Uwe Schmidt
   License    : MIT

   Maintainer : Uwe Schmidt (uwe@fh-wedel.de)
   Stability  : stable
   Portability: portable

   Generator for Unicode Blocks

-}

-- ------------------------------------------------------------

module Main
where

import           Control.Arrow

import           Data.Char     (isAlphaNum)

main :: IO ()
main
    = do
      c <- readFile "Blocks.txt"
      putStrLn $ genBlocks c

genBlocks       :: String -> String
genBlocks
    = lines
      >>> ( ( filter isBlock
              >>> map parseLine
            )
            &&&
            ( head
              >>> dropWhile (/= '-')
              >>> drop 1
              >>> reverse
              >>> drop 4
              >>> reverse
            )
          )
      >>> processBlockDefs
    where
    isBlock l
        = take 1 l /= ['#']
          &&
          not (all (`elem` " \t") l)

    header
        = unlines
          [ cmt
          , ""
          , "{- |"
          , "   Module     : Data.Char.Properties.UnicodeBlocks"
          , "   Copyright  : Copyright (C) 2010- Uwe Schmidt"
          , "   License    : MIT"
          , ""
          , "   Maintainer : Uwe Schmidt (uwe@fh-wedel.de)"
          , "   Stability  : stable"
          , "   Portability: portable"
          , ""
          , "   Unicode Code Blocks"
          , ""
          , "   don't edit this module"
          , "   it's generated from 'http:\\/\\/www.unicode.org\\/Public\\/UNIDATA\\/Blocks.txt'"
          , "-}"
          , ""
          , cmt
          , ""
          , "module Data.Char.Properties.UnicodeBlocks"
          , "  ( codeBlocks"
          , "  , elemCodeBlock"
          , "  , versionUnicode"
          ]

    header2
        = unlines
          [ "  )"
          , "where"
          , ""
          , cmt
          , ""
          ]
    header3
        = unlines
          [ "elemCodeBlock     :: Char -> String -> Bool"
          , "elemCodeBlock c b = maybe False (\\ (lb, ub) -> c >= lb && c <= ub) $ lookup b codeBlocks"
          , ""
          , "codeBlocks        :: [(String, (Char, Char))]"
          , "codeBlocks ="
          ]

    trailer
        = unlines
          [ cmt
          ]

    processBlockDefs (ls, vers)
        = header ++
          processExports (map (blockName . fst) ls) ++
          header2 ++
          processVersion vers ++
          processBlockTable ls ++
          ( "\n" ++ cmt ++ "\n\n" ) ++
          concatMap processBlockPredicate ls ++
          trailer

    processExports
        = joinStr "\n  , "
          >>> (++ "\n")
          >>> ("  , " ++)

    processVersion vers
        = unlines
          [ "versionUnicode :: String"
          , "versionUnicode = " ++ show vers
          , ""
          ]

    processBlockTable ls
        = header3 ++ "    [ " ++ joinStr "\n    , " (map entry ls) ++ "\n    ]\n"
        where
        entry (name, (lb, ub))
            = "( " ++ show name ++ ", ( " ++ hexChar lb ++ ", " ++ hexChar ub ++ ") )"

    processBlockPredicate (name', (lb, ub))
        = unlines
          [ name ++ "   :: Char -> Bool"
          , name ++ " c = c >= " ++ hexChar lb ++ " && c <= " ++ hexChar ub
          , ""
          ]
        where
        name = blockName name'

    parseLine l
            = (name, (lb, ub))
            where
            (rng, name') = break (==';') l
            (lb,ub')     = break (=='.') rng
            ub           = drop 2 ub'
            name         = filter legalChar . drop 1 $ name'
            legalChar c  = 'A' <= c && c <= 'Z' ||
                           'a' <= c && c <= 'z' ||
                           '0' <= c && c <= '9' ||
                           '-' == c
    blockName
        = ("is" ++) . filter isAlphaNum

joinStr :: String -> [String] -> String
joinStr js
    = foldr1 (\ x y -> x ++ js ++ y)

hexChar :: String -> String
hexChar x = "'\\x" ++ x ++ "'"

cmt :: String
cmt = "-- " ++ replicate 60 '-'
