-- This file is part of Qtah.
--
-- Copyright 2015-2021 The Qtah Authors.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

module Graphics.UI.Qtah.Generator.Enum (installEnumCalculator) where

import qualified Data.ByteString.Lazy as B
import Data.Maybe (isJust)
import Foreign.Hoppy.Generator.Hook (
  EnumEvaluator,
  enumEvaluatorArgsKeepOutputsOnFailure,
  hookEvaluateEnums,
  interpretOutputToEvaluateEnums,
  makeCppSourceToEvaluateEnums,
  )
import Foreign.Hoppy.Generator.Spec (Interface, interfaceModifyHooks)
import Foreign.Hoppy.Generator.Util (withTempDirectory)
import Graphics.UI.Qtah.Generator.Config (
  qmakeArguments,
  qmakeExecutable,
  )
import System.Directory (withCurrentDirectory)
import System.FilePath ((</>))
import System.IO (hPutStrLn, stderr)
import System.Process (callProcess, readProcess)

installEnumCalculator :: Interface -> Interface
installEnumCalculator :: Interface -> Interface
installEnumCalculator = (Hooks -> Hooks) -> Interface -> Interface
interfaceModifyHooks ((Hooks -> Hooks) -> Interface -> Interface)
-> (Hooks -> Hooks) -> Interface -> Interface
forall a b. (a -> b) -> a -> b
$ \Hooks
hooks ->
  Hooks
hooks { hookEvaluateEnums :: EnumEvaluator
hookEvaluateEnums = EnumEvaluator
calculateEnumValues }

calculateEnumValues :: EnumEvaluator
calculateEnumValues :: EnumEvaluator
calculateEnumValues EnumEvaluatorArgs
args =
  String
-> Bool
-> (String -> IO (Bool, Maybe EnumEvaluatorResult))
-> IO (Maybe EnumEvaluatorResult)
forall a. String -> Bool -> (String -> IO (Bool, a)) -> IO a
withTempDirectory String
"qtahenum" Bool
removeBuildFailures ((String -> IO (Bool, Maybe EnumEvaluatorResult))
 -> IO (Maybe EnumEvaluatorResult))
-> (String -> IO (Bool, Maybe EnumEvaluatorResult))
-> IO (Maybe EnumEvaluatorResult)
forall a b. (a -> b) -> a -> b
$ \String
dir ->
  String
-> IO (Bool, Maybe EnumEvaluatorResult)
-> IO (Bool, Maybe EnumEvaluatorResult)
forall a. String -> IO a -> IO a
withCurrentDirectory String
dir (IO (Bool, Maybe EnumEvaluatorResult)
 -> IO (Bool, Maybe EnumEvaluatorResult))
-> IO (Bool, Maybe EnumEvaluatorResult)
-> IO (Bool, Maybe EnumEvaluatorResult)
forall a b. (a -> b) -> a -> b
$ do
  let sourceFile :: String
sourceFile = String
"qtahenum.cpp"
      qmakeFile :: String
qmakeFile = String
"qtahenum.pro"
  String -> ByteString -> IO ()
B.writeFile String
sourceFile (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ EnumEvaluatorArgs -> ByteString
makeCppSourceToEvaluateEnums EnumEvaluatorArgs
args
  String -> String -> IO ()
writeFile String
qmakeFile (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$
    [String] -> String
unlines
    [ String
"# This file is generated by qtah-generator."
    , String
""
    , String
"QT += core gui"
    , String
"greaterThan(QT_MAJOR_VERSION, 4): QT += widgets"
      -- Prevent separate debug/ and release/ build directories caused by this
      -- flag being enabled by default on Windows (see issue #50).
    , String
"CONFIG -= debug_and_release"
    , String
"CONFIG += cmdline"
    , String
"TARGET = qtahenum"
    , String
"SOURCES += " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
sourceFile
    ]
  String -> [String] -> IO ()
callProcess String
qmakeExecutable ([String] -> IO ()) -> [String] -> IO ()
forall a b. (a -> b) -> a -> b
$ [String]
qmakeArguments [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
qmakeFile]
  -- TODO This should read from a QTAH_MAKE environment variable:
  String -> [String] -> IO ()
callProcess String
"make" []
  String
out <- String -> [String] -> String -> IO String
readProcess (String
"." String -> String -> String
</> String
"qtahenum") [] String
""
  Maybe EnumEvaluatorResult
result <- case EnumEvaluatorArgs -> String -> Either String EnumEvaluatorResult
interpretOutputToEvaluateEnums EnumEvaluatorArgs
args String
out of
    Left String
err -> do Handle -> String -> IO ()
hPutStrLn Handle
stderr String
err
                   Maybe EnumEvaluatorResult -> IO (Maybe EnumEvaluatorResult)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe EnumEvaluatorResult
forall a. Maybe a
Nothing
    Right EnumEvaluatorResult
values -> Maybe EnumEvaluatorResult -> IO (Maybe EnumEvaluatorResult)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe EnumEvaluatorResult -> IO (Maybe EnumEvaluatorResult))
-> Maybe EnumEvaluatorResult -> IO (Maybe EnumEvaluatorResult)
forall a b. (a -> b) -> a -> b
$ EnumEvaluatorResult -> Maybe EnumEvaluatorResult
forall a. a -> Maybe a
Just EnumEvaluatorResult
values
  let remove :: Bool
remove = Bool
False Bool -> Bool -> Bool
&& (Maybe EnumEvaluatorResult -> Bool
forall a. Maybe a -> Bool
isJust Maybe EnumEvaluatorResult
result Bool -> Bool -> Bool
|| Bool
removeBuildFailures)
  (Bool, Maybe EnumEvaluatorResult)
-> IO (Bool, Maybe EnumEvaluatorResult)
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool
remove, Maybe EnumEvaluatorResult
result)

  where removeBuildFailures :: Bool
removeBuildFailures = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ EnumEvaluatorArgs -> Bool
enumEvaluatorArgsKeepOutputsOnFailure EnumEvaluatorArgs
args