module Panes.Modals exposing (..)

import Html exposing (div, text)
import Html.Attributes as HA exposing (class)
import Html.Events as HE
import Panes exposing (..)
import Panes.Modal.Error
import Panes.Modal.FileInfo
import Panes.Modal.AddPerson
import Panes.Modal.AttributeHistory
import Panes.Modal.Upload
import Panes.Modal.AddDatabase
import Panes.Modal.ManageDatabaseAccess
import Triple exposing (Triple, triple)
import Draggable
import Panes.Controller exposing (Pane)
import PaneMeta exposing (PaneMeta)
import Data exposing (Id)
import Json.Decode exposing (bool)

type alias Modals =
    { nextId: Int
    , modals: List ModalWindow
    }

type alias ModalWindow =
    { modal: Modal
    , id: Int
    , maximize: Bool
    , collapsed: Bool
    , drag: Draggable.State ()
    , x: Float -- this is only an offset to the screen
    , y: Float -- this is only an offset to the screen
    }

type Modal
    = Error Panes.Modal.Error.Model
    | File Panes.Modal.FileInfo.Model
    | AddPerson Panes.Modal.AddPerson.Model
    | AttributeHistory Panes.Modal.AttributeHistory.Model
    | Upload Panes.Modal.Upload.Model
    | AddDatabase Panes.Modal.AddDatabase.Model
    | ManageDatabaseAccess Panes.Modal.ManageDatabaseAccess.Model

type Msg
    = Close Int
    | Focus Int
    | Maximize Int Bool
    | Collapse Int Bool
    | Drag Int Draggable.Delta
    | WrapDrag Int (Draggable.Msg ())
    | Wrap Int SubMsg

type SubMsg
    = ErrorMsg Never
    | FileMsg Panes.Modal.FileInfo.Msg
    | AddPersonMsg Panes.Modal.AddPerson.Msg
    | AttributeHistoryMsg Panes.Modal.AttributeHistory.Msg
    | UploadMsg Panes.Modal.Upload.Msg
    | AddDatabaseMsg Panes.Modal.AddDatabase.Msg
    | ManageDatabaseAccessMsg Panes.Modal.ManageDatabaseAccess.Msg

modalError = Panes.map Error ErrorMsg Panes.Modal.Error.component
modalFile = Panes.map File FileMsg Panes.Modal.FileInfo.component
modalAddPerson = Panes.map AddPerson AddPersonMsg Panes.Modal.AddPerson.component
modalAttributeHistory = Panes.map AttributeHistory AttributeHistoryMsg Panes.Modal.AttributeHistory.component
modalUpload = Panes.map Upload UploadMsg Panes.Modal.Upload.component
modalAddDatabase = Panes.map AddDatabase AddDatabaseMsg Panes.Modal.AddDatabase.component
modalManageDatabaseAccess = Panes.map ManageDatabaseAccess ManageDatabaseAccessMsg Panes.Modal.ManageDatabaseAccess.component

init : Modals
init =
    { nextId = 0
    , modals = []
    }

initFileInfo : PaneMeta -> Id -> Triple Modal (Cmd SubMsg) (List Event)
initFileInfo meta id =
    modalFile.init meta id

initAddPerson : PaneMeta -> Id -> String -> Triple Modal (Cmd SubMsg) (List Event)
initAddPerson meta interview database =
    modalAddPerson.init meta (interview, database)

initAttributeHistory : PaneMeta -> Panes.Modal.AttributeHistory.InitModel 
    -> Triple Modal (Cmd SubMsg) (List Event)
initAttributeHistory meta data =
    modalAttributeHistory.init meta data

initUpload : PaneMeta -> Id -> Maybe Id -> Triple Modal (Cmd SubMsg) (List Event)
initUpload meta person parent =
    modalUpload.init meta (person, parent)

initAddDatabase : PaneMeta -> Triple Modal (Cmd SubMsg) (List Event)
initAddDatabase meta =
    modalAddDatabase.init meta ()

initManageDatabaseAccess : PaneMeta -> String -> Triple Modal (Cmd SubMsg) (List Event)
initManageDatabaseAccess meta name =
    modalManageDatabaseAccess.init meta name
    
getCompModel : Modal -> ComponentWithModel Modal SubMsg
getCompModel modal =
    case modal of
        Error model ->
            attachModel model modalError
        File model ->
            attachModel model modalFile
        AddPerson model ->
            attachModel model modalAddPerson
        AttributeHistory model ->
            attachModel model modalAttributeHistory
        Upload model ->
            attachModel model modalUpload
        AddDatabase model ->
            attachModel model modalAddDatabase
        ManageDatabaseAccess model ->
            attachModel model modalManageDatabaseAccess

