content: -- | Types for edits.
  --
  -- Every content edit is associated with an 'Edit', which is stored in the
  -- database and can be undone. In addition, each edit has a corresponding
  -- 'EditDetails' (which stores IP, date, and ID of an edit).
  module Guide.Types.Edit
  (
    Edit(..),
    isVacuousEdit,
    EditDetails(..),
  )
  where
  
  
  import Imports
  
  -- Network
  import Data.IP
  -- acid-state
  import Data.SafeCopy hiding (kind)
  import Data.SafeCopy.Migrate
  
  import Guide.Types.Core
  import Guide.Utils
  import Guide.Uid
  
  
  -- | Edits made by users. It should always be possible to undo an edit.
  data Edit
    -- Add
    = EditAddCategory {
        editCategoryUid   :: Uid Category,
        editCategoryTitle :: Text,
        editCategoryGroup :: Text }
    | EditAddItem {
        editCategoryUid :: Uid Category,
        editItemUid     :: Uid Item,
        editItemName    :: Text }
    | EditAddPro {
        editItemUid      :: Uid Item,
        editTraitId      :: Uid Trait,
        editTraitContent :: Text }
    | EditAddCon {
        editItemUid      :: Uid Item,
        editTraitId      :: Uid Trait,
        editTraitContent :: Text }
  
    -- Change category properties
    | EditSetCategoryTitle {
        editCategoryUid      :: Uid Category,
        editCategoryTitle    :: Text,
        editCategoryNewTitle :: Text }
    | EditSetCategoryGroup {
        editCategoryUid      :: Uid Category,
        editCategoryGroup    :: Text,
        editCategoryNewGroup :: Text }
    | EditSetCategoryNotes {
        editCategoryUid      :: Uid Category,
        editCategoryNotes    :: Text,
        editCategoryNewNotes :: Text }
    | EditSetCategoryStatus {
        editCategoryUid       :: Uid Category,
        editCategoryStatus    :: CategoryStatus,
        editCategoryNewStatus :: CategoryStatus }
    | EditChangeCategoryEnabledSections {
        editCategoryUid             :: Uid Category,
        editCategoryEnableSections  :: Set ItemSection,
        editCategoryDisableSections :: Set ItemSection }
  
    -- Change item properties
    | EditSetItemName {
        editItemUid     :: Uid Item,
        editItemName    :: Text,
        editItemNewName :: Text }
    | EditSetItemLink {
        editItemUid     :: Uid Item,
        editItemLink    :: Maybe Url,
        editItemNewLink :: Maybe Url }
    | EditSetItemGroup {  -- TODO: remove after migration to Postgres
        editItemUid      :: Uid Item,
        editItemGroup    :: Maybe Text,
        editItemNewGroup :: Maybe Text }
    | EditSetItemHackage {
        editItemUid        :: Uid Item,
        editItemHackage    :: Maybe Text,
        editItemNewHackage :: Maybe Text }
  
    | EditSetItemSummary {
        editItemUid        :: Uid Item,
        editItemSummary    :: Text,
        editItemNewSummary :: Text }
    | EditSetItemNotes {
        editItemUid      :: Uid Item,
        editItemNotes    :: Text,
        editItemNewNotes :: Text }
    | EditSetItemEcosystem {
        editItemUid          :: Uid Item,
        editItemEcosystem    :: Text,
        editItemNewEcosystem :: Text }
  
    -- Change trait properties
    | EditSetTraitContent {
        editItemUid         :: Uid Item,
        editTraitUid        :: Uid Trait,
        editTraitContent    :: Text,
        editTraitNewContent :: Text }
  
    -- Delete
    | EditDeleteCategory {
        editCategoryUid      :: Uid Category,
        editCategoryPosition :: Int }
    | EditDeleteItem {
        editItemUid      :: Uid Item,
        editItemPosition :: Int }
    | EditDeleteTrait {
        editItemUid       :: Uid Item,
        editTraitUid      :: Uid Trait,
        editTraitPosition :: Int }
  
    -- Other
    | EditMoveItem {
        editItemUid   :: Uid Item,
        editDirection :: Bool }
    | EditMoveTrait {
        editItemUid   :: Uid Item,
        editTraitUid  :: Uid Trait,
        editDirection :: Bool }
  
    deriving (Eq, Show, Data)
  
  deriveSafeCopySimple 9 'extension ''Edit
  
  genVer ''Edit (Current 9, Past 8) [
    -- Add
    Copy "EditAddCategory",
    Copy "EditAddItem",
    Copy "EditAddPro",
    Copy "EditAddCon",
    -- Change category properties
    Copy "EditSetCategoryTitle",
    Copy "EditSetCategoryGroup",
    Copy "EditSetCategoryNotes",
    Copy "EditSetCategoryStatus",
    Copy "EditChangeCategoryEnabledSections",
    -- Change item properties
    Copy "EditSetItemName",
    Copy "EditSetItemLink",
    Copy "EditSetItemGroup",
    Custom "EditSetItemKind" [
      ("editItemUid", [t|Uid Item|]),
      ("editItemKind", [t|ItemKind|]),
      ("editItemNewKind", [t|ItemKind|])],
    Copy "EditSetItemSummary",
    Copy "EditSetItemNotes",
    Copy "EditSetItemEcosystem",
    -- Change trait properties
    Copy "EditSetTraitContent",
    -- Delete
    Copy "EditDeleteCategory",
    Copy "EditDeleteItem",
    Copy "EditDeleteTrait",
    -- Other
    Copy "EditMoveItem",
    Copy "EditMoveTrait" ]
  
  deriveSafeCopySimple 8 'extension ''Edit_v8
  
  instance Migrate Edit where
    type MigrateFrom Edit = Edit_v8
    migrate = $(migrateVer ''Edit (Current 9, Past 8) [
      -- Add
      CopyM "EditAddCategory",
      CopyM "EditAddItem",
      CopyM "EditAddPro",
      CopyM "EditAddCon",
      -- Change category properties
      CopyM "EditSetCategoryTitle",
      CopyM "EditSetCategoryGroup",
      CopyM "EditSetCategoryNotes",
      CopyM "EditSetCategoryStatus",
      CopyM "EditChangeCategoryEnabledSections",
      -- Change item properties
      CopyM "EditSetItemName",
      CopyM "EditSetItemLink",
      CopyM "EditSetItemGroup",
      CustomM "EditSetItemKind" [|\x ->
        EditSetItemHackage
          { editItemUid = editItemUid_v8 x
          , editItemHackage = case editItemKind_v8 x of
            Library m -> m
            Tool m    -> m
            Other     -> Nothing
          , editItemNewHackage  = case editItemNewKind_v8 x of
            Library m -> m
            Tool m    -> m
            Other     -> Nothing
          }
      |],
      CopyM "EditSetItemSummary",
      CopyM "EditSetItemNotes",
      CopyM "EditSetItemEcosystem",
      -- Change trait properties
      CopyM "EditSetTraitContent",
      -- Delete
      CopyM "EditDeleteCategory",
      CopyM "EditDeleteItem",
      CopyM "EditDeleteTrait",
      -- Other
      CopyM "EditMoveItem",
      CopyM "EditMoveTrait"
      ])
  
  genVer ''Edit (Past 8, Past 7) [
    -- Add
    Custom "EditAddCategory" [
        ("editCategoryUid"  , [t|Uid Category|]),
        ("editCategoryTitle", [t|Text|]) ],
    Copy "EditAddItem",
    Copy "EditAddPro",
    Copy "EditAddCon",
    -- Change category properties
    Copy "EditSetCategoryTitle",
    Copy "EditSetCategoryGroup",
    Copy "EditSetCategoryNotes",
    Copy "EditSetCategoryStatus",
    Copy "EditChangeCategoryEnabledSections",
    -- Change item properties
    Copy "EditSetItemName",
    Copy "EditSetItemLink",
    Copy "EditSetItemGroup",
    Copy "EditSetItemKind",
    Copy "EditSetItemSummary",
    Copy "EditSetItemNotes",
    Copy "EditSetItemEcosystem",
    -- Change trait properties
    Copy "EditSetTraitContent",
    -- Delete
    Copy "EditDeleteCategory",
    Copy "EditDeleteItem",
    Copy "EditDeleteTrait",
    -- Other
    Copy "EditMoveItem",
    Copy "EditMoveTrait" ]
  
  deriveSafeCopySimple 7 'base ''Edit_v7
  
  instance Migrate Edit_v8 where
    type MigrateFrom Edit_v8 = Edit_v7
    migrate = $(migrateVer ''Edit (Past 8, Past 7) [
      CustomM "EditAddCategory" [|\x ->
        EditAddCategory_v8
          { editCategoryUid_v8 = editCategoryUid_v7 x
          , editCategoryTitle_v8 = editCategoryTitle_v7 x
          , editCategoryGroup_v8 = "Miscellaneous"
          } |],
      CopyM "EditAddItem",
      CopyM "EditAddPro",
      CopyM "EditAddCon",
      -- Change category properties
      CopyM "EditSetCategoryTitle",
      CopyM "EditSetCategoryGroup",
      CopyM "EditSetCategoryNotes",
      CopyM "EditSetCategoryStatus",
      CopyM "EditChangeCategoryEnabledSections",
      -- Change item properties
      CopyM "EditSetItemName",
      CopyM "EditSetItemLink",
      CopyM "EditSetItemGroup",
      CopyM "EditSetItemKind",
      CopyM "EditSetItemSummary",
      CopyM "EditSetItemNotes",
      CopyM "EditSetItemEcosystem",
      -- Change trait properties
      CopyM "EditSetTraitContent",
      -- Delete
      CopyM "EditDeleteCategory",
      CopyM "EditDeleteItem",
      CopyM "EditDeleteTrait",
      -- Other
      CopyM "EditMoveItem",
      CopyM "EditMoveTrait"
      ])
  
  -- | Determine whether the edit doesn't actually change anything and so isn't
  -- worth recording in the list of pending edits.
  isVacuousEdit :: Edit -> Bool
  isVacuousEdit EditSetCategoryTitle {..} =
    editCategoryTitle == editCategoryNewTitle
  isVacuousEdit EditSetCategoryGroup {..} =
    editCategoryGroup == editCategoryNewGroup
  isVacuousEdit EditSetCategoryNotes {..} =
    editCategoryNotes == editCategoryNewNotes
  isVacuousEdit EditSetCategoryStatus {..} =
    editCategoryStatus == editCategoryNewStatus
  isVacuousEdit EditChangeCategoryEnabledSections {..} =
    null editCategoryEnableSections && null editCategoryDisableSections
  isVacuousEdit EditSetItemName {..} =
    editItemName == editItemNewName
  isVacuousEdit EditSetItemLink {..} =
    editItemLink == editItemNewLink
  isVacuousEdit EditSetItemGroup {..} =
    editItemGroup == editItemNewGroup
  isVacuousEdit EditSetItemHackage {..} =
    editItemHackage == editItemNewHackage
  isVacuousEdit EditSetItemSummary {..} =
    editItemSummary == editItemNewSummary
  isVacuousEdit EditSetItemNotes {..} =
    editItemNotes == editItemNewNotes
  isVacuousEdit EditSetItemEcosystem {..} =
    editItemEcosystem == editItemNewEcosystem
  isVacuousEdit EditSetTraitContent {..} =
    editTraitContent == editTraitNewContent
  isVacuousEdit EditAddCategory{}    = False
  isVacuousEdit EditAddItem{}        = False
  isVacuousEdit EditAddPro{}         = False
  isVacuousEdit EditAddCon{}         = False
  isVacuousEdit EditDeleteCategory{} = False
  isVacuousEdit EditDeleteItem{}     = False
  isVacuousEdit EditDeleteTrait{}    = False
  isVacuousEdit EditMoveItem{}       = False
  isVacuousEdit EditMoveTrait{}      = False
  
  data EditDetails = EditDetails {
    editIP   :: Maybe IP,
    editDate :: UTCTime,
    editId   :: Int }
    deriving (Eq, Show, Data)
  
  deriveSafeCopySorted 4 'extension ''EditDetails
  
  changelog ''EditDetails (Current 4, Past 3) []
  deriveSafeCopySorted 3 'base ''EditDetails_v3
  