implementation module Incidone.OP.ContactManagementTasks
import iTasks, iTasks.Extensions.SQLDatabase, iTasks.Extensions.Web, iTasks.Extensions.Process
import Incidone.Configuration
import Incidone.OP.Concepts, Incidone.OP.SDSs, Incidone.OP.Conversions
import Incidone.OP.IncidentManagementTasks
import Incidone.Util.TaskPatterns
import Incidone.DeviceBased.VideoWall

import Incidone.Extensions.CrewLists //For demo

import qualified Data.Map as DM
import Data.Map.GenJSON
import Text, Text.HTML, Data.Either, Data.Functor
import iTasks.UI.Editor.Controls

CONVERT_BIN :== "/opt/local/bin/convert"
//CONVERT_BIN :== "/usr/bin/convert"

openContactInWorkspace :: Workspace ContactNo -> Task ()
openContactInWorkspace ws contactNo = addOnceToWorkspace ("contact-"<+++contactNo) (doOrClose (manageContactInformation ws contactNo)) ws @! ()

selectContact :: Task (Either ContactNo MMSI)
selectContact = withShared Nothing
    \sel -> (
		(selectContactFromLists sel <<@ Title "Browse")
		 -||-
         (viewContactsOnMap mapContacts sel <<@ Title "Map")
		 <<@ ArrangeWithTabs True
        )
where
    mapContacts = mapRead (\(x,y) -> x++y) (contactsOfOpenIncidentsGeo |*| contactsProvidingHelpGeo)

	selectContactFromLists :: (Shared sds (Maybe (Either ContactNo MMSI))) -> Task (Either ContactNo MMSI) | RWShared sds
	selectContactFromLists sel
		= anyTask [editSharedSelectionWithShared (Title "Involved in open incidents") False
						(SelectInTree groupByIncident select) contactsOfOpenIncidentsShort (selIds sel)
				  ,editSharedSelectionWithShared (Title "Available for help") False
						(SelectInTree groupByGroup select) contactsProvidingHelpShort (selIds sel)
                  ,editSharedSelectionWithShared (Title "All contacts") False
                        (SelectInTree groupByGroup select) allContactsShort (selIds sel)
/*
				  ,(editSharedSelectionWithShared (Title "AIS") False
						(SelectInTree ungrouped) (mapRead (sortBy (\x y -> contactTitle x < contactTitle y) o map aisToContact) allAISContacts) (Right o contactIdentity)) sel
*/				  ] <<@ (ArrangeSplit Horizontal True) @? tvHd

    fromOpenOption [{ContactShortWithIncidents|contactNo}] = contactNo

    selIds :: (Shared sds (Maybe (Either ContactNo MMSI))) -> SimpleSDSLens [Int] | RWShared sds
	selIds sel = mapReadWrite (toPrj,fromPrj) (Just \p w. Ok (toPrj w)) sel
	where
		toPrj Nothing = []
		toPrj (Just (Left contactNo)) = [contactNo]
		toPrj (Just (Right mmsi)) = [~ mmsi]

		fromPrj [s] _ = Just (Just (if (s > 0) (Left s) (Right (~s))))
		fromPrj _ _   = Just Nothing

	//Organize contacts into a tree
	groupByIncident contacts
        = [{ChoiceNode|id=c.ContactShortWithIncidents.contactNo, label=label c
					,icon=Just (contactIcon {Contact|defaultValue&type=c.ContactShortWithIncidents.type})
					,expanded=False,children=[]} \\ c <- contacts]
    where
        label {ContactShortWithIncidents|name,incidents}
            = fromMaybe "-" name +++ " (" +++ join "," [fromMaybe "-" title\\{IncidentShort|title}<-incidents] +++ ")"

    groupByGroup contacts
        = [{ChoiceNode|id=0,label=group,icon=Nothing,expanded=True,children=
			[{ChoiceNode|id=c.ContactShort.contactNo, label=fromMaybe "-" c.ContactShort.name
					,icon=Just (contactIcon c)
					,expanded=False,children=[]} \\ c <- items]} \\ (group,items) <- 'DM'.toList (foldl group 'DM'.newMap contacts)]
    where
        label {ContactShort|name} = fromMaybe "-" name
        group groups c=:{ContactShort|group}
            # g = fromMaybe "Uncategorized" group
            = 'DM'.put g (fromMaybe [] ('DM'.get g groups) ++ [c]) groups

	ungrouped contacts =
		[{ChoiceNode|id=c.ContactShort.contactNo, label=fromMaybe "-" c.ContactShort.name, icon=Just (contactIcon c),expanded=False,children=[]} \\ c <- contacts]

	select _ [0] = []
	select _ [s] = [if (s > 0) (Left s) (Right (~s))]
	select _ _   = []


