{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE CPP #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  ToySolver.Data.MIP
-- Copyright   :  (c) Masahiro Sakai 2011-2014
-- License     :  BSD-style
--
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  provisional
-- Portability :  non-portable
--
-- Mixed-Integer Programming Problems with some commmonly used extensions
--
-----------------------------------------------------------------------------
module ToySolver.Data.MIP
  ( module ToySolver.Data.MIP.Base
  , readFile
  , readLPFile
  , readMPSFile
  , parseLPString
  , parseMPSString
  , writeFile
  , writeLPFile
  , writeMPSFile
  , toLPString
  , toMPSString
  , ParseError (..)
  ) where

import Prelude hiding (readFile, writeFile)
import Control.Exception
import Data.Char
import Data.Scientific (Scientific)
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TLIO
#if MIN_VERSION_megaparsec(6,0,0)
import Data.Void
#endif
import System.FilePath (takeExtension)
import System.IO hiding (readFile, writeFile)
import Text.Megaparsec

import ToySolver.Data.MIP.Base
import qualified ToySolver.Data.MIP.LPFile as LPFile
import qualified ToySolver.Data.MIP.MPSFile as MPSFile

-- | Parse .lp or .mps file based on file extension
readFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readFile opt fname =
  case map toLower (takeExtension fname) of
    ".lp"  -> readLPFile opt fname
    ".mps" -> readMPSFile opt fname
    ext -> ioError $ userError $ "unknown extension: " ++ ext

-- | Parse a file containing LP file data.
readLPFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readLPFile = LPFile.parseFile

-- | Parse a file containing MPS file data.
readMPSFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readMPSFile = MPSFile.parseFile

-- | Parse a string containing LP file data.
#if MIN_VERSION_megaparsec(6,0,0)
parseLPString :: FileOptions -> String -> String -> Either (ParseError Char Void) (Problem Scientific)
#elif MIN_VERSION_megaparsec(5,0,0)
parseLPString :: FileOptions -> String -> String -> Either (ParseError Char Dec) (Problem Scientific)
#else
parseLPString :: FileOptions -> String -> String -> Either ParseError (Problem Scientific)
#endif
parseLPString = LPFile.parseString

-- | Parse a string containing MPS file data.
#if MIN_VERSION_megaparsec(6,0,0)
parseMPSString :: FileOptions -> String -> String -> Either (ParseError Char Void) (Problem Scientific)
#elif MIN_VERSION_megaparsec(5,0,0)
parseMPSString :: FileOptions -> String -> String -> Either (ParseError Char Dec) (Problem Scientific)
#else
parseMPSString :: FileOptions -> String -> String -> Either ParseError (Problem Scientific)
#endif
parseMPSString = MPSFile.parseString

writeFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeFile opt fname prob =
  case map toLower (takeExtension fname) of
    ".lp"  -> writeLPFile opt fname prob
    ".mps" -> writeMPSFile opt fname prob
    ext -> ioError $ userError $ "unknown extension: " ++ ext

writeLPFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeLPFile opt fname prob =
  case LPFile.render opt prob of
    Left err -> ioError $ userError err
    Right s ->
      withFile fname WriteMode $ \h -> do
        case optFileEncoding opt of
          Nothing -> return ()
          Just enc -> hSetEncoding h enc
        TLIO.hPutStr h s

writeMPSFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeMPSFile opt fname prob =
  case MPSFile.render opt prob of
    Left err -> ioError $ userError err
    Right s ->
      withFile fname WriteMode $ \h -> do
        case optFileEncoding opt of
          Nothing -> return ()
          Just enc -> hSetEncoding h enc
        TLIO.hPutStr h s

toLPString :: FileOptions -> Problem Scientific -> Either String TL.Text
toLPString = LPFile.render

toMPSString :: FileOptions -> Problem Scientific -> Either String TL.Text
toMPSString = MPSFile.render