-- This file is part of khph.
--
-- Copyright 2016 Bryan Gardiner <bog@khumba.net>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

-- | Data types for constructing predicates.
module Khph.Query.Base (
  Logic (..),
  ListPredicate (..),
  Query, Query' (..),
  StringQuery, StringQuery' (..),
  LinkQuery, LinkQuery' (..),
  ProjectPathComponent (..),
  TreeQuery, TreeQuery' (..),
  TagQuery,
  ) where

import Khph.Project.Base
import Khph.Util

-- | Wraps a query of type @q@ in Boolean logic.
data Logic q =
    LogicQuery q
  | LogicTrue
  | LogicFalse
  | LogicNot (Logic q)
  | LogicAnd [Logic q]
  | LogicOr [Logic q]
  deriving (Eq, Show)

-- | Represent a fold of a query @q@ over a list.
--
-- TODO Count lt/gt operators?  Useful for e.g. "more than one place tag."
data ListPredicate q =
    All q
    -- ^ All items in the list must match @q@.
  | All1 q
    -- ^ All items in the list must match @q@, and the list must be nonempty.
  | Some q
    -- ^ Some item in the list must match @q@.
  | None q
    -- ^ All items in the list must not match @q@.
  | Exists
    -- ^ The list must be nonempty.
  | DoesNotExist
    -- ^ The list must be empty.
  deriving (Eq, Show)

-- | A query on an entry.
type Query = Logic Query'

data Query' =
    QueryLink (ListPredicate LinkQuery)
  | QueryTag (ListPredicate TagQuery)
  deriving (Eq, Show)

-- | A query on a string.
type StringQuery = Logic StringQuery'

data StringQuery' =
    StringEq String
  | StringContains String
  | StringStartsWith String
  | StringEndsWith String
  deriving (Eq, Show)

-- | A query on one of an entry's links.
type LinkQuery = Logic LinkQuery'

data LinkQuery' =
    LinkTypeIs LinkType
    -- ^ Matches based on whether the link is hard or soft.
  | LinkMatchesEntrySpec EntrySpec
    -- ^ Matches based on an 'EntrySpec'.
  | LinkIsSourcePath
    -- ^ Matches if the link is at or below one of the project's source
    -- directories.
  | LinkStringQuery (Maybe ProjectPathComponent) StringQuery
    -- ^ Matches if a 'StringQuery' matches the project-relative path's textual
    -- representation.  'ProjectPathComponent' may be used to select only the
    -- directory or file name components of the path.
  | LinkTreeQuery (TreeQuery [String])
    -- ^ Matches if a 'TreeQuery' matches the link's position in the directory
    -- tree.
  deriving (Eq, Show)

data ProjectPathComponent = DirectoryComponent | FileComponent
                          deriving (Eq, Show)

-- | A query on a position in a tree.  @a@ may be an arbitrary data type, that
-- must be convertible into a @Eq b => [b]@ for comparison.
type TreeQuery a = Logic (TreeQuery' a)

data TreeQuery' a =
    TreeAt a
    -- ^ The position must be at the exact location given.
  | TreeAtAbove a
    -- ^ The position must either at or be an ancestor of the location given.
  | TreeAbove a
    -- ^ The position must be an ancestor (not at) the location given.
  | TreeAtBelow a
    -- ^ The position must either at or be a child of the location given.
  | TreeBelow a
    -- ^ The position must be a child (not at) the location given.
  deriving (Eq, Show)

-- | A tag query is just a query on the tag's position in the tag hierarchy.
type TagQuery = TreeQuery Tag
