{-# LINE 1 "UI/HSCurses/IConv.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "UI/HSCurses/IConv.hsc" #-}

--
-- Copyright (c) 2004 Tuomo Valkonen <tuomov at iki dot fi>
-- Copyright (c) 2004 Don Stewart - http://www.cse.unsw.edu.au/~dons
-- Copyright (c) 2005-2011 Stefan Wehr - http://www.stefanwehr.de
--
-- This library 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 2.1 of the License, or (at your option) any later version.
--
-- This library 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 library; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


-- | Iconv binding


{-# LINE 28 "UI/HSCurses/IConv.hsc" #-}

module UI.HSCurses.IConv {-(
    IConv,
    iconv,
    iconv_,
    with_iconv,
    to_unicode,
    from_unicode,
    to_unicode_,
    from_unicode_
  )-} where

import UI.HSCurses.CWString          ( peekUTF8StringLen, withUTF8StringLen )

import System.IO.Unsafe ( unsafePerformIO )

import Foreign hiding ( unsafePerformIO )
import Foreign.C
import Control.Exception    ( Exception, try, bracket )

type IConv = Ptr () --(#type iconv_t)

err_ptr :: Ptr b -> Bool
err_ptr p = p == (plusPtr nullPtr (-1))

throw_if_not_2_big :: String -> IO CSize -> IO CSize
throw_if_not_2_big s r_ = do
    r <- r_
    if r == fromIntegral (-1 :: Int) then do
        errno <- getErrno
        if errno /= e2BIG then
	    throwErrno s
	  else
	    return r
      else
        return r

iconv_open :: String -> String -> IO IConv
iconv_open to from =
    withCString to $
        \cto -> withCString from $
            \cfrom -> do
	        throwErrnoIf err_ptr "iconv_open"
		    $ c_iconv_open cto cfrom


iconv_close :: IConv -> IO ()
iconv_close ic =
    throwErrnoIfMinus1_ "iconv_close" $ c_iconv_close ic

outbuf_size :: Int
outbuf_size = 1024

do_iconv :: ((Ptr a, Int) -> IO String) -> IConv -> (Ptr b, Int) -> IO String
do_iconv get_string_fn ic (inbuf, inbuf_bytes) =
    alloca $ \inbuf_ptr ->
        alloca $ \inbytesleft_ptr ->
            alloca $ \outbuf_ptr ->
                alloca $ \outbytesleft_ptr ->
                    allocaBytes outbuf_size $ \outbuf -> do
      poke (inbytesleft_ptr :: Ptr CSize) (fromIntegral inbuf_bytes)
      poke inbuf_ptr inbuf
      let loop acc = do
          poke (outbytesleft_ptr :: Ptr CSize) (fromIntegral outbuf_size)
          poke outbuf_ptr outbuf
          ret <- throw_if_not_2_big "c_iconv" $
              c_iconv ic inbuf_ptr inbytesleft_ptr
                         outbuf_ptr outbytesleft_ptr
          left <- peek outbytesleft_ptr
          res <- get_string_fn (castPtr outbuf, outbuf_size - fromIntegral left)
          if ret == fromIntegral (-1 :: Int) then
              loop (acc++res)
            else
              return (acc++res)
      loop []


with_iconv :: String -> String -> (IConv -> IO a) -> IO a
with_iconv to from fn =
    bracket (iconv_open to from) iconv_close fn

iconv_ :: String -> IConv -> IO String
iconv_ str ic =
    withCStringLen str $ do_iconv peekCStringLen ic

-- between 8-bit encodings only
iconv :: Exception e => String -> String -> String -> Either e String
iconv to from str =
    unsafePerformIO $ try $ with_iconv to from (iconv_ str)



{-# LINE 150 "UI/HSCurses/IConv.hsc" #-}
-- no CF_WCHAR_SUPPORT

-- Due to endianness problems, it is easiest to do this through UTF-8

cuni_charset :: [Char]
cuni_charset = "UTF-8"

peek_cuni :: CStringLen -> IO String
peek_cuni = peekUTF8StringLen

with_cuni :: [Char] -> (CStringLen -> IO a) -> IO a
with_cuni = withUTF8StringLen


{-# LINE 164 "UI/HSCurses/IConv.hsc" #-}

to_unicode_ :: String -> String -> IO String
to_unicode_ from str =
     with_iconv cuni_charset from $
      \ic -> withCStringLen str $ do_iconv peek_cuni ic

to_unicode :: Exception e => String -> String -> Either e String
to_unicode from str =
    unsafePerformIO $ try $ to_unicode_ from str

from_unicode_ :: String -> String -> IO String
from_unicode_ to str =
     with_iconv to cuni_charset $
      \ic -> with_cuni str $ do_iconv peekCStringLen ic

from_unicode :: Exception e => String -> String -> Either e String
from_unicode from str =
    unsafePerformIO $ try $ from_unicode_ from str



{-# LINE 185 "UI/HSCurses/IConv.hsc" #-}

foreign import ccall unsafe "iconv.h iconv_open" c_iconv_open
    :: CString -> CString -> IO IConv

foreign import ccall unsafe "iconv.h iconv_close" c_iconv_close
    :: IConv -> IO CInt

foreign import ccall unsafe "iconv.h iconv" c_iconv
    :: IConv -> Ptr a -> Ptr CSize -> Ptr b -> Ptr CSize -> IO CSize


{-# LINE 207 "UI/HSCurses/IConv.hsc" #-}