module Text.Seonbi.Hangul
    ( JamoTriple
    , fromJamoTriple
    , isHangulSyllable
    , toJamoTriple
    ) where

-- $setup
-- >>> import qualified Text.Show.Unicode
-- >>> :set -interactive-print=Text.Show.Unicode.uprint

-- | A triple of an initial consonant, a vowel, and an optional final consonant.
type JamoTriple = (Char, Char, Maybe Char)

-- | Checks if a character is a hangul letter and a complete syllable.
--
-- >>> isHangulSyllable '가'
-- True
-- >>> isHangulSyllable 'ㄱ'
-- False
isHangulSyllable :: Char -> Bool
isHangulSyllable :: Char -> Bool
isHangulSyllable Char
c =
    Char
c forall a. Ord a => a -> a -> Bool
>= Char
'\xac00' Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'\xd7a3';

syllableBase :: Int
syllableBase :: Int
syllableBase = Int
0xac00

initialBase :: Int
initialBase :: Int
initialBase = Int
0x1100

vowelBase :: Int
vowelBase :: Int
vowelBase = Int
0x1161

finalBase :: Int
finalBase :: Int
finalBase = Int
0x11a7

vowelCount :: Int
vowelCount :: Int
vowelCount = Int
21;

finalCount :: Int
finalCount :: Int
finalCount = Int
28;

-- | Takes a complete hangul syllable apart into consonants and a vowel.
-- Returns 'Nothing' for non-hangul letters.
--
-- >>> toJamoTriple '가'
-- Just ('ᄀ','ᅡ',Nothing)
-- >>> toJamoTriple '글'
-- Just ('ᄀ','ᅳ',Just 'ᆯ')
-- >>> toJamoTriple 'A'
-- Nothing
toJamoTriple :: Char -> Maybe JamoTriple
toJamoTriple :: Char -> Maybe JamoTriple
toJamoTriple Char
c
  | Char -> Bool
isHangulSyllable Char
c = forall a. a -> Maybe a
Just
      ( forall a. Enum a => Int -> a
toEnum forall a b. (a -> b) -> a -> b
$ Int
initialBase forall a. Num a => a -> a -> a
+ ((Int
syllable forall a. Integral a => a -> a -> a
`div` Int
finalCount) forall a. Integral a => a -> a -> a
`div` Int
vowelCount)
      , forall a. Enum a => Int -> a
toEnum forall a b. (a -> b) -> a -> b
$ Int
vowelBase forall a. Num a => a -> a -> a
+ ((Int
syllable forall a. Integral a => a -> a -> a
`div` Int
finalCount) forall a. Integral a => a -> a -> a
`mod` Int
vowelCount)
      , case Int
syllable forall a. Integral a => a -> a -> a
`mod` Int
finalCount of
          Int
0 -> forall a. Maybe a
Nothing
          Int
f -> forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Enum a => Int -> a
toEnum (Int
finalBase forall a. Num a => a -> a -> a
+ Int
f)
      )
  | Bool
otherwise = forall a. Maybe a
Nothing
  where
    syllable :: Int
    syllable :: Int
syllable = forall a. Enum a => a -> Int
fromEnum Char
c forall a. Num a => a -> a -> a
- Int
syllableBase

-- | Composes hangul jamo triple into a hangul syllable.
--
-- >>> fromJamoTriple ('ᄀ', 'ᅡ', Nothing)
-- Just '가'
-- >>> fromJamoTriple ('ᄀ', 'ᅳ', Just 'ᆯ')
-- Just '글'
fromJamoTriple :: JamoTriple -> Maybe Char
fromJamoTriple :: JamoTriple -> Maybe Char
fromJamoTriple (Char
initial, Char
vowel, Maybe Char
final)
  | Int
initialIndex forall a. Ord a => a -> a -> Bool
< Int
0 = forall a. Maybe a
Nothing
  | Int
initialIndex forall a. Ord a => a -> a -> Bool
> Int
18 = forall a. Maybe a
Nothing
  | Int
vowelIndex forall a. Ord a => a -> a -> Bool
< Int
0 = forall a. Maybe a
Nothing
  | Int
vowelIndex forall a. Ord a => a -> a -> Bool
> Int
20 = forall a. Maybe a
Nothing
  | Int
finalIndex forall a. Ord a => a -> a -> Bool
< Int
0 = forall a. Maybe a
Nothing
  | Int
finalIndex forall a. Ord a => a -> a -> Bool
> Int
27 = forall a. Maybe a
Nothing
  | Bool
otherwise = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Enum a => Int -> a
toEnum forall a b. (a -> b) -> a -> b
$ Int
syllableBase forall a. Num a => a -> a -> a
+
      (Int
initialIndex forall a. Num a => a -> a -> a
* Int
vowelCount forall a. Num a => a -> a -> a
+ Int
vowelIndex) forall a. Num a => a -> a -> a
* Int
finalCount forall a. Num a => a -> a -> a
+ Int
finalIndex
  where
    initialIndex :: Int
    initialIndex :: Int
initialIndex = forall a. Enum a => a -> Int
fromEnum Char
initial forall a. Num a => a -> a -> a
- Int
initialBase
    vowelIndex :: Int
    vowelIndex :: Int
vowelIndex = forall a. Enum a => a -> Int
fromEnum Char
vowel forall a. Num a => a -> a -> a
- Int
vowelBase
    finalIndex :: Int
    finalIndex :: Int
finalIndex = forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int
0 (\ Char
f -> forall a. Enum a => a -> Int
fromEnum Char
f forall a. Num a => a -> a -> a
- Int
finalBase) Maybe Char
final