{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE FlexibleInstances #-}
module Data.Connect.Modules
   ( Modules(..)
   , JIRAModules(..)
   , emptyJIRAModules
   , ConfluenceModules(..)
   , emptyConfluenceModules
   , WebPanel(..)
   , WebPanelLayout(..)
   , GeneralPage(..)
   , JIRAProjectAdminTabPanel(..)
   ) where

import           Data.Aeson
import           Data.Aeson.Types
import           Data.Connect.AesonHelpers
import           Data.Connect.BaseTypes
import           Data.Connect.Conditions
import           Data.Connect.Webhooks
import qualified Data.HashMap.Strict       as HM
import qualified Data.Text                 as T
import           GHC.Generics

-- | 'Modules' are perhaps the most important part of your Atlassian Connect descriptor. They specify which parts of the
-- host application you wish to inject content into. They provide your entry point into the host application.
--
-- Atlassian Connect provides a large set of pre-defined entry points into the host application. Some of which are common
-- to every application and some of which are unique to the particular application that you are targeting:
--
-- * To see the JIRA modules: <https://developer.atlassian.com/static/connect/docs/modules/jira/index.html>
-- * To see the Confluence modules: <https://developer.atlassian.com/static/connect/docs/modules/confluence/index.html>
--
-- Note: One important point about modules: they must all have a key and that key must be unique inside the same Atlassian
-- Connect addon.
data Modules = Modules
   { jiraModules       :: JIRAModules -- ^ All of the JIRA Modules that you wish to define.
   , confluenceModules :: ConfluenceModules -- ^ All of the Confluence modules that you wish to define.
   } deriving (Show, Generic)

instance ToJSON Modules where
   toJSON modules = case (jm, cm) of
      (Object jiraObject, Object confluenceObject) -> Object $ HM.union jiraObject confluenceObject
      _ -> Null
      where
         jm = toJSON . jiraModules $ modules
         cm = toJSON . confluenceModules $ modules

-- TODO use Endo Modules to add modules for multiple different products to the modules list

-- | A collection of all of the JIRA Modules that you can define. For more documentation on which Modules are supported
-- the Atlassian Connect framework please see 'Modules'. You can also find more documentation on each of the modules.
data JIRAModules = JIRAModules
   { jiraWebPanels                 :: [WebPanel]
   , jiraGeneralPages              :: [GeneralPage]
   , jiraWebhooks                  :: [Webhook]
   , jiraJiraProjectAdminTabPanels :: [JIRAProjectAdminTabPanel]
   } deriving (Show, Generic)

instance ToJSON JIRAModules where
   toJSON = genericToJSON baseOptions
      { fieldLabelModifier = stripFieldNamePrefix "jira"
      }

-- | A collection of all of the Confluence Modules that you can define. For more documentation on which Modules are supported
-- the Atlassian Connect framework please see 'Modules'. You can also find more documentation on each of the modules.
data ConfluenceModules = ConfluenceModules
   { confluenceWebPanels :: [WebPanel]
   } deriving (Show, Generic)

instance ToJSON ConfluenceModules where
   toJSON = genericToJSON baseOptions
      { fieldLabelModifier = stripFieldNamePrefix "confluence"
      }

-- | Empty JIRA Modules; useful when you only want to define a few modules via Haskell record syntax.
emptyJIRAModules :: JIRAModules
emptyJIRAModules = JIRAModules [] [] [] []

-- | Empty Confluence Modules; useful when you only want to define a few modules via Haskell record syntax.
emptyConfluenceModules :: ConfluenceModules
emptyConfluenceModules = ConfluenceModules []

-- | A 'WebPanel' is an injectable segment of the host application that you can place content inside. Currently the
-- WebPanel has the same structure for both JIRA and Confluence but, potentially, that could change in the future.
-- You can read their Atlassian Connect documentation here:
--
-- * JIRA Web panels: <https://developer.atlassian.com/static/connect/docs/modules/jira/web-panel.html>
-- * Confluence Web panels: <https://developer.atlassian.com/static/connect/docs/modules/confluence/web-panel.html>
--
-- Here is what an example Hello World web panel might look like:
--
-- > helloWorldWebPanel = WebPanel
-- >    { wpKey = "hello-world"
-- >    , wpName = Name "Hello world!"
-- >    , wpUrl = "/panel/show-hello-world"
-- >    , wpLocation = "atl.jira.view.issue.right.context"
-- >    , wpConditions = [staticJiraCondition UserIsLoggedInJiraCondition]
-- >    }
-- >    where
-- >       toURI = fromJust . parseRelativeReference
--
-- WebPanels are a great way to inject your add-on's content into the host application.
data WebPanel = WebPanel
   { wpKey        :: T.Text -- ^ The add-on unique key for this module.
   , wpName       :: Name WebPanel -- ^ The name of this panel, likely to appear in the User Interface.
   , wpUrl        :: T.Text -- ^ The relative URI that the host product will hit to get HTML content.
   , wpLocation   :: T.Text -- ^ The location that this content should be injected in the host product.
   , wpConditions :: [Condition] -- ^ The 'Condition's that need to be met for this module to be displayed.
   , wpWeight     :: Maybe Integer
   , wpLayout     :: Maybe WebPanelLayout
   } deriving (Show, Generic)

instance ToJSON WebPanel where
   toJSON = genericToJSON baseOptions
      { fieldLabelModifier = stripFieldNamePrefix "wp"
      }

-- | A 'WebPanelLayout' allows you to specify the dimensions of your Web Panel if that is required.
data WebPanelLayout = WebPanelLayout
   { wplWidth  :: Length
   , wplHeight :: Length
   } deriving (Show, Generic)

instance ToJSON WebPanelLayout where
   toJSON = genericToJSON baseOptions
      { fieldLabelModifier = stripFieldNamePrefix "wpl"
      }

data WebItem = WebItem
   { wiName         :: Name WebItem
   , wiKey          :: T.Text
   -- TODO add tooltip support
   , wiLocation     :: T.Text
   , wiUrl          :: T.Text
   , wiConditions   :: [Condition]
   , wiContext      :: Maybe WebItemContext
   , wiIcon         :: Maybe IconDetails
   , wiStyleClasses :: [String]
   } deriving (Show, Generic)

data WebItemContext = PageContext | AddonContext | ProductContext
   deriving(Show, Generic)

instance ToJSON WebItemContext where
   toJSON PageContext = String . T.pack $ "page"
   toJSON AddonContext = String . T.pack $ "addon"
   toJSON ProductContext = String . T.pack $ "product"

-- TODO update the docs for the JIRAProjectAdminTabPanel based on this question: http://goo.gl/c6QUdd

-- | A 'JIRAProjectAdminTabPanel' is useful for when you want to add a page to the administration screens of a project.
-- This module will create a web item in the sidebar of every project for you and provide a web panel in the JIRA Project
-- Admin section.
data JIRAProjectAdminTabPanel = JIRAProjectAdminTabPanel
   { jpatpKey        :: T.Text
   , jpatpName       :: Name JIRAProjectAdminTabPanel
   , jpatpUrl        :: T.Text
   , jpatpLocation   :: T.Text
   , jpatpConditions :: [Condition]
   , jpatpWeight     :: Maybe Integer
   } deriving (Show, Generic)

instance ToJSON JIRAProjectAdminTabPanel where
   toJSON = genericToJSON baseOptions
      { fieldLabelModifier = stripFieldNamePrefix "jpatp"
      }

instance ToJSON (Name JIRAProjectAdminTabPanel) where
   toJSON = nameToValue

-- | A 'GeneralPage' makes your add-on take a large section of screen realestate, with the intention of displaying
-- your own page with no other distracting content. This is very useful for pages like Configuration screens or
-- Statistics pages where you really want the user to be fully working with your add-on inside the host product.
--
-- General pages, like Web Panels, are common to JIRA and Confluence and share the same json format. However they have
-- separate documentation:
--
-- * JIRA General Page: <https://developer.atlassian.com/static/connect/docs/modules/jira/general-page.html>
-- * Confluence General Page: <https://developer.atlassian.com/static/connect/docs/modules/confluence/general-page.html>
--
-- Even though, at this point in time, the documentation looks identical.
data GeneralPage = GeneralPage
   { generalPageKey        :: T.Text -- ^ The add-on unique key for this module.
   , generalPageName       :: Name GeneralPage -- ^ The name of this General Page. Likely to be used in the page title.
   , generalPageUrl        :: T.Text -- ^ The relative URI that the host product will hit to get the HTML content for the page.
   , generalPageLocation   :: Maybe T.Text -- ^ The location for this General Page to display; see the docs for your options.
   , generalPageIcon       :: Maybe IconDetails -- ^ The optional icon to use for this general page.
   , generalPageWeight     :: Maybe Integer -- ^ Determines the order that this item appears in any menu or list.
                                            -- Lower numbers mean that it will appear higher in the list.
   , generalPageConditions :: [Condition] -- ^ The 'Condition's that need to be met for this module to be displayed.
   } deriving (Show, Generic)

instance ToJSON GeneralPage where
   toJSON = genericToJSON baseOptions
      { fieldLabelModifier = stripFieldNamePrefix "generalPage"
      }

instance ToJSON (Name WebPanel) where
   toJSON = nameToValue

instance ToJSON (Name GeneralPage) where
   toJSON = nameToValue

nameToValue :: Name a -> Value
nameToValue (Name name) = object [ T.pack "value" .= name ]