{-# LANGUAGE OverloadedStrings #-}
module Haddock.Sqlite where
import           Control.Monad.IO.Class
import           Control.Monad.M
import           Data.Monoid
import qualified Data.Text as T
import           Database.SQLite.Simple
import           Haddock.Artifact
import qualified Module as Ghc
import qualified Name as Ghc

data IndexRow = IndexRow {
  nameAttr :: T.Text 
  , typeAttr :: T.Text 
  , pathAttr :: T.Text 
  , modAttr :: T.Text 
} deriving (Show)

instance Monoid IndexRow where
  mempty = IndexRow mempty mempty mempty mempty
  mappend l r =  
    IndexRow 
      (mappend (nameAttr l) (nameAttr r))
      (mappend (typeAttr l) (typeAttr r))
      (mappend (pathAttr l) (pathAttr r))
      (mappend (modAttr l) (modAttr r))

-- TODO lensify
instance ToRow IndexRow where
  toRow index = 
    [SQLText . nameAttr $ index
     , SQLText . typeAttr $ index
     , SQLText . pathAttr $ index
     , SQLText . modAttr $ index
    ]
  
-- I probably chould derive this from a type, but that's overkill right now.
table :: String
table = "searchIndex(name, type, path, module)"

createTable :: Connection -> IO ()
createTable conn =
  mapM_ (execute_ conn) 
    ["CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT, module TEXT);",
      Query . T.pack $ "CREATE UNIQUE INDEX anchor ON " ++ table ++ ";"]

insertRow :: Connection -> IndexRow -> IO ()
insertRow conn =
  execute conn 
    (Query . T.pack $ "INSERT OR IGNORE INTO " ++ table ++ " VALUES (?,?,?,?);")

modUrl :: Ghc.Module -> String
modUrl = 
  map (\c-> if c == '.' then '-' else c)
    . Ghc.moduleNameString 
    . Ghc.moduleName 

escapeSpecial :: String -> String 
escapeSpecial = 
  concatMap (\c -> if c `elem` specialChars then '-': show (fromEnum c) ++ "-" else [c])
  where
    specialChars  = "!&|+$%(,)*<>-/=#^\\?"
 
-- | Update the sqlite database with the given haddock artifact.
fromArtifact :: Ghc.PackageId -> Connection -> Artifact -> M ()
fromArtifact p conn art = do
  attributes <- toAttributes
  case attributes of 
    Just (name, type', path, m) -> 
      liftIO . insertRow conn $ 
        mempty { 
          nameAttr = T.pack name
          , typeAttr = type'
          , pathAttr = T.pack path
          , modAttr = T.pack m
          }
    Nothing -> 
      return ()
  where
    modStr m = Ghc.moduleNameString $ Ghc.moduleName m
    -- | Convert haddock artifacts to attributes for table update.
    toAttributes = 
      case art of 
       Haddock _               ->
         -- TODO unsupported right now 
         return Nothing 
       Package                 ->  
         return . Just $ 
           (Ghc.packageIdString p, "Package", "index.html", [])
       Module ghcmod           -> 
         return . Just $
            (modStr ghcmod, "Module" , modUrl ghcmod ++ ".html" , modStr ghcmod)
       Function ghcmod ghcname -> 
         let (declType, pfx) = toPair ghcname in
           return . Just $ 
             ( modStr ghcmod ++ '.':Ghc.getOccString ghcname,
              declType, 
              url pfx,
              modStr ghcmod)
          where
            url pfx =
              modUrl ghcmod ++ ".html#" ++ pfx : ':' : 
                escapeSpecial (Ghc.getOccString ghcname)
            toPair n
              | Ghc.isTyConName   n = ("Type"        , 't')
              | Ghc.isDataConName n = ("Constructor" , 'v')
              | otherwise           = ("Function"    , 'v')