updateModal : Update Modal SubMsg
updateModal msg { meta, state } =
    case (msg, state) of
        (ErrorMsg sub, Error model) ->
            modalError.update sub
            <| PaneData meta model
        (FileMsg sub, File model) ->
            modalFile.update sub
            <| PaneData meta model
        (AddPersonMsg sub, AddPerson model) ->
            modalAddPerson.update sub
            <| PaneData meta model
        (AttributeHistoryMsg sub, AttributeHistory model) ->
            modalAttributeHistory.update sub
            <| PaneData meta model
        (UploadMsg sub, Upload model) ->
            modalUpload.update sub
            <| PaneData meta model
        (AddDatabaseMsg sub, AddDatabase model) ->
            modalAddDatabase.update sub
            <| PaneData meta model
        (ManageDatabaseAccessMsg sub, ManageDatabaseAccess model) ->
            modalManageDatabaseAccess.update sub
            <| PaneData meta model
        _ -> triple state Cmd.none []

setId : Int -> Modal -> Modal
setId id modal =
    case modal of
        AddPerson m -> AddPerson { m | modalId = Just id }
        AddDatabase m -> AddDatabase { m | modalId = Just id }
        _ -> modal

add : Modals -> Triple Modal (Cmd SubMsg) (List Event) -> Triple Modals (Cmd Msg) (List Event)
add modals =
    Triple.mapAll
        (\modal ->
            { modals
            | nextId = modals.nextId + 1
            , modals = (++) modals.modals
                <| List.singleton
                    { modal = setId modals.nextId modal
                    , id = modals.nextId
                    , maximize = False
                    , collapsed = False
                    , drag = Draggable.init
                    , x = 0
                    , y = 0
                    }
            }
        )
        (Cmd.map <| Wrap modals.nextId)
        identity

viewTaskBar : View Modals Msg
viewTaskBar { meta, state } =
    div [ class "modal-taskbar" ]
    <| List.map
        (\modal ->
            div
                [ class "modal-taskbar-entry" 
                ]
                [ div
                    [ class "modal-taskbar-title" 
                    , HA.title
                        <| (\comp -> comp.title meta)
                        <| getCompModel modal.modal
                    , HE.onClick <| Collapse modal.id False
                    ]
                    [ text
                        <| (\comp -> comp.title meta)
                        <| getCompModel modal.modal
                    ]
                , div
                    [ class "modal-taskbar-close" 
                    , HA.title "close window"
                    , HE.onClick <| Close modal.id
                    ]
                    [ Html.img
                        [ HA.src "/img/svgrepo/essential-set-2/multiply-135247.svg"
                        ] []
                    ]
                ]
        )
    <| List.filter
        (\modal -> modal.collapsed)
        state.modals

view : View Modals Msg
view { meta, state } =
    div [ class "modal" ]
    <| List.map
        (\modal ->
            div ( if modal.maximize
                    then 
                        [ class "modal-box-orientation"
                        , class "modal-box-orientation-max"
                        , HA.style "visibility" <| if modal.collapsed then "hidden" else "visible"
                        ]
                    else
                        [ class "modal-box-orientation"
                        , HA.style "visibility" <| if modal.collapsed then "hidden" else "visible"
                        , HA.style "transform"
                            <| "translate("
                            ++ String.fromFloat modal.x
                            ++ "px, "
                            ++ String.fromFloat modal.y
                            ++ "px)"
                        ]
                )
            <| List.singleton
            <| div 
                [ class "modal-box"
                , HE.onClick <| Focus modal.id
                ]
                [ div [ class "modal-title" ]
                    [ div 
                        (
                            [ class "modal-title-text"
                            , Draggable.mouseTrigger () <| WrapDrag modal.id
                            ]
                            ++ (Draggable.touchTriggers () <| WrapDrag modal.id)
                        )
                        [ text
                            <| (\comp -> comp.title meta)
                            <| getCompModel modal.modal
                        ]
                    , div
                        [ class "modal-title-hide"
                        , HA.title "Hide in Taskbar"
                        , HE.onClick <| Collapse modal.id True
                        ]
                        <| List.singleton
                        <| Html.img
                            [ HA.src "/img/svgrepo/essential-set-2/hide-84030.svg" ]
                            []
                    , div
                        [ class "modal-title-maximize"
                        , HE.onClick <| Maximize modal.id <| not modal.maximize
                        , HA.title <|
                            if modal.maximize
                            then "normalize window"
                            else "fullscreen window"
                        ]
                        <| List.singleton
                        <| Html.img
                            [ HA.src
                                <| if modal.maximize
                                    then "/img/svgrepo/essential-set-2/zoom-out-126011.svg"
                                    else "/img/svgrepo/essential-set-2/zoom-in-45597.svg"
                            ] []
                    , div 
                        [ class "modal-title-close" 
                        , HE.onClick <| Close modal.id
                        ]
                        <| List.singleton
                        <| Html.img
                            [ HA.src "/img/svgrepo/essential-set-2/multiply-135247.svg"
                            ] []
                    ]
                , div [ class "modal-body" ]
                    [ Html.map (Wrap modal.id)
                        <| (\comp -> comp.view meta)
                        <| getCompModel modal.modal
                    ]
                ]
        )
    <| state.modals

