{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
-- | ICalendar types, based on RFC5545.
module Text.ICalendar.Types
    ( module Text.ICalendar.Types
    ) where

import Codec.MIME.Type (MIMEType)
import Data.ByteString.Lazy.Char8 (ByteString)
import Data.CaseInsensitive (CI)
import Data.Default
import Data.Set (Set)
import Data.Text.Lazy (Text)
import Data.Time
import Data.Typeable (Typeable)
import Data.Version (Version(..))
import Network.URI (URI)

-- | Language.
newtype Language = Language (CI Text) -- TODO: RFC5646 types and parser.
                   deriving (Eq, Show, Ord, Typeable)

type CalAddress = URI

-- | One other parameter, either x-param or iana-param.
data OtherParam = OtherParam (CI Text) [Text]
                  deriving (Show, Eq, Ord, Typeable)

-- | Other parameters, either x-param or other iana-param.
data OtherParams = OtherParams (Set OtherParam)
                   deriving (Show, Eq, Ord, Typeable)

instance Default OtherParams where
    def = OtherParams def

-- | VCalendar component. 3.4.
data VCalendar = VCalendar
    { vcProdId     :: ProdId
    , vcVersion    :: ICalVersion
    , vcScale      :: Scale
    , vcMethod     :: Maybe Method
    , vcOther      :: Set OtherProperty
    , vcTimeZones  :: Set VTimeZone
    , vcEvents     :: Set VEvent
    , vcTodos      :: Set VTodo
    , vcJournals   :: Set VJournal
    , vcFreeBusys  :: Set VFreeBusy
    , vcOtherComps :: Set VOther
    } deriving (Show, Eq, Ord, Typeable)

instance Default VCalendar where
    def = VCalendar (ProdId "-//haskell.org/NONSGML iCalendar-0.1//EN" def)
                    (MaxICalVersion (Version [2,0] []) def)
                    def Nothing def def def def def def def

-- | Product Identifier. 3.7.3.
data ProdId = ProdId
    { prodIdValue :: Text
    , prodIdOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Version. 3.7.4.
data ICalVersion
    = MaxICalVersion
    { versionMax   :: Version
    , versionOther :: OtherParams
    }
    | MinMaxICalVersion
    { versionMax   :: Version
    , versionMin   :: Version
    , versionOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Calendar Scale. 3.7.1.
data Scale = Scale
    { scaleValue :: CI Text
    , scaleOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

instance Default Scale where
    def = Scale "GREGORIAN" def

-- | Method. 3.7.2.
data Method = Method
    { methodValue :: CI Text -- TODO: iTIP, RFC5546
    , methodOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Event Component. 3.6.1.
data VEvent = VEvent
    { veDTStamp       :: DTStamp
    , veUID           :: UID
    , veClass         :: Class -- ^ 'def' = 'Public'
    , veDTStart       :: Maybe DTStart
    , veCreated       :: Maybe Created
    , veDescription   :: Maybe Description
    , veGeo           :: Maybe Geo
    , veLastMod       :: Maybe LastModified
    , veLocation      :: Maybe Location
    , veOrganizer     :: Maybe Organizer
    , vePriority      :: Priority -- ^ 'def' = 0
    , veSeq           :: Sequence -- ^ 'def' = 0
    , veStatus        :: Maybe EventStatus
    , veSummary       :: Maybe Summary
    , veTransp        :: TimeTransparency -- ^ 'def' = 'Opaque'
    , veUrl           :: Maybe URL
    , veRecurId       :: Maybe RecurrenceId
    , veRRule         :: Set RRule
    , veDTEndDuration :: Maybe (Either DTEnd DurationProp)
    , veAttach        :: Set Attachment
    , veAttendee      :: Set Attendee
    , veCategories    :: Set Categories
    , veComment       :: Set Comment
    , veContact       :: Set Contact
    , veExDate        :: Set ExDate
    , veRStatus       :: Set RequestStatus
    , veRelated       :: Set RelatedTo
    , veResources     :: Set Resources
    , veRDate         :: Set RDate
    , veAlarms        :: Set VAlarm
    , veOther         :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | To-Do Component. 3.6.2
data VTodo = VTodo
    { vtDTStamp       :: DTStamp
    , vtUID           :: UID
    , vtClass         :: Class -- ^ 'def' = 'Public'
    , vtCompleted     :: Maybe Completed
    , vtCreated       :: Maybe Created
    , vtDescription   :: Maybe Description
    , vtDTStart       :: Maybe DTStart
    , vtGeo           :: Maybe Geo
    , vtLastMod       :: Maybe LastModified
    , vtLocation      :: Maybe Location
    , vtOrganizer     :: Maybe Organizer
    , vtPercent       :: Maybe PercentComplete
    , vtPriority      :: Priority -- ^ 'def' = 0
    , vtRecurId       :: Maybe RecurrenceId
    , vtSeq           :: Sequence -- ^ 'def' = 0
    , vtStatus        :: Maybe TodoStatus
    , vtSummary       :: Maybe Summary
    , vtUrl           :: Maybe URL
    , vtRRule         :: Set RRule
    , vtDueDuration   :: Maybe (Either Due DurationProp)
    , vtAttach        :: Set Attachment
    , vtAttendee      :: Set Attendee
    , vtCategories    :: Set Categories
    , vtComment       :: Set Comment
    , vtContact       :: Set Contact
    , vtExDate        :: Set ExDate
    , vtRStatus       :: Set RequestStatus
    , vtRelated       :: Set RelatedTo
    , vtResources     :: Set Resources
    , vtRDate         :: Set RDate
    , vtAlarms        :: Set VAlarm
    , vtOther         :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | Journal Component. 3.6.3
data VJournal = VJournal
    { vjDTStamp       :: DTStamp
    , vjUID           :: UID
    , vjClass         :: Class -- ^ 'def' = 'Public'
    , vjCreated       :: Maybe Created
    , vjDTStart       :: Maybe DTStart
    , vjLastMod       :: Maybe LastModified
    , vjOrganizer     :: Maybe Organizer
    , vjRecurId       :: Maybe RecurrenceId
    , vjSeq           :: Sequence -- ^ 'def' = 0
    , vjStatus        :: Maybe JournalStatus
    , vjSummary       :: Maybe Summary
    , vjUrl           :: Maybe URL
    , vjRRule         :: Set RRule
    , vjAttach        :: Set Attachment
    , vjAttendee      :: Set Attendee
    , vjCategories    :: Set Categories
    , vjComment       :: Set Comment
    , vjContact       :: Set Contact
    , vjDescription   :: Set Description
    , vjExDate        :: Set ExDate
    , vjRelated       :: Set RelatedTo
    , vjRDate         :: Set RDate
    , vjRStatus       :: Set RequestStatus
    , vjOther         :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | Free/Busy Component. 3.6.4
data VFreeBusy = VFreeBusy
    { vfbDTStamp       :: DTStamp
    , vfbUID           :: UID
    , vfbContact       :: Maybe Contact
    , vfbDTStart       :: Maybe DTStart
    , vfbDTEnd         :: Maybe DTEnd
    , vfbOrganizer     :: Maybe Organizer
    , vfbUrl           :: Maybe URL
    , vfbAttendee      :: Set Attendee
    , vfbComment       :: Set Comment
    , vfbFreeBusy      :: Set FreeBusy
    , vfbRStatus       :: Set RequestStatus
    , vfbOther         :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | Time Zone Component. 3.6.5.
data VTimeZone = VTimeZone
    { vtzId            :: TZID
    , vtzLastMod       :: Maybe LastModified
    , vtzUrl           :: Maybe TZUrl
    , vtzStandardC     :: Set TZProp
    , vtzDaylightC     :: Set TZProp
    , vtzOther         :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | Time zone property, also 3.6.5.
data TZProp = TZProp
    { tzpDTStart      :: DTStart
    , tzpTZOffsetTo   :: UTCOffset
    , tzpTZOffsetFrom :: UTCOffset
    , tzpRRule        :: Set RRule -- SHOULD NOT have multiple RRules.
    , tzpComment      :: Set Comment
    , tzpRDate        :: Set RDate
    , tzpTZName       :: Set TZName
    , tzpOther        :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | VAlarm component. 3.6.6.
data VAlarm
    = VAlarmAudio
    { vaTrigger     :: Trigger
    , vaRepeat      :: Repeat -- ^ 'def' = 0
    , vaDuration    :: Maybe DurationProp
    , vaAudioAttach :: Maybe Attachment
    , vaOther       :: Set OtherProperty
    , vaActionOther :: OtherParams
    }
    | VAlarmDisplay
    { vaDescription :: Description
    , vaTrigger     :: Trigger
    , vaRepeat      :: Repeat
    , vaDuration    :: Maybe DurationProp
    , vaOther       :: Set OtherProperty
    , vaActionOther :: OtherParams
    }
    | VAlarmEmail
    { vaDescription :: Description
    , vaTrigger     :: Trigger
    , vaSummary     :: Summary
    , vaAttendee    :: Set Attendee
    , vaRepeat      :: Repeat
    , vaDuration    :: Maybe DurationProp
    , vaMailAttach  :: Set Attachment
    , vaOther       :: Set OtherProperty
    , vaActionOther :: OtherParams
    }
    | VAlarmX
    { vaAction      :: CI Text
    , vaTrigger     :: Trigger
    , vaActionOther :: OtherParams
    , vaOther       :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | Any other component not recognized.
data VOther = VOther
    { voName :: CI Text
    , voProps :: Set OtherProperty
    } deriving (Show, Eq, Ord, Typeable)

-- | Attachment. 3.8.1.1.
data Attachment
    = UriAttachment
    { attachFmtType :: Maybe MIMEType
    , attachUri     :: URI
    , attachOther   :: OtherParams
    }
    | BinaryAttachment
    { attachFmtType :: Maybe MIMEType
    , attachContent :: ByteString
    , attachOther   :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Categories. 3.8.1.2.
data Categories = Categories
    { categoriesValues   :: Set Text
    , categoriesLanguage :: Maybe Language
    , categoriesOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Classification. 3.8.1.3.
data Class = Class
    { classValue :: ClassValue
    , classOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

instance Default Class where
    def = Class def def

-- | Classification value. 3.8.1.3.
-- Unrecognized ClassValueX MUST be treated as Private.
data ClassValue
    = Public
    | Private
    | Confidential
    | ClassValueX (CI Text)
      deriving (Show, Eq, Ord, Typeable)

instance Default ClassValue where
    def = Public

-- | Date-Time Completed. 3.8.2.1.
data Completed = Completed
    { completedValue :: DateTime
    , completedOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Comment. 3.8.1.4.
data Comment = Comment
    { commentValue    :: Text
    , commentAltRep   :: Maybe URI
    , commentLanguage :: Maybe Language
    , commentOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Description. 3.8.1.5.
data Description = Description
    { descriptionValue    :: Text
    , descriptionAltRep   :: Maybe URI
    , descriptionLanguage :: Maybe Language
    , descriptionOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Geographic Position. 3.8.1.6.
data Geo = Geo
    { geoLat   :: Float
    , geoLong  :: Float
    , geoOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Location. 3.8.1.7.
data Location = Location
    { locationValue    :: Text
    , locationAltRep   :: Maybe URI
    , locationLanguage :: Maybe Language
    , locationOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Percent complete. 3.8.1.8.
data PercentComplete = PercentComplete
    { percentCompleteValue :: Int
    , percentCompleteOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Priority. 3.8.1.9.
data Priority = Priority
    { priorityValue :: Int
    , priorityOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

instance Default Priority where
    def = Priority 0 def

-- | Resources. 3.8.1.10.
data Resources = Resources
    { resourcesValue    :: Set Text
    , resourcesAltRep   :: Maybe URI
    , resourcesLanguage :: Maybe Language
    , resourcesOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Status, but only for Events. 3.8.1.11.
data EventStatus
    = TentativeEvent { eventStatusOther :: OtherParams }
    | ConfirmedEvent { eventStatusOther :: OtherParams }
    | CancelledEvent { eventStatusOther :: OtherParams }
      deriving (Show, Eq, Ord, Typeable)

-- | Status, but only for TODOs. 3.8.1.11.
data TodoStatus
    = TodoNeedsAction { todoStatusOther :: OtherParams }
    | CompletedTodo   { todoStatusOther :: OtherParams }
    | InProcessTodo   { todoStatusOther :: OtherParams }
    | CancelledTodo   { todoStatusOther :: OtherParams }
      deriving (Show, Eq, Ord, Typeable)

-- | Status, but only for Journals. 3.8.1.11.
data JournalStatus
    = DraftJournal     { journalStatusOther :: OtherParams }
    | FinalJournal     { journalStatusOther :: OtherParams }
    | CancelledJournal { journalStatusOther :: OtherParams }
      deriving (Show, Eq, Ord, Typeable)

-- | Summary. 3.8.1.12.
data Summary = Summary
    { summaryValue    :: Text
    , summaryAltRep   :: Maybe URI
    , summaryLanguage :: Maybe Language
    , summaryOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Date. 3.3.4
data Date = Date
    { dateValue :: Day
    } deriving (Show, Eq, Ord, Typeable)

-- | Date-Time value. 3.3.5.
data DateTime
    = FloatingDateTime
    { dateTimeFloating :: LocalTime
    }
    | UTCDateTime
    { dateTimeUTC      :: UTCTime
    }
    | ZonedDateTime
    { dateTimeFloating :: LocalTime
    , dateTimeZone     :: Text
    } deriving (Show, Eq, Ord, Typeable)

-- | Date-Time End. 3.8.2.2.
data DTEnd
    = DTEndDateTime
    { dtEndDateTimeValue :: DateTime
    , dtEndOther         :: OtherParams
    }
    | DTEndDate
    { dtEndDateValue     :: Date
    , dtEndOther         :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Date-Time Due. 3.8.2.3.
data Due
    = DueDateTime
    { dueDateTimeValue :: DateTime
    , dueOther         :: OtherParams
    }
    | DueDate
    { dueDateValue     :: Date
    , dueOther         :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Date-Time Start. 3.8.2.4.
data DTStart
    = DTStartDateTime
    { dtStartDateTimeValue :: DateTime
    , dtStartOther         :: OtherParams
    }
    | DTStartDate
    { dtStartDateValue     :: Date
    , dtStartOther         :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Duration value. 3.3.6.
data Duration -- TODO(?): Convert to DiffTime?
    = DurationDate
    { durSign   :: Sign -- ^ 'def' = 'Positive'
    , durDay    :: Int
    , durHour   :: Int
    , durMinute :: Int
    , durSecond :: Int
    }
    | DurationTime
    { durSign   :: Sign
    , durHour   :: Int
    , durMinute :: Int
    , durSecond :: Int
    }
    | DurationWeek
    { durSign   :: Sign
    , durWeek   :: Int
    } deriving (Show, Eq, Ord, Typeable)

-- | Sign.
data Sign = Positive | Negative
            deriving (Show, Eq, Ord, Typeable)

instance Default Sign where
    def = Positive

-- | Duration property. 3.8.2.5.
data DurationProp = DurationProp
    { durationValue :: Duration
    , durationOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

data FreeBusy = FreeBusy
    { freeBusyType    :: FBType
    , freeBusyPeriods :: Set UTCPeriod
    , freeBusyOther   :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Period of time. 3.3.9.
data Period
    = PeriodDates    DateTime DateTime
    | PeriodDuration DateTime Duration
      deriving (Show, Eq, Ord, Typeable)

-- | Period of time which must be UTC, as in FreeBusy. 3.3.9.
data UTCPeriod
    = UTCPeriodDates    UTCTime UTCTime
    | UTCPeriodDuration UTCTime Duration
      deriving (Show, Eq, Ord, Typeable)

-- | Free/Busy Time Type. 3.2.9.
-- Unrecognized FBTypeX MUST be treated as Busy.
data FBType
    = Free
    | Busy
    | BusyUnavailable
    | BusyTentative
    | FBTypeX (CI Text)
      deriving (Show, Eq, Ord, Typeable)

instance Default FBType where
    def = Busy

-- | Time Transparency. 3.8.2.7.
data TimeTransparency
    = Opaque      { timeTransparencyOther :: OtherParams }
    | Transparent { timeTransparencyOther :: OtherParams }
      deriving (Show, Eq, Ord, Typeable)

instance Default TimeTransparency where
    def = Opaque def

-- | Time Zone Identifier. 3.8.3.1.
data TZID = TZID
    { tzidValue  :: Text
    , tzidGlobal :: Bool
    , tzidOther  :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Time Zone Name. 3.8.3.2.
data TZName = TZName
    { tzNameValue    :: Text
    , tzNameLanguage :: Maybe Language
    , tzNameOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | UTC Offset. 3.3.14, 3.8.3.4, and 3.8.3.3. (unified-ish)
data UTCOffset = UTCOffset
    { utcOffsetValue :: Int -- ^ Number of seconds away from UTC
    , utcOffsetOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Time Zone URL. 3.8.3.5.
data TZUrl = TZUrl
    { tzUrlValue :: URI
    , tzUrlOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Attendee. 3.8.4.1.
data Attendee = Attendee
    { attendeeValue    :: CalAddress
    , attendeeCUType   :: CUType -- ^ 'def' = 'Individual'
    , attendeeMember   :: Set CalAddress
    , attendeeRole     :: Role -- ^ 'def' = 'ReqParticipant'
    , attendeePartStat :: PartStat -- ^ 'def' = 'PartStatNeedsAction'
    , attendeeRSVP     :: Bool
    , attendeeDelTo    :: Set CalAddress
    , attendeeDelFrom  :: Set CalAddress
    , attendeeSentBy   :: Maybe CalAddress
    , attendeeCN       :: Maybe Text
    , attendeeDir      :: Maybe URI
    , attendeeLanguage :: Maybe Language
    , attendeeOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Calendar User Type. 3.2.3.
-- Unrecognized CUTypeX MUST be treated as Unknown.
data CUType
    = Individual
    | Group
    | Resource
    | Room
    | Unknown
    | CUTypeX (CI Text)
      deriving (Show, Eq, Ord, Typeable)

instance Default CUType where
    def = Individual

-- | Role. 3.2.16.
data Role = Chair
          | ReqParticipant
          | OptParticipant
          | NonParticipant
          | RoleX (CI Text)
            deriving (Show, Eq, Ord, Typeable)

instance Default Role where
    def = ReqParticipant

-- | Participation Status. 3.2.12.
data PartStat -- Splitting requires splitting attendee too...
    = PartStatNeedsAction
    | Accepted
    | Declined
    | Tentative
    | Delegated
    | PartStatCompleted
    | InProcess
    | PartStatX (CI Text)
      deriving (Show, Eq, Ord, Typeable)

instance Default PartStat where
    def = PartStatNeedsAction

-- | Contact. 3.8.4.2.
data Contact = Contact
    { contactValue    :: Text
    , contactAltRep   :: Maybe URI
    , contactLanguage :: Maybe Language
    , contactOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Organizer. 3.8.4.3.
-- TODO: CAL-ADDRESS-related properties.
data Organizer = Organizer
    { organizerValue    :: CalAddress
    , organizerCN       :: Maybe Text
    , organizerDir      :: Maybe URI
    , organizerSentBy   :: Maybe CalAddress
    , organizerLanguage :: Maybe Language
    , organizerOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Recurrence ID. 3.8.4.4.
data RecurrenceId
    = RecurrenceIdDate
    { recurrenceIdDate  :: Date
    , recurrenceIdRange :: Range -- ^ 'def' = 'ThisAndFuture'
    , recurrenceIdOther :: OtherParams
    }
    | RecurrenceIdDateTime
    { recurrenceIdDateTime :: DateTime
    , recurrenceIdRange    :: Range -- ^ 'def' = 'ThisAndFuture'
    , recurrenceIdOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Recurrence Identifier Range. 3.2.13
data Range = ThisAndFuture | ThisAndPrior
             deriving (Show, Eq, Ord, Typeable)

instance Default Range where
    def = ThisAndFuture

-- | Related To. 3.8.4.5.
data RelatedTo = RelatedTo
    { relatedToValue :: Text
    , relatedToType  :: RelationshipType
    , relatedToOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Relationship Type. 3.2.15.
-- Unrecognized RelationshipTypeX values MUST be treated as Parent.
data RelationshipType = Parent | Child | Sibling | RelationshipTypeX (CI Text)
                        deriving (Show, Eq, Ord, Typeable)

instance Default RelationshipType where
    def = Parent

-- | Uniform Resource Locator. 3.8.4.6.
data URL = URL
    { urlValue :: URI
    , urlOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Unique Identifier. 3.8.4.7.
data UID = UID
    { uidValue :: Text
    , uidOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Exception Date-Times. 3.8.5.1.
data ExDate
    = ExDates
    { exDates     :: Set Date
    , exDateOther :: OtherParams
    }
    | ExDateTimes
    { exDateTimes :: Set DateTime
    , exDateOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Recurrence Date-Times. 3.8.5.2.
data RDate
    = RDateDates
    { rDateDates :: Set Date
    , rDateOther :: OtherParams
    }
    | RDateDateTimes
    { rDateDateTimes :: Set DateTime
    , rDateOther     :: OtherParams
    }
    | RDatePeriods
    { rDatePeriods :: Set Period
    , rDateOther   :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Frequency in recurrences. 3.3.10.
data Frequency
    = Secondly
    | Minutely
    | Hourly
    | Daily
    | Weekly
    | Monthly
    | Yearly
      deriving (Show, Eq, Ord, Typeable)

-- | Weekday, in recurrences. 3.3.10.
data Weekday = Sunday | Monday | Tuesday | Wednesday | Thursday
                      | Friday | Saturday
               deriving (Show, Eq, Ord, Bounded, Enum, Typeable)

-- | Recur value. 3.3.10.
data Recur = Recur
    { recurFreq       :: Frequency
    , recurUntilCount :: Maybe (Either (Either Date DateTime) Int)
    , recurInterval   :: Int
    , recurBySecond   :: [Int]
    , recurByMinute   :: [Int]
    , recurByHour     :: [Int]
    , recurByDay      :: [Either (Int, Weekday) Weekday]
    , recurByMonthDay :: [Int]
    , recurByYearDay  :: [Int]
    , recurByWeekNo   :: [Int]
    , recurByMonth    :: [Int]
    , recurBySetPos   :: [Int]
    , recurWkSt       :: Weekday
    } deriving (Show, Eq, Ord, Typeable)

-- | Recurrence Rule. 3.8.5.3.
data RRule = RRule
    { rRuleValue :: Recur
    , rRuleOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Repeat count. 3.8.6.2.
data Repeat = Repeat
    { repeatValue :: Integer
    , repeatOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

instance Default Repeat where
    def = Repeat 0 def

-- | Alarm Trigger Relationship. 3.2.14.
data AlarmTriggerRelationship = Start | End
                                deriving (Show, Eq, Ord, Typeable)

instance Default AlarmTriggerRelationship where
    def = Start

-- | Trigger. 3.8.6.3.
data Trigger
    = TriggerDuration
    { triggerDuration :: Duration
    , triggerRelated  :: AlarmTriggerRelationship -- ^ 'def' = 'Start'
    , triggerOther    :: OtherParams
    }
    | TriggerDateTime
    { triggerDateTime :: UTCTime
    , triggerOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Date-Time Created. 3.8.7.1.
data Created = Created
    { createdValue :: DateTime
    , createdOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Date-Time Stamp. 3.8.7.2.
data DTStamp = DTStamp
    { dtStampValue :: UTCTime
    , dtStampOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Last Modified. 3.8.7.3.
data LastModified = LastModified
    { lastModifiedValue :: UTCTime
    , lastModifiedOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Sequence number. 3.8.7.4.
data Sequence = Sequence
    { sequenceValue :: Integer
    , sequenceOther :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

instance Default Sequence where
    def = Sequence 0 def

-- | Request Status. 3.8.8.3.
data RequestStatus = RequestStatus
    { requestStatusCode     :: [Int]
    , requestStatusDesc     :: Text
    , requestStatusLanguage :: Maybe Language
    , requestStatusExt      :: Maybe Text
    , requestStatusOther    :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)

-- | Any other property.
data OtherProperty = OtherProperty
    { otherName   :: CI Text
    , otherValue  :: ByteString
    , otherParams :: OtherParams
    } deriving (Show, Eq, Ord, Typeable)