{- |
   Module      :   Cookbook.Project.Quill2.Q2Api
   Copyright   :   (c) 2014 by Nate Pisarski
   License     :   BSD3
   Maintainer  :   nathanpisarski@gmail.com
   Stability   :   Stable
   Portability :   Portable (Cookbook)
Q2Api is the user-facing part of Quill. It has all the functions necessar to Create, Read, Update, and Delete information from the database, and turn a database back into a Quill-readable string.
-}

module Cookbook.Project.Quill2.Q2Api where

import qualified Cookbook.Ingredients.Tupples.Look as Lk
import qualified Cookbook.Essential.Common         as Cm
import qualified Cookbook.Essential.Continuous     as Ct
import qualified Cookbook.Ingredients.Lists.Access as Ac

import Cookbook.Project.Quill2.Q2Prelude

-- | Get the name of a Quill.
getQuillName :: Quill -> String
getQuillName = fst

-- | Return the element of the Quill, specifically useful for lists.
getQuillBody :: Quill -> Element String
getQuillBody = snd

-- | Find a quill in the database by name, returning it or a possible error type.
getQuill :: [Quill] -> String -> QuillStatus Quill
getQuill [] c = QuillMissing c
getQuill (x:xs) c
  | Ac.count (map getQuillName (x:xs)) c > 1 = QuillMultiple c
  | getQuillName x  == c = QuillSuccess x
  | otherwise = getQuill xs c

-- | Look up the value of a Quill TABLE. Will produce an error on a list.
lookUp :: [Quill] -> (String, String) -> QuillStatus String
lookUp x (a,b) = case getQuill x a of
  (QuillSuccess (_,d)) -> case d of
    (List _) -> error "Cannot look up the value of a list."
    (Table f) -> let c = Lk.lookList f b in if null c|| (length c > 1) then QuillMissing b else QuillSuccess $ head c
  (QuillMissing _)  -> QuillMissing a
  (QuillMultiple _) -> QuillMultiple a

-- | Remove a quill from the database by name.
removeQuill :: [Quill] -> String -> [Quill]
removeQuill [] _ = []
removeQuill ((x,y):xs) c
  | x == c = removeQuill xs c -- See [Q2N1]
  | otherwise = (x,y) : removeQuill xs c

-- | Remove an item from a Quill within a database. Works aggressively, meaning it removes all copies to help sanitize QuillMultiples out. th
removeItem :: [Quill] -> (String, String) -> QuillStatus [Quill]
removeItem x (a,b) = case getQuill x a of
  QuillSuccess (c,j) -> case j of
    (Table d) -> QuillSuccess $ (c, Table [(y,t) | (y,t) <- d, y /= b]) : removeQuill x a
    (List d) -> QuillSuccess  $ (c,List $ Ct.remove d c) : removeQuill x a
  QuillMissing _ -> QuillMissing a
  QuillMultiple _ -> QuillMultiple a

-- | Adds a Quill databse to the file.
addQuill :: [Quill] -> Quill -> [Quill]
addQuill x c = c : x

-- | Add a QuillAddition to the databse. QuillAddition is a safe encapsulation of list and table values.
addItem :: [Quill] -> QuillAddition -> QuillStatus [Quill]
addItem x qa = case getQuill x a of
  QuillSuccess (y, ys) -> case ys of
    (Table d) -> case qa of
      (ATable (_,b,c)) -> QuillSuccess $ (y,Table $ (b,c) : d) : removeQuill x a
      (AList _ ) -> error $ "$ Type Mismatch! Attempted to add a List type to Table in table " ++ show qa
    (List d)  -> case qa of
      (AList (_,b)) -> QuillSuccess $ (y, List (b:d)) : removeQuill x a
      (ATable _) -> error $ "Type Mismatch! Attempted to add Table type to List in table " ++ show qa
  QuillMultiple _ -> QuillMultiple a
  QuillMissing  _ -> QuillMissing a --M
  where
    a = case qa of
      (ATable (g,_,_)) -> g
      (AList  (g,_))   -> g

-- | Map a Quill function.
qMap :: QuillStatus [Quill] -> ([Quill] -> QuillStatus [Quill]) -> QuillStatus [Quill]
qMap c f = case c of
  (QuillSuccess a) ->  f a
  _ -> c

-- | Change an item within the database using a Quill addition. Wrapper of addItem and removeItem.
changeItem :: [Quill] -> QuillAddition -> QuillStatus [Quill]
changeItem x y = case y of
                      (ATable (a,b,_)) ->  qMap (removeItem x (a,b)) (`addItem` y)
                      (AList  (a,b))   ->  qMap (removeItem x (a,b)) (`addItem` y)
                 

-- | Turn a Quill table into a string.
toString :: Quill -> String
toString (nm,typ) = case typ of
  (List a)  -> Cm.flt ["list(",  nm, "){", Cm.flt (map (++ ";") a),"}"]
  (Table a) -> Cm.flt ["table(", nm, "){", Cm.flt (stringify a),   "}"]
  where
    stringify [] = []
    stringify ((a,b):xs) = Cm.flt [a,":",b,";"]  : stringify xs