handleClosed : PaneMeta -> Triple Modals (Cmd Msg) (List Event) -> Triple Modals (Cmd Msg) (List Event)
handleClosed meta (modals, cmds, events) =
    Triple.mapSecond Cmd.batch
    <| List.foldl
        (\event (m, c, e) ->
            case event of
                Panes.CloseModal id ->
                    PaneData meta m
                    |> update (Close id)
                    |> handleClosed meta
                    |> Triple.mapAll
                        identity
                        (\x -> x :: c)
                        (\x -> x ++ e)
                _ -> (m, c, event :: e)
        )
        (triple modals [ cmds ] [])
    <| events

update : Update Modals Msg
update msg { meta, state } =
    case msg of
        Close id ->
            List.foldr
                (\modal (new, events) ->
                    if modal.id == id
                    then Tuple.pair new
                        <| (\comp -> comp.save ())
                        <| getCompModel modal.modal
                    else (modal :: new, events)
                )
                ([], [])
                state.modals
            |> Tuple.mapFirst
                (\new -> { state | modals = new })
            |> Triple.insertSecond Cmd.none
        Focus id -> triple
            { state
            | modals =
                List.filter (\x -> x.id /= id) state.modals
                ++ List.filter (\x -> x.id == id) state.modals
            }
            Cmd.none
            []
        Maximize id maximize -> triple
            { state
            | modals =
                List.map
                    (\modal ->
                        if modal.id == id
                        then { modal | maximize = maximize }
                        else modal
                    )
                    state.modals
            }
            Cmd.none
            []
        Collapse id collapse -> triple
            { state
            | modals =
                List.map
                    (\modal ->
                        if modal.id == id
                        then { modal | collapsed = collapse }
                        else modal
                    )
                    state.modals
            }
            Cmd.none
            []
        Drag id (dx, dy) -> triple
            { state
            | modals =
                List.map
                    (\modal ->
                        if modal.id == id
                        then
                            { modal
                            | x = modal.x + dx
                            , y = modal.y + dy
                            }
                        else modal
                    )
                    state.modals
            }
            Cmd.none
            []
        WrapDrag id sub ->
            Triple.insertThird []
            <| Tuple.mapBoth
                (\modals -> { state | modals = modals })
                Cmd.batch
            <| List.foldr
                (\modal (new, cmds) ->
                    if modal.id == id
                    then 
                        Draggable.update
                            (Draggable.basicConfig <| Drag id)
                            sub
                            modal
                        |> Tuple.mapBoth
                            (\x -> x :: new)
                            (\x -> x :: cmds)
                    else (modal::new, cmds)
                )
                ([], [])
                state.modals
        Wrap id sub ->
            List.foldr
                (\modal (list, cmds, events) ->
                    if modal.id == id
                    then PaneData meta modal.modal
                        |> updateModal sub
                        |> Triple.mapAll
                            (\x -> { modal | modal = x } :: list)
                            (\x -> Cmd.map (Wrap id) x :: cmds)
                            (\x -> x ++ events)
                    else (modal::list, cmds, events)
                )
                ([], [], [])
                state.modals
            |> Triple.mapFirst (\x -> { state | modals = x})
            |> Triple.mapSecond Cmd.batch
            |> handleClosed meta

apply : Apply Modals Msg
apply event { meta, state } =
    List.foldr
        (\modal (list, cmds, events) ->
            getCompModel modal.modal
            |> (\comp -> comp.apply event meta)
            |> Triple.mapAll
                (\x -> { modal | modal = x } :: list)
                (\x -> Cmd.map (Wrap modal.id) x :: cmds)
                (\x -> x ++ events)
        )
        ([], [], [])
        state.modals
    |> Triple.mapFirst (\x -> { state | modals = x})
    |> Triple.mapSecond Cmd.batch
    |> handleClosed meta

subscription : Subscription Modals Msg
subscription { meta, state } =
    Sub.batch
    <| List.concatMap
        (\modal ->
            [ Sub.map (Wrap modal.id)
                <| (\comp -> comp.subscription meta)
                <| getCompModel modal.modal
            , Draggable.subscriptions (WrapDrag modal.id) modal.drag
            ]
        )
    <| state.modals
