{-# LANGUAGE DeriveAnyClass #-}

module Bio.NucleicAcid.Nucleotide.Type
  ( DNA (..)
  , RNA (..)
  , nucleoIso
  , toRNA
  , toDNA
  , Complementary (..)
  ) where

import           Control.DeepSeq (NFData)
import           Control.Lens    (Iso', iso)
import           Data.Array      (Array, Ix, bounds, listArray)
import           Data.Foldable   (Foldable (..))
import           GHC.Generics    (Generic)

data DNA = DA | DC | DG | DT
  deriving (Eq, Ord, Bounded, Enum, Generic, NFData)

instance Show DNA where
    show DA = "Adenine"
    show DC = "Cytosine"
    show DG = "Guanine"
    show DT = "Thymine"

data RNA = RA | RC | RG | RU
  deriving (Eq, Ord, Bounded, Enum, Generic, NFData)

instance Show RNA where
    show RA = "Adenine"
    show RC = "Cytosine"
    show RG = "Guanine"
    show RU = "Uracil"

-------------------------------------------------------------------------------
-- Transciption
-------------------------------------------------------------------------------

nucleoIso :: Iso' DNA RNA
nucleoIso = iso toRNA toDNA

{-# INLINE toRNA #-}
toRNA :: DNA -> RNA
toRNA DA = RA
toRNA DC = RC
toRNA DG = RG
toRNA DT = RU

{-# INLINE toDNA #-}
toDNA :: RNA -> DNA
toDNA RA = DA
toDNA RC = DC
toDNA RG = DG
toDNA RU = DT

------------------------------------------------------------------------------
-- Complementary and reverse complementary
-------------------------------------------------------------------------------

class Complementary a where
    -- | complement *NA (DNA or RNA)
    --
    cNA :: a -> a

    -- | reverce complement *NA (DNA or RNA)
    --
    rcNA :: a -> a

instance Complementary DNA where
    cNA DA = DT
    cNA DC = DG
    cNA DG = DC
    cNA DT = DA

    rcNA = cNA

instance Complementary RNA where
    cNA = toRNA . cNA . toDNA

    rcNA = cNA

instance Complementary a => Complementary [a] where
   cNA = fmap cNA

   rcNA = reverse . cNA

instance (Complementary a, Ix i) => Complementary (Array i a) where
   cNA = fmap cNA

   rcNA l = listArray (bounds l) rl
     where
       rl = rcNA . toList $ l