module Data.Encoding.ISO2022 where

import Data.Encoding.Base
import Data.Encoding.ByteSource
import Data.Encoding.ByteSink
import Data.Encoding.Exception
import Data.Encoding.ASCII

import Data.Word
import Control.Throws

class ISO2022 e where
    readEscape :: ByteSource m => e -> m (Maybe DynEncoding)
    encodingForChar :: e -> Char -> Maybe (DynEncoding,[Word8])

encodeCharISO2022 :: (ISO2022 e,ByteSink m) => e -> Char -> m ()
encodeCharISO2022 e c = case encodingForChar e c of
                          Nothing -> throwException (HasNoRepresentation c)
                          Just (enc,esc) -> do
                                            mapM_ pushWord8 esc
                                            encodeChar enc c

decodeCharISO2022 :: (ISO2022 e,ByteSource m) => e -> m Char
decodeCharISO2022 e = do
  enc <- readEscape e
  case enc of
    Nothing -> decodeChar ASCII
    Just renc -> decodeChar renc

encodeISO2022 :: (ISO2022 e,ByteSink m) => e -> String -> m ()
encodeISO2022 e = encode' (DynEncoding ASCII)
    where
      encode' _ [] = return ()
      encode' enc (c:cs) = case encodingForChar e c of
                             Nothing -> throwException (HasNoRepresentation c)
                             Just (nenc,esc)
                                  | enc==nenc -> do
                                                 encodeChar enc c
                                                 encode' enc cs
                                  | otherwise -> do
                                                 mapM_ pushWord8 esc
                                                 encodeChar nenc c
                                                 encode' nenc cs

decodeISO2022 :: (ISO2022 e,ByteSource m) => e -> m String
decodeISO2022 e = decode' (DynEncoding ASCII)
    where
      decode' enc = do
        empty <- sourceEmpty
        if empty
          then return []
          else (do
                 nenc <- readEscape e
                 case nenc of
                   Just renc -> decode' renc
                   Nothing -> do
                             c <- decodeChar enc
                             cs <- decode' enc
                             return (c:cs)
               )