module Types where

import           Database.SQLite.Simple
import           Data.Aeson
import           Data.Monoid ((<>))
import           Data.Text (Text, unpack)
import           Control.Error (headMay)
import           Text.Read (readMaybe)

type DB a = Connection -> IO a

-- | Language

data Language = Language
  { langId   :: Integer
  , langName :: Text
  } deriving (Eq, Show)

instance FromRow Language where
  fromRow = Language <$> field <*> field

instance ToRow Language where
  toRow l = toRow (Only (langName l))

instance ToJSON Language where
  toJSON l = object [ "id"   .= langId l
                    , "name" .= langName l
                    ]

instance FromJSON Language where
  parseJSON = withObject "Language object" $ \ o ->
    Language <$> o .: "id"
             <*> o .: "name"

allLanguages :: DB [Language]
allLanguages conn =
  query_ conn "SELECT id, name FROM languages"

-- | Entry

data Entry = Entry
  { eID       :: Integer
  , eWord     :: Text
  , eTranslit :: Text
  , eMeaning  :: Text
  , eNotes    :: Text
  , eLanguage :: Language
  } deriving (Eq, Show)

instance FromRow Entry where
  fromRow = Entry <$> field
                  <*> field
                  <*> field
                  <*> field
                  <*> field
                  <*> (Language <$> field <*> field)

instance ToRow Entry where
  toRow e = toRow ( eWord e
                  , eTranslit e
                  , eMeaning e
                  , eNotes e
                  , langId (eLanguage e)
                  )

instance ToJSON Entry where
  toJSON e = object [ "id"       .= eID e
                    , "word"     .= eWord e
                    , "translit" .= eTranslit e
                    , "meaning"  .= eMeaning e
                    , "notes"    .= eNotes e
                    , "language" .= eLanguage e
                    ]

instance FromJSON Entry where
  parseJSON = withObject "Word object" $ \ o ->
    Entry <$> o .: "id"
          <*> o .: "word"
          <*> o .: "translit"
          <*> o .: "meaning"
          <*> o .: "notes"
          <*> o .: "language"

paramsToEntry :: [(Text,Text)] -> Maybe Entry
paramsToEntry ps = Entry <$> pure 0
                         <*> lookup "word" ps
                         <*> lookup "translit" ps
                         <*> lookup "meaning" ps
                         <*> lookup "notes" ps
                         <*> (flip Language "???" <$> langNum)
  where langNum = lookup "lang" ps >>= (readMaybe . unpack)

--

addWord :: Entry -> DB Integer
addWord entry conn = do
  let q = "INSERT INTO words (word, trans, meaning, notes, language) \
          \ VALUES (?,?,?,?,?)"
  execute conn q entry
  fromIntegral <$> lastInsertRowId conn

updateWord :: Integer -> Entry -> DB ()
updateWord i e conn = do
  let q = "UPDATE words \
          \ SET word=?, trans=?, meaning=?, notes=? \
          \ WHERE id=?"
  execute conn q ( eWord e
                 , eTranslit e
                 , eMeaning e
                 , eNotes e
                 , i
                 )

getWordById :: Integer -> DB (Maybe Entry)
getWordById i conn = do
  let q = "SELECT w.id, w.word, w.trans, w.meaning, w.notes, l.id, l.name \
          \ FROM words w, languages l \
          \ WHERE w.id = ? AND w.language = l.id"
  headMay <$> query conn q (Only i)

getAllWords :: DB [Entry]
getAllWords conn = do
  let q = "SELECT w.id, w.word, w.trans, w.meaning, w.notes, l.id, l.name \
          \ FROM words w, languages l \
          \ WHERE w.language = l.id \
          \ ORDER BY w.word ASC"
  query_ conn q

getWordsByLanguage :: Text -> DB [Entry]
getWordsByLanguage lang conn = do
  let q = "SELECT w.id, w.word, w.trans, w.meaning, w.notes, l.id, l.name \
          \ FROM words w, languages l \
          \ WHERE w.language = l.id AND l.name = ? \
          \ ORDER BY w.word ASC"
  query conn q (Only lang)

searchText :: Text -> Text
searchText t = "%" <> t <> "%"

searchWord :: Text -> DB [Entry]
searchWord t conn = do
  let q = "SELECT w.id, w.word, w.trans, w.meaning, w.notes, l.id, l.name \
          \ FROM words w, languages l \
          \ WHERE w.language = l.id AND \
          \ (w.word LIKE ? OR w.trans LIKE ?) \
          \ ORDER BY w.word ASC"
  query conn q (searchText t, searchText t)

searchMeaning :: Text -> DB [Entry]
searchMeaning t conn = do
  let q = "SELECT w.id, w.word, w.trans, w.meaning, w.notes, l.id, l.name \
          \ FROM words w, languages l \
          \ WHERE w.language = l.id AND \
          \ (w.meaning LIKE ? OR w.notes LIKE ?) \
          \ ORDER BY w.word ASC"
  query conn q (searchText t, searchText t)

searchAll :: Text -> DB [Entry]
searchAll t conn = do
  let q = "SELECT w.id, w.word, w.trans, w.meaning, w.notes, l.id, l.name \
          \ FROM words w, languages l \
          \ WHERE w.language = l.id AND \
          \ (w.word LIKE ? OR w.trans LIKE ? OR \
          \  w.meaning LIKE ? OR w.notes LIKE ?) \
          \ ORDER BY w.word ASC"
  query conn q (searchText t, searchText t, searchText t, searchText t)