manageContactInformation :: Workspace ContactNo -> Task ()
manageContactInformation ws contactNo
	= 	withHeader (viewTitle contactNo)
		(allTasks
			[manageContactBasics contactNo
			,manageContactDetails contactNo
			,manageContactPhotos contactNo
			,viewAISInfo contactNo
			,manageContactCommunication contactNo
			,manageContactIncidents ws contactNo
            ,manageContactActions False contactNo
            ,manageContactCrew` contactNo <<@ Title "Crew"
            ] <<@ ArrangeWithTabs True) @! ()
where
    viewTitle contactNo	= viewSharedTitle (mapRead contactTitle (sdsFocus contactNo contactByNo))

    manageContactCrew` contactNo = whileUnchanged (mapRead (\{Contact|type,providesHelp}->(type,providesHelp)) (sdsFocus contactNo contactByNo))
        \(type,providesHelp) -> if (type === Just Vessel && providesHelp)
            (manageContactCrew contactNo)
            (viewInformation () [] () <<@ NoUserInterface)

manageContactBasics :: ContactNo -> Task ()
manageContactBasics contactNo = (
    allTasks
        [viewAndEdit (viewContactBasics contactNo) (editContactBasics contactNo)
        ,manageContactAccess contactNo
        ]
    >^*[OnAction (Action "/Share to wall") (\_ -> Just (shareContact contactNo))
       ] @! ()) <<@ Title "General" <<@ (ApplyAttribute "icon" "basic-information")
where

    contactBasics contactNo = mapReadWrite (toPrj,fromPrj) (Just \p w. Ok (toPrj w)) (sdsFocus contactNo contactByNo)
    where
        toPrj {Contact|type,name,group,position,heading,needsHelp,providesHelp,status,notes}
            = {ContactBasic|type=type,name=name,group=group,position=position,heading=heading,needsHelp=needsHelp,providesHelp=providesHelp,status=status,notes=notes}
        fromPrj {ContactBasic|type,name,group,position,heading,needsHelp,providesHelp,status,notes} c
            = Just {Contact|c & type=type,name=name,group=group,position=position,heading=heading,needsHelp=needsHelp,providesHelp=providesHelp,status=status,notes=notes}

    viewContactBasics contactNo
        = viewSharedInformation () [] (contactBasics contactNo)

    editContactBasics contactNo
        =   (get (contactBasics contactNo)
        >>= \old ->
            updateInformation () [] old
        >>? \new -> set new (contactBasics contactNo)
        >>| logContactBasicsUpdated contactNo old new
        ) @! ()

    shareContact contactNo
        = set (WallContactSummary (Just contactNo)) wallContent @! ()

manageContactDetails :: ContactNo -> Task ()
manageContactDetails contactNo
    = whileUnchanged (mapRead (\{Contact|type} -> type) (sdsFocus contactNo contactByNo))
        \mbType -> case mbType of
            Just Person = manageDetails (sdsFocus contactNo personDetailsByNo) (logPersonDetailsUpdated contactNo)  @! ()
            Just Vessel = manageDetails (sdsFocus contactNo vesselDetailsByNo) (logVesselDetailsUpdated contactNo) @! ()
            Just Surfer = manageDetails (sdsFocus contactNo surferDetailsByNo) (logSurferDetailsUpdated contactNo) @! ()
            Just Diver  = manageDetails (sdsFocus contactNo diverDetailsByNo) (logDiverDetailsUpdated contactNo) @! ()
            Just Airplane = manageDetails (sdsFocus contactNo airplaneDetailsByNo) (logAirplaneDetailsUpdated contactNo) @! ()
            Just Helicopter = manageDetails (sdsFocus contactNo helicopterDetailsByNo) (logHelicopterDetailsUpdated contactNo) @! ()
            _               = viewInformation ("Details","No details can be entered if the contact type is unknown.") [] ()
where
    manageDetails share log = forever (
            viewSharedInformation [Att (Title "Details"),Att (Icon "details")] [] share
        >>* [OnAction (Action "/Edit") (always (editDetails share log))]
        )

    editDetails share log
        =   get share
        >>- \old -> updateInformation [Att (Title "Details (editing)"),Att (Icon "details")] [] old
        >>? \new -> set new share
        >>| log old new

manageContactPhotos :: ContactNo -> Task ()
manageContactPhotos contactNo
    =   (enterChoiceWithShared () [ChooseFromList toPrj] (sdsFocus contactNo contactPhotos)
    >^* [OnAction (Action "/Add photo") (always (addPhoto <<@ InWindow))
        ,OnAction (Action "/Delete photo") (hasValue (\p -> delPhoto p <<@ InWindow))
        ])
        <<@ Title "Photos"
        <<@ Icon "photos"
    @!  ()
where
    toPrj {original,thumb} = ATag [StyleAttr "margin: 5px; float:left",HrefAttr original.Document.contentUrl] [ImgTag [WidthAttr "200",HeightAttr "200",SrcAttr thumb.Document.contentUrl]]

    addPhoto
        =   enterInformation ("Add photo","Please select a photo to upload") []
        >>? addContactPhoto contactNo

    delPhoto photo
        =   viewInformation ("Delete photo","Do you want to delete this photo?") [ViewAs (\{original} ->original.Document.name)] photo
        >>? removeContactPhoto contactNo

manageContactCommunication :: ContactNo -> Task ()
manageContactCommunication contactNo
    = ((manageContactCommunicationMeans True contactNo) // <<@ AfterLayout (tweakUI fill)) //FIXME
       -&&-
       (viewContactCommunications contactNo) //<<@ AfterLayout (tweakUI fill)) //FIXME
      ) <<@ ArrangeWithSideBar 0 LeftSide True <<@ Title "Communication" <<@ Icon "communication"
    @! ()
where
    viewContactCommunications contactNo
        = enterChoiceWithShared (Title "Communication history") [] (sdsFocus contactNo contactCommunications)

manageContactCommunicationMeans :: Bool ContactNo -> Task CommunicationMean
manageContactCommunicationMeans compact contactNo = forever (
		enterInformation "FIXME" []

//        editSelectionWithShared (Title "Communication means") [SelectInTree group] (sdsFocus contactNo contactCommunicationMeans) @? tvHd
    >^* [OnAction ActionAdd  (always (addMean contactNo <<@ InWindow @! ()))
        ,OnAction ActionEdit (hasValue (\{CommunicationMean|id} -> editMean id <<@ InWindow @! ()))
        ,OnAction ActionRemove (hasValue (\{CommunicationMean|id} -> removeMean id))
        ]
    )
where
    ActionAdd = Action (if compact "Add" "/Add")
    ActionEdit = Action (if compact "Edit" "/Edit")
    ActionRemove = Action (if compact "Remove" "/Remove")

    group means expanded = [] /*[{ChoiceTree|label=label m,icon=Just (icon m.CommunicationMean.type),value=ChoiceNode i,type=LeafNode} \\ (i,m) <- means]
    where
        label {CommunicationMean|type=CMPhone,phoneNo} = fromMaybe "-" phoneNo
        label {CommunicationMean|type=CMVHF,callSign,mmsi} = fromMaybe "-" callSign +++ " / " +++ maybe "-" toString mmsi
        label {CommunicationMean|type=CMEmail,emailAddress} = maybe "-" toSingleLineText emailAddress
        label {CommunicationMean|type=CMP2000,capCode} = fromMaybe "-" capCode

        icon CMPhone = "phone-call"
        icon CMVHF = "vhf-call"
        icon CMEmail = "e-mail-message"
        icon CMP2000 = "p2000-message"
*/

    addMean contactNo
        =   enterInformation (Title "Add communication mean") [EnterAs toNewCommunicationMean]
        >>? createCommunicationMean contactNo

    editMean id
        = get (sdsFocus id communicationMeanById)
        >>- \c=:{CommunicationMean|type,phoneNo,callSign,mmsi,emailAddress,capCode} -> case type of
            CMPhone -> updateInformation (Title "Edit telephone details") [] {TelephoneDetails|phoneNo=phoneNo}
                       @ \{TelephoneDetails|phoneNo} -> {CommunicationMean|c & phoneNo = phoneNo}
            CMVHF   -> updateInformation (Title "Edit VHF details") [] {VHFRadioDetails|callSign=callSign,mmsi=mmsi}
                       @ \{VHFRadioDetails|callSign,mmsi} -> {CommunicationMean|c & callSign=callSign, mmsi=mmsi}
            CMEmail -> updateInformation (Title "Edit e-mail details") [] {EmailAccountDetails|emailAddress=emailAddress}
                       @ \{EmailAccountDetails|emailAddress} -> {CommunicationMean|c & emailAddress = emailAddress}
            CMP2000 -> updateInformation (Title "Edit P2000 details") [] {P2000ReceiverDetails|capCode = capCode}
                       @ \{P2000ReceiverDetails|capCode} -> {CommunicationMean|c & capCode = capCode}
        >>? \new ->
            set new (sdsFocus id communicationMeanById)

    removeMean id = deleteCommunicationMean id

manageContactActions :: Bool ContactNo -> Task ()
manageContactActions compact contactNo
	=	if compact selectActions selectAndWorkOnActions
    >^* [OnAction ActionAdd (always (addTopActionItem [contactNo] [] @! ()))
        ,OnAction ActionEdit (hasValue (\a -> editActionItem a @! ()))
        ]
	@!  ()
where
    ActionAdd = Action (if compact "Add" "/Add action")
    ActionEdit = Action (if compact "Edit" "/Edit action")

    title = (Title (if compact "Actions" "Overview"))
    selectActions
        = chooseActionItem title False False (sdsFocus contactNo actionStatusesByContact) // <<@ AfterLayout (tweakUI fill) //FIXME

    selectAndWorkOnActions
        = ( chooseActionItem title False True (sdsFocus contactNo actionStatusesByContact) // <<@ AfterLayout (tweakUI fill)) //FIXME
            >&> \s -> whileUnchanged s
                (\t -> case t of
                Just taskId    = workOnActionItem taskId @! taskId
                Nothing        = viewInformation () [] () @? const NoValue
                )
          ) <<@ (ArrangeWithSideBar 0 LeftSide True) <<@ (Icon "actions") <<@ (Title "Actions")

manageContactIncidents :: Workspace ContactNo -> Task ()
manageContactIncidents ws contactNo
    =	feedForward choose
    (	\sel ->
        withSelection viewNoSelection viewIncidentDetails sel
        -&&-
        doAddRemoveOpen (add <<@ InWindow) (\c -> (remove c) <<@ InWindow) (\c -> doOrClose (open c)) ws sel
    )	<<@ (ArrangeWithSideBar 1 RightSide True) <<@ (Icon "incidents") <<@ (Title "Incidents")
    @! ()
where
    incidents   =   sdsFocus contactNo incidentsByContactDetails
    choose		=	enterChoiceWithSharedAs () [] incidents incidentDetailsIdentity
    open sel	=	manageIncidentInformation ws sel
    add			=	enterChoiceWithSharedAs ("Add contact to incident","Select an incident to add this contact to") [] allIncidentsShort incidentShortIdentity
                >>? \i ->
                    upd (\is -> [incidentNo \\{IncidentDetails|incidentNo} <-is] ++ [i]) incidents
                >>| logContactAdded i contactNo
    remove sel	=	viewSharedInformation ("Remove contact from incident","Are your sure you want this contact to be removed from this incident?") [] (sdsFocus sel incidentTitleByNo)
                >>* [OnAction ActionNo (always (return ()))
                    ,OnAction ActionYes (always (upd (\is -> [ incidentNo \\ {IncidentDetails|incidentNo} <- is | incidentNo <> sel]) incidents >>| logContactRemoved sel contactNo))
                    ]

	viewNoSelection = return () //FIXME
    incidentShortIdentity {IncidentShort|incidentNo} = incidentNo
    incidentDetailsIdentity {IncidentDetails|incidentNo} = incidentNo


viewAISInfo :: ContactNo -> Task ()
viewAISInfo contactNo = whileUnchanged (sdsFocus contactNo contactMMSI)
    \mbMMSI -> case mbMMSI of
        Nothing = viewInformation () [] () <<@ NoUserInterface
        Just mmsi = viewSharedInformation (Icon "ais","AIS","Latest AIS data") [] (sdsFocus mmsi AISContactByMMSI) @! ()

manageContactAccess :: ContactNo -> Task ()
manageContactAccess contactNo = (
        viewAndEdit (viewContactAccess contactNo) (editContactAccess contactNo)
    @!  ()
    ) <<@ Title "Partner access" <<@ Icon "access"
where
    access = sdsFocus contactNo contactAccess

    viewContactAccess contactNo
        = viewSharedInformation () [ViewAs view] access
    where
        view {ContactAccess|account=Nothing} = "This contact can not log in to Incidone"
        view {ContactAccess|account=Just {Credentials|username=Username uname}}
            = "This contact can log in to Incidone as user '"+++uname+++"'"

    editContactAccess contactNo
        =   get access
        >>- \original ->
            ( (Label "Account" @>> updateInformation () [] original.ContactAccess.account)
              -&&-
              (Label "Access level" @>> updateChoice () [ChooseFromCheckGroup viewLevel] [PartnerAccess,WOAccess] (fromMaybe PartnerAccess original.ContactAccess.access))
            )
        >>? \(updatedAccount,updatedAccess) ->
            set {ContactAccess|account=updatedAccount,access=Just updatedAccess} access

    viewLevel WOAccess = "Watch Officer access"
    viewLevel PartnerAccess = "Partner access"

viewContactDetails :: ContactNo -> Task ()
viewContactDetails contactNo
	= withHeader (viewSharedTitle (mapRead contactTitle contact))
	( viewPhoto contact
	  -&&-
	  viewSharedInformation () [] (mapRead contactDetails contact)
      -&&-
      viewContactCommunicationMeans contactNo
      -&&-
      viewSharedInformation "Actions:" [ViewAs viewActions] (sdsFocus contactNo actionStatusesByContact)
	) @! ()
where
	contact = sdsFocus contactNo contactByNo

	viewPhoto contact
		= viewSharedInformation () [ViewAs contactThumbHtml] contact

    viewActions items = case [title \\ (_,_,{ActionStatus|title}) <- items] of
        []      = ["There are no actions for this contact"]
        titles  = titles

viewContactCommunicationMeans :: ContactNo -> Task [CommunicationMean]
viewContactCommunicationMeans contactNo
    = viewSharedInformation "Communication means:" [ViewAs viewComms] (sdsFocus contactNo contactCommunicationMeans)
where
    viewComms items = TableTag [] (map viewComm items)
    where
        viewComm {CommunicationMean|type=CMPhone,phoneNo} = commRow "phone-call" ("Telephone :" +++ fromMaybe "-" phoneNo)
        viewComm {CommunicationMean|type=CMVHF,callSign,mmsi} = commRow "vhf-call" ("Callsign: " +++ fromMaybe "-" callSign +++ " / MMSI: " +++ maybe "-" toString mmsi)
        viewComm {CommunicationMean|type=CMEmail,emailAddress} = commRow "e-mail-message" ("E-mail: " +++ maybe "-" toSingleLineText emailAddress)
        viewComm {CommunicationMean|type=CMP2000,capCode} = commRow "p2000-message" ("P2000: " +++ fromMaybe "-" capCode)

        commRow icon label
            = TrTag [] [TdTag [] [DivTag [StyleAttr "width:16px;height:16px;",ClassAttr ("icon-"+++icon)] []],TdTag [] [Text label]]


viewAISContactDetails :: MMSI -> Task ()
viewAISContactDetails mmsi
    = withHeader (viewTitle (toString mmsi))
        (viewSharedInformation () [ViewAs (fmap aisToDetails)] (sdsFocus mmsi AISContactByMMSI)
         -&&-
         viewVesselWebLinks mmsi
        ) @! ()

viewContactHeader :: ContactNo -> Task ()
viewContactHeader contactNo
    = viewSharedInformation () [ViewAs toView] (sdsFocus contactNo contactByNo) @! ()
where
    toView c=:{Contact|photos,name}
        = DivTag [] [ImgTag [StyleAttr "float: left; margin-right: 5px":attributes]
                    ,H1Tag [StyleAttr "font-size: 30px; font-weight: normal"] [Text (contactTitle c)]
                    ]
    where
        (ImgTag attributes) = contactAvatarHtml c

viewVesselWebLinks :: MMSI -> Task ()
viewVesselWebLinks mmsi
    =   get webLinksConfig
    >>- \webLinks -> if (isEmpty webLinks.vesselLinks)
        (return ())
        (viewInformation "Find on the web" [ViewAs toLinks] webLinks.vesselLinks @! ())
where
    toLinks links
        = UlTag [] [LiTag [] [ATag [TargetAttr "_blank", HrefAttr (replaceSubString "{mmsi}" (toString mmsi) url)] [Text title]]\\{WebLink|title,url=URL url} <- links]

updateContactPosition :: ContactNo -> Task (Maybe (Maybe ContactPosition))
updateContactPosition contactNo
    =   get (sdsFocus contactNo contactByNo |*| standardMapLayers)
    >>- \({Contact|name,type,position},baseLayers) ->
        withShared (position,initPerspective position)
        \tmpInfo ->
        (updateSharedInformation ("Position update","Update position of contact "<+++ name) [UpdateAs fst (\(_,y) x -> (x,y))] tmpInfo
         -||-
         updateSharedInformation () [UpdateAs (toMap baseLayers) (fromMap baseLayers)] tmpInfo
         -||-
         viewSharedInformation "Search the web" [ViewAs (toSearchURLs o fst)] tmpInfo
        ) @ fst
    >>? \newPosition ->
        upd (\c -> {Contact|c&position=newPosition}) (sdsFocus contactNo contactByNo)
    >>| logContactPositionUpdated contactNo position newPosition
    @!  newPosition
where
	initPerspective position = {ContactMapPerspective|defaultValue & center = fromMaybe defaultValue.ContactMapPerspective.center contactPos, zoom = 7, cursor = contactPos}
    where
        contactPos = maybe Nothing latLng position

    toMap baseLayers (pos,perspective)
        = toLeafletMap (contactMap baseLayers (pos,perspective))
    fromMap baseLayers (pos,perspective) leafletMap
        # {ContactMap|perspective} = fromLeafletMap (contactMap baseLayers (pos,perspective)) leafletMap
        = case perspective.ContactMapPerspective.cursor of
            (Just cursor)   = (Just (PositionLatLng cursor),perspective)
            _               = (pos, perspective)

    fromMap _ info _ = info

    contactMap baseLayers (pos,perspective)
        = {ContactMap|defaultValue & perspective = {ContactMapPerspective|perspective & cursor = maybe Nothing latLng pos},layers = baseLayers}

    toSearchURLs Nothing    = UlTag [] []
    toSearchURLs (Just pos) = UlTag [] [LiTag [] [ATag [TargetAttr "_blank", HrefAttr ("http://maps.google.com/?q="+++toSingleLineText pos)] [Text "Search with Google Maps"]]
                                       ,LiTag [] [ATag [TargetAttr "_blank", HrefAttr ("http://www.bing.com/maps/?q="+++toSingleLineText pos)] [Text "Search with Bing Maps"]]
                                       ]

updateContactStatus :: ContactNo -> Task (Maybe (Maybe ContactStatus))
updateContactStatus contactNo
    =   get (sdsFocus contactNo contactByNo)
    >>- \{Contact|status} ->
        updateInformation (Title "Status") [] status
    >>? \newStatus ->
        upd (\c -> {Contact|c&status = newStatus}) (sdsFocus contactNo contactByNo)
    >>| logContactStatusUpdated contactNo status newStatus
    @!  newStatus

updateSharedContactRefList :: d (Shared sds [ContactNo]) -> Task [ContactNo] | toPrompt d & RWShared sds
updateSharedContactRefList d refs
    =   manageCurrentItems
    >^* [OnAction (Action "Add") (always (addItem <<@ InWindow))]
where
    manageCurrentItems
        = updateSharedInformation d [UpdateAs toPrj fromPrj] items @ map contactIdentity
    where
        items = sdsDeref refs id contactsByNosShort (\_ cs -> cs)
        toPrj l = [(contactIdentity c,contactTitle c) \\ c <-l]
        fromPrj _ items = map fst items

    addItem
        =   selectKnownOrDefineNewContact
        >>? (\def -> createContactIfNew def >>- \contactNo -> upd (\r -> r ++ [contactNo]) refs)

selectKnownOrDefineNewContact :: Task (Either ContactNo NewContact)
selectKnownOrDefineNewContact
    = oneOrAnother ("Add contact...","You can either select a know contact, or define a new one.")
        ("Known contact", chooseKnownContact)
        ("Add new contact",enterNewContact)
where
    chooseKnownContact
        = enterChoiceWithSharedAs () [ChooseFromDropdown id] allContactsShort contactIdentity
    enterNewContact
        = enterInformation () []

createContactIfNew :: (Either ContactNo NewContact) -> Task ContactNo
createContactIfNew (Left no) = return no
createContactIfNew (Right contact) = createContact contact

createContact :: NewContact -> Task ContactNo
createContact {NewContact|type,name,position,needsHelp}
	=   get databaseDef
	>>- \db -> sqlExecute db ["allContacts"] (
		execInsert "INSERT INTO Contact (`type`,`name`,`position_lat`,`position_lon`,`position_desc`,`needsHelp`) VALUES (?,?,?,?,?,?)"
			(flatten
            [mbToSQL type
			,mbToSQL name
			,mbToSQL position
			,toSQL   needsHelp
			]))

deleteContact :: ContactNo -> Task ()
deleteContact contactNo
	=		delete contactNo
	-&&-	upd (\m -> 'DM'.put contactNo [] m) (sdsFocus (Just [contactNo]) incidentNosByContactNosIndexed)
	-&&-	upd (\m -> 'DM'.put contactNo [] m) (sdsFocus (Just [contactNo]) communicationNosByContactNosIndexed)
	@!  ()
where
	delete :: ContactNo -> Task ()
	delete contactNo
		=	get databaseDef
		>>= \db -> sqlExecute db ["allContacts"] (execDelete "DELETE FROM Contact WHERE contactNo = ?" (toSQL contactNo)) @! ()

addContactPhoto :: ContactNo Document -> Task ContactPhoto
addContactPhoto contactNo original
    =   withTemporaryDirectory
        \tmp ->
        exportDocument (tmp</>"orig.jpg") original
    >-|
        callProcess "Creating thumbnail..." [] CONVERT_BIN
            ["-define","jpeg:size=400x400",(tmp</>"orig.jpg"),"-thumbnail","200x200^","-gravity","center","-extent","200x200",(tmp</>"thumb.png")] Nothing Nothing
    >-|
        importDocument (tmp</>"thumb.png")
    >>- \thumb ->
        callProcess "Creating avatar..." [] CONVERT_BIN
            ["-define","jpeg:size=100x100",(tmp</>"orig.jpg"),"-thumbnail","50x50^","-gravity","center","-extent","50x50",(tmp</>"avatar.png")] Nothing Nothing
    >-|
        importDocument (tmp</>"avatar.png")
    >>- \avatar -> let photo = {ContactPhoto|original = original, thumb = thumb, avatar = avatar} in
        upd (\photos -> 'DM'.put contactNo [photo:fromMaybe [] ('DM'.get contactNo photos)] photos) allContactPhotos
    >-|
        logContactPhotoAdded contactNo photo
    @!  photo

removeContactPhoto :: ContactNo ContactPhoto -> Task ContactPhoto
removeContactPhoto contactNo photo
    =  upd (\photos -> 'DM'.put contactNo (removePhoto photo (fromMaybe [] ('DM'.get contactNo photos))) photos) allContactPhotos
    @! photo
where
    removePhoto photo photos = [p \\ p <- photos | p =!= photo]

createCommunicationMean :: ContactNo NewCommunicationMean -> Task CommunicationMeanId
createCommunicationMean contactNo mean=:{NewCommunicationMean|type,phoneNo,callSign,mmsi,emailAddress,capCode}
    =   get databaseDef
    >>- \db ->
        sqlExecute db ["allCommunicationMeans"] (execInsert "INSERT INTO CommunicationMean (type) VALUES (?)" (toSQL type))
    >>- \id ->
        sqlExecute db [] (execInsert "INSERT INTO communicationMeans1_communicationMeans2 (communicationMeans1,communicationMeans2) VALUES (?,?)" (toSQL id ++ toSQL contactNo))
    >-| case type of
        CMPhone = sqlExecute db [] (execInsert "INSERT INTO Telephone (id,phoneNo) VALUES (?,?)" (toSQL id ++ mbToSQL phoneNo))
        CMVHF   = sqlExecute db [] (execInsert "INSERT INTO VHFRadio (id,callSign,mmsi) VALUES (?,?,?)" (toSQL id ++ mbToSQL callSign ++ mbToSQL mmsi))
        CMEmail = sqlExecute db [] (execInsert "INSERT INTO EmailAccount (id,emailAddress) VALUES (?,?)" (toSQL id ++ mbToSQL emailAddress))
        CMP2000 = sqlExecute db [] (execInsert "INSERT INTO P2000Receiver (id,capCode) VALUES (?,?)" (toSQL id ++ mbToSQL capCode))

deleteCommunicationMean :: CommunicationMeanId -> Task ()
deleteCommunicationMean id
    =   get databaseDef
    >>- \db ->
        sqlExecute db ["allCommunicationMeans"] (execDelete "DELETE FROM communicationMeans1_communicationMeans2 WHERE communicationMeans1 = ? " (toSQL id))
    >-|
        allTasks [sqlExecute db [] (execDelete ("DELETE FROM " +++ table +++" WHERE id = ? ") (toSQL id)) \\ table <-
                    ["CommunicationMean","Telephone","VHFRadio","EmailAccount","P2000Receiver"]]
    @!  ()

updatePosition :: ContactPosition String (Shared sds Contact) -> Task Contact | RWShared sds
updatePosition newposition src contact
	= upd (update newposition src) contact
where
	update newposition src contact=:{Contact|position}
		= {Contact|contact & position = Just newposition}

verifyContactCredentials :: Credentials -> Task (Maybe User)
verifyContactCredentials credentials
    = get (sdsFocus credentials contactByCredentials)
    @ fmap contactUser

viewContactsOnMap :: (sds1 () [ContactGeo] w) (Shared sds2 (Maybe (Either ContactNo MMSI))) -> Task (Either ContactNo MMSI) | iTask w & RWShared sds1 & RWShared sds2
viewContactsOnMap sharedContacts sel
   =   get (standardMapLayers |*| standardPerspective)
   >>- \(baseLayers,perspective) ->
       withShared (False,perspective)
       \localState ->
            updateSharedInformation "Show AIS contacts:" [UpdateAs fst (\(_,y) x -> (x,y))] localState
            ||-
            (updateSharedInformation () [UpdateAs (toPrj baseLayers) fromPrj] (mapState localState sharedContacts sel)) @ (\(a,b,c) -> (b,c))
            >^* [OnAction (Action "/Share map to wall") (hasValue sharePerspective)
                ]
        @? selection
where
    mapState :: (Shared sds1 (Bool,ContactMapPerspective))
                (sds2 () [ContactGeo] w)
                (Shared sds3 (Maybe (Either ContactNo MMSI)))  ->
                SDSSequence () ([(Bool,ContactGeo)], Maybe (Either ContactNo MMSI), ContactMapPerspective) (Maybe (Either ContactNo MMSI), ContactMapPerspective) | iTask w & RWShared sds1 & RWShared sds2 & RWShared sds3
    mapState local contacts sel = sdsSequence "mapState" id (\_ r -> r) (\_ _ -> Right read) writel writer (local >*< sel) mapContacts
    where
        mapContacts = sdsSelect "mapContacts" choose (SDSNotifyConst (\_ _ _ _-> False)) (SDSNotifyConst (\_ _ _ _-> False)) withoutAISContacts withAISContacts
        where
            choose ((withAIS,{ContactMapPerspective|bounds=Just bounds}),_) = (if withAIS (Right bounds) (Left bounds))
            choose _                                                        = (Left defaultValue)

            baseContacts = mapRead (\cs -> [(False,c) \\ c<-cs]) (toReadOnly contacts)
            aisContacts = mapRead (\cs -> [(True,c)\\c=:{ContactGeo|position=Just position}<-map aisToContactGeo cs]) (toReadOnly boundedAISContacts)

            withoutAISContacts = sdsFocus () baseContacts
            withAISContacts = mapRead (\(a,b) -> a++b) (aisContacts |*| sdsFocus () baseContacts)

        read (((showAis,perspective),mbSel),contacts) = (contacts,mbSel,perspective)
        writel = SDSWrite (\_ ((showAis,_),_) (mbSel,perspective) -> Ok (Just ((showAis,perspective),mbSel)))
        writer = SDSWriteConst (\_ _ -> Ok Nothing)

    toPrj baseLayers (contacts,sel,perspective)
        = toLeafletMap {ContactMap|perspective=perspective,layers=[{title="Contacts",def=CMMarkersLayer (toMarkers sel contacts)}:baseLayers]}

    fromPrj (contacts,sel,_) map=:{LeafletMap|perspective}
        = (maybe sel Just (updateSelection (selectionFromLeafletMap map)),fromLeafletPerspective perspective)

	selection (Value (Just no,_) stable)	= Value no stable
	selection _								= NoValue

    sharePerspective (_,perspective) = set (WallOverview perspective) wallContent @! ()

    toMarkers sel contacts
        = [contactGeoToMapMarker ais (isSelected contactNo sel) c \\ (ais,c=:{ContactGeo|contactNo,name=Just _,position=Just _}) <- contacts]

    isSelected contactNo (Just (Left no)) = no == contactNo
    isSelected contactNo (Just (Right no)) = no == contactNo
    isSelected _ _ = False

    updateSelection [] = Nothing
	updateSelection [LeafletObjectID markerId:ms]
        | startsWith "c" markerId   = Just (Left (toInt (subString 1 (textSize markerId) markerId)))
        | startsWith "a" markerId   = Just (Right (toInt (subString 1 (textSize markerId) markerId)))
                                    = updateSelection ms

	findContactNo title contacts = case [(isAis,contactNo) \\ (isAis,{ContactGeo|contactNo,name}) <- contacts | name == title] of
		[(False,contactNo)] = Just (Left contactNo)
		[(True,mmsi)]   = Just (Right mmsi)
		_			    = Nothing

