{-|
  Module:      Anki.Collection
  Copyright:   (c) 2016 Al Zohali
  License:     BSD3
  Maintainer:  Al Zohali <zohl@fmap.me>
  Stability:   experimental

  = Description
  Representation of a collection and related definitions.
-}

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleInstances #-}

module Anki.Collection (
    Collection(..)
  , GlobalOptions(..)
  , Tag(..)
  ) where


import Anki.Common (ModificationTime, AnkiException(..), throwErr)
import Anki.Common (dropPrefixOptions, getTextValue, getJsonValue, fromDictionary)
import Anki.Deck (DeckId, Deck, DeckOptions)
import Anki.Model (ModelId, Model)
import Data.Aeson (Value(..), decode, FromJSON(..), genericParseJSON)
import Data.Text (Text)
import Database.SQLite.Simple (FromRow(..), field)
import Database.SQLite.Simple.FromField (FromField(..))
import Database.SQLite.Simple.Internal (Field(..))
import Database.SQLite.Simple.Ok (Ok(..))
import GHC.Generics (Generic)
import qualified Data.Text as T


-- | Collection as in col table.
data Collection = Collection {
    collectionId            :: Int            -- ^ collection identifier (id)
  , collectionCrt           :: Int            -- TODO check type
  , collectionMod           :: ModificationTime
  , collectionScm           :: Int            -- TODO check type
  , collectionVer           :: Int            -- TODO check type
  , collectionDty           :: Int            -- TODO check type
  , collectionUsn           :: Int            -- TODO check type
  , collectionLs            :: Int            -- TODO check type
  , collectionGlobalOptions :: GlobalOptions  -- ^ global options (col.conf)
  , collectionModels        :: [Model]        -- ^ models in the collection(col.models)
  , collectionDecks         :: [Deck]         -- ^ decks in the collection (col.decks)
  , collectionDeckOptions   :: [DeckOptions]  -- ^ deck options (col.dconf)
  , collectionTags          :: [Tag]          -- ^ tags (col.tags)
  } deriving (Show, Eq, Generic)

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


-- | Global opitions.
data GlobalOptions = GlobalOptions {
   goNextPos       :: Value     -- TODO Int?
 , goEstTimes      :: Value     -- TODO Bool?
 , goSortBackwards :: Value     -- TODO Bool?
 , goSortType      :: Value     -- TODO String?
 , goTimeLim       :: Value     -- TODO Int?
 , goActiveDecks   :: [DeckId]  -- ^ TODO
 , goAddToCur      :: Value     -- TODO Bool?
 , goCurDeck       :: Value     -- TODO DeckId?
 , goCurModel      :: ModelId   -- ^ TODO
 , goLastUnburied  :: Value     -- TODO Int?
 , goCollapseTime  :: Value     -- TODO Int?
 , goActiveCols    :: Value     -- TODO [String]?
 , goSavedFilters  :: Value     -- TODO [wtf]?
 , goDueCounts     :: Value     -- TODO Bool?
 , goNewBury       :: Value     -- TODO Bool?
 , goNewSpread     :: Value     -- TODO Int?
 } deriving (Show, Eq, Generic)

instance FromJSON GlobalOptions where
  parseJSON = genericParseJSON dropPrefixOptions

instance FromField GlobalOptions where
  fromField f = getTextValue f >>= maybe (throwErr f WrongJsonFormat) return . decode


-- | Tags from col.tags.
data Tag = Tag {
    tagName   :: String -- ^ tag name (key)
  , tagNumber :: Int    -- ^ TODO: wtf? (value)
  } deriving (Show, Eq)

instance FromField [Tag] where
  fromField f = getJsonValue f >>= fromDictionary mkTag f where
    mkTag :: Field -> (Text, Value) -> Ok Tag
    mkTag f' = \case
      (name, Number number) -> return $ Tag (T.unpack name) (round number)
      _                     -> throwErr f' WrongJsonFormat