{-# LANGUAGE FlexibleInstances #-}
-----------------------------------------------------------------------------
-- Copyright 2019, Advise-Me project team. This file is distributed under 
-- the terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------

module Service.AdviseMe (runService, adviseMeDr, runDomainReasoner) where

import Database.SQLite.Simple.ToField
import Ideas.Service.Types hiding (tUserId)
import Ideas.Text.HTML
import Ideas.Text.XML
import Recognize.Data.DiagnoseResult
import Data.List
import Data.Maybe
import Recognize.Data.Entry
import Recognize.Data.MathStoryProblem
import Recognize.Data.Solution
import Main.Tasks
import Bayes.Script
import Bayes.StudentReport (StudentReport)
import Service.Sequencer
import Service.Types
import Util.W3CSSHTML
import Ideas.Common.Library
import Ideas.Main.Default
import Ideas.Encoding.Request (Schema(V2))
import Ideas.Encoding.ModeXML (processXML)
import Ideas.Encoding.Options (logRef, maxTime)

import qualified Ideas.Encoding.Logging as Log
import qualified Database.HDBC as SQL
import qualified Database.HDBC.Sqlite3 as SQL (connectSqlite3, setBusyTimeout)
import Database.Data

-- COMPILER BUG? instance needed?
instance ToField Rational where
 toField d = let x = fromRational d :: Double
             in toField x

-- | Run the Advise-Me domain reasoner, and then afterwards the Bayesian
-- networks. We defer logging of the request so that the request and the
-- student model are logged in one fell swoop.
runService :: IO ()
runService = do
      ref <- Log.makeLogRef "advise-me.db" V2
      Log.disableLogging ref
      defaultMainWith (mempty {logRef=ref}) adviseMeDr
      conn <- SQL.connectSqlite3 (fromJust $ Log.getFilePath ref)
      SQL.setBusyTimeout conn 1000 -- milliseconds
      Log.logRecordWith ref conn
      addModelToLatestRequest conn --temporarily turned off: no BN
      --calculation!
      SQL.commit conn
      SQL.disconnect conn

adviseMeDr :: DomainReasoner
adviseMeDr = describe "Advise-Me domain reasoner" (newDomainReasoner "advise-me")
   { exercises = map (\(Task e) -> Some (getExercise e)) tasks
   , services  = myServices
   }

myServices :: [Service]
myServices = adviseMeUserModelService
           : assessmentService
           : sequencerService
           : metaServiceList adviseMeDr ++ serviceList
           -- : adviseMeService

adviseMeUserModelService :: Service
adviseMeUserModelService = makeService "advise-me-usermodel" "Obtain user models for Advise-Me" $
   adviseMeUserModel ::: tUserRequest .-> tIO tStudentReports

-- | Request to obtain the user model.
adviseMeUserModel :: UserRequest -> IO [StudentReport]
adviseMeUserModel userRequest = do
   conn <- SQL.connectSqlite3 "advise-me.db"
   reports <- mapM (latestStudentReport conn (lang userRequest)) (ids userRequest)
   SQL.disconnect conn
   return reports

assessmentService :: Service
assessmentService = makeService "advise-me" "Assessment service for Advise-Me project" $
 assess ::: tExercise .-> tSource .-> tSolution .-> tIO tDiagnosis

assess :: Exercise a -> Source -> Solution -> IO (DiagnosisReply a)
assess ex src sol = do
   Task e <- findTask (getId ex)
   return $ DiagnosisReply
      { exercise = ex
      , solution = sol
      , source = src
      , entry = diagnose e sol
      , examinator = Just "ideas"
      }

-- | Run the domain reasoner directly, on a single input, without logging.
-- Intended for internal usage.
runDomainReasoner :: String -> IO String
runDomainReasoner inputString = do
   (_, reply, _) <- processXML options adviseMeDr inputString
   return reply

   where
   options = mempty { logRef = mempty
                    , maxTime = Just 5 }