content: {-# LANGUAGE ScopedTypeVariables        #-}
  {-# LANGUAGE FlexibleContexts           #-}
  {-# LANGUAGE OverloadedStrings          #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  module Main where
  
  import           Control.Monad
  import           Data.Char
  import           Data.Either
  
  import           Data.Maybe
  import           Data.Text             (Text)
  import qualified Data.Text             as T
  import qualified Data.Text.IO          as T
  import           System.Directory
  import           System.Environment
  import           System.Exit
  import           Text.Printf
  
  import           Lex
  import           Parse
  import           Print
  import           Types
  
  main :: IO ()
  main = mapM_ writeToDisk =<< getDirectoryFiles
  
  getDirectoryFiles :: IO [String]
  getDirectoryFiles = do
    filter (`notElem` exclude) <$> listDirectory "include"
      where
        exclude = [ "defines.h"
                  , "complex.h"
                  , "extra.h"
                  ]
  
  writeToDisk :: String -> IO ()
  writeToDisk fileName = do
    bindings
      <- map run
       . drop 1
       . filter (not . T.null)
       . T.lines <$> T.readFile ("include/" <> fileName)
    case partitionEithers bindings of
      (failures, successes) -> do
        if length failures > 0
          then do
            mapM_ print (listToMaybe failures)
            printf "%s failed to generate bindings\n" fileName
          else do
            let name = makeName (reverse . drop 2 . reverse $ fileName)
            T.writeFile (makePath name) $
              file name <> T.intercalate "\n" (genBinding <$> successes)
            printf "Wrote bindings to %s\n" (makePath name)
  
  -- | Filename remappings
  makeName :: String -> String
  makeName n
    | n == "lapack" = "LAPACK"
    | n == "blas" = "BLAS"
    | n == "cuda" = "CUDA"
    | otherwise = n
  
  makePath :: String -> String
  makePath s =
    printf "src/ArrayFire/Internal/%s.hsc" (capitalName (makeName s))
  
  file :: String -> Text
  file a = T.pack $ printf
    "{-# LANGUAGE CPP #-}\n\
    \module ArrayFire.Internal.%s where\n\n\
    \import ArrayFire.Internal.Defines\n\
    \import ArrayFire.Internal.Types\n\
    \import Foreign.Ptr\n\
    \import Foreign.C.Types\n\n\
    \#include \"af/%s.h\"\n\
    \" (capitalName a) (if lowerCase a == "exception"
                          then "defines"
                          else lowerCase a)
  
  capitalName, lowerCase :: [Char] -> [Char]
  capitalName (x:xs) = toUpper x : xs
  lowerCase (x:xs) = map toLower (x:xs)
  