{- Xml2Html

   Read BLAST Output in XML format, and output as a set of
   HTML files.  In contrast to almost everything else out there, 
   we generate HTML tables, rather than a GIF or similar for the
   graphical output.

-}

module Html where

import Tabulate

import Bio.Alignment.BlastData

import Data.ByteString.Lazy.Char8 (unpack,ByteString)
import Text.XHtml
import System.Directory
import System.IO
import Text.Printf
import Debug.Trace (trace)

import Options (htmldir)

instance HTML ByteString where
    toHtml = toHtml . unpack

makeDirectory :: FilePath -> IO ()
makeDirectory f = do
   createDirectory f `catch` (\e -> fail ("Couldn't create directory: '" ++ f++ "'\n"++show e))

doctype, htmlfooter, default_bg :: String

-- manual open tags
doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"

htmlheader :: BlastResult -> [String] -> String
htmlheader b hs = doctype ++ "<html>" ++ renderHtmlFragment (header << thetitle << "Blast") 
                ++ "<body>" ++ renderHtmlFragment pagehdr ++ "\n<table border=\"1\">\n" 
                ++ renderHtmlFragment (tr << map (th <<) hs)
    where pagehdr = (h1 << "Blast results") 
                    +++ p ((toHtml ("Program: "++unpack (blastversion b)))
                    +++ br +++ toHtml ("Database: "++ unpack (database b)))

htmlfooter = "    </table>\n  </body>\n</html>"

-- xs = writeFile "index.html" $ renderHtmlFragment document
--          mklink x = anchor ! [href (mkdirname x)] << x +++ br 

mkdirname :: FilePath -> FilePath
mkdirname x = htmldir++"/"++x++".html"

type Link = String

-- | Generate two results: 
--   1. a new file displaying the aligned matches (via genBrfile)
--   2. a row in the index file, with a link to the former (using writeRow)
mkHtml :: Handle -> (BlastRecord -> [[String]]) -> BlastRecord -> IO ()
mkHtml h writer br1 = 
    do let ls@((q:_):_) = writer br1
           writeRow (x:xs) = hPutStr h $ renderHtmlFragment (tr << (td << mklink x : (map (td <<) xs)))
           mklink x = anchor ! [href (mkdirname x)] << x
       mapM writeRow ls 
       genBrfile q br1

genBrfile :: FilePath -> BlastRecord -> IO ()
genBrfile fn x = writeFile (mkdirname fn) $ renderHtml $ record $ tabulate x

record :: Table -> Html
record (t,w,rs) = table -- ! [ border 1 ]
           << (tr << (th << t +++ th << ruler w
                      +++ map hit rs))
                where ruler i = table ! [ border 1, cellspacing 0, width (show i) ] << tr << go i
                      go i | i > 100 = td ! [ width "100" , small_font ] 
                                       << "|" +++ go (i-100)
                           | otherwise = td ! [ width (show i), small_font ] << ""

small_font :: HtmlAttr
small_font = thestyle "font-size:50%"
default_bg = "#d0e0ff"

-- | Format a BlastHit as a <tr>
hit :: Row -> Html
hit (h_name,ms) = tr ! [bgcolor default_bg] << 
        (td << anchor ! [ href (unpack h_name++".html") {-, title (unwords "h_desc") -} ] << h_name
        +++ td << (line ms))

-- | Format a set of BlastMatches from one BlastHit
--   Total length in pixels(?
line :: [Cell] -> Html
line bs = table ! [ cellspacing 0 ] << tr << map mycell bs +++ mycomment
    where
      mycomment = comment $ show bs
--                  $ map (\b -> let (f,t) = (q_from b,q_to b) 
--                               in (f,t,bits b/fromIntegral (t-f),aux b)) $ bs

      mycell (Cell w c fr) = td ! [width (show w), bgcolor (makeColor c), small_font] 
                             << (maybe "" showFrame fr)

      showFrame (Frame Plus n)  = "+"++show n
      showFrame (Frame Minus n) = "-"++show n
      showFrame (Strands a b)   = if a==b then "->" else "<-"

-- blastn and tblastn report bits > 2/position
-- Currently we just report it and truncate to 2 (i.e. 100%).
makeColor :: Int -> String
makeColor c | c == 0    = default_bg 
            | c > 100   = trace (" - makeColor: value >100 ("++show c++") - truncating!")
                          $ makeColor 100
            | otherwise = printf "#%2x8080" ((0x80::Int)+0x79*c`div`100)

comment :: String -> Html
comment xs = primHtml $ "<!--"++xs++"-->"


