{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TupleSections #-}

-- | Several methods to draw ascii dot-bracket figures onto the screen. The
-- result will be one sequence line, a dot-bracket string, and possibly a list
-- of annotated basepairs. The dot-bracket string can handle base triplets.

module BioInf.Secondary.Draw.DotBracket where

import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as BS
import qualified Data.Vector.Unboxed as V
import Text.Printf
import Prelude hiding (sequence)

import Biobase.Secondary



-- | The class of dot-bracket drawable structures.

class DotBracketDraw a where
  -- | Draw a secondary structure. It is assumed that the structure is
  -- pseudoknot-free.
  draw :: PairAnno -> SequenceNumbering -> a -> String
  -- | As 'draw' but returns the different strings as parts. These can then be
  -- further altered by the receiving end.
  drawParts :: PairAnno -> SequenceNumbering -> a -> Parts
  -- | Draw a pseudoknotted secondary structures.
  drawPK :: PairAnno -> SequenceNumbering -> a -> String

data Parts = Parts
  { numbers :: Maybe String
  , sequence :: String
  , structure :: String
  , extended :: [String]
  }

-- | 

instance DotBracketDraw (String,[ExtPairIdx]) where
  draw pa sn (seq,xs) = unlines $
    maybe [] (:[]) numbers ++
    [ sequence
    , structure
    ] ++ extended
    where
      Parts{..} = drawParts pa sn (seq,xs)
  drawParts pa sn (seq,xs) = Parts
    { numbers = if sn==Numbered then Just . take l . concat $ repeat ['0'..'9'] else Nothing
    , sequence = seq
    , structure = V.toList $ V.accum updateStructure (V.fromList $ replicate l '.') (map ((,'(').baseL) xs ++ map ((,')').baseR) xs)
    , extended = [ printf "  %4d %4d   %c-%c   %s" (baseL x) (baseR x) (seq!!baseL x) (seq!!baseR x) (show $ baseT x)
                 | x <- xs, pa == Always || pa == Required && baseT x /= cWW
                 ]
    } where
      l = length seq
      updateStructure cur new
        | cur == '.' = new
        | cur == new && cur == '(' = '<'
        | cur == new && cur == ')' = '>'
        | cur `elem` "()" && new `elem` "()" = 'X'
        | otherwise = '3' -- three outgoing edges...

instance DotBracketDraw (String,[PairIdx]) where
  draw pa sn (seq,xs) = draw pa sn (seq,map (\x -> ((baseL x, baseR x), baseT x)) xs)
  drawParts pa sn (seq,xs) = drawParts pa sn (seq, map (\x -> ((baseL x, baseR x), baseT x)) xs)

instance DotBracketDraw (ByteString,[PairIdx]) where
  draw pa sn (seq,xs) = draw pa sn (BS.unpack seq,xs)
  drawParts pa sn (seq,xs) = drawParts pa sn (BS.unpack seq, xs)

instance DotBracketDraw (ByteString,[ExtPairIdx]) where
  draw pa sn (seq,xs) = draw pa sn (BS.unpack seq,xs)
  drawParts pa sn (seq,xs) = drawParts pa sn (BS.unpack seq, xs)

-- | How to handle the list of annotated pairs.

data PairAnno
  -- | Never write out the pair list with pair-type information.
  = Never
  -- | Only write out those pairs that are non-canonical, or the required list
  -- of pairs to get all information on the secondary structure.
  | Required
  -- | Always write out all pairs.
  | Always
  deriving (Show,Read,Eq)

data SequenceNumbering
  = Numbered
  | NotNumbered
  deriving (Show,Read,Eq)