module Editor.Attributes exposing (..)

import Data exposing (Id, Attribute)
import Editor exposing (Component)
import Data.Schema exposing (Role)
import Dict exposing (Dict)
import Time exposing (Zone, Posix)
import Json.Decode as JD
import Iso8601
import Html exposing (Html, div, text)
import Html.Attributes as HA exposing (class)
import Html.Events as HE
import Network.Messages exposing (EditDeniedData)
import Panes exposing (Event)
import Triple exposing (triple, Triple)

type alias Editor =
    { editor: Dict String AttrEditor
    , person: Id
    , schema: Role
    }

type alias AttrEditor =
    { component: Component
    , data: Maybe Attribute
    , key: String
    , schema: Data.Schema.Attribute
    }

type Msg
    = Wrap String Editor.EditorMsg
    | SendEvent Panes.Event
    | None

logFail : String -> b -> Maybe a -> Maybe a
logFail suffix log data =
    let
        d_ = case data of
            Just _ -> log
            Nothing -> Debug.log ("Editor.Attributes:logFail:" ++ suffix) log
    in data

init : Maybe String -> Zone -> Posix 
    -> Id -> List Attribute -> Role -> Editor
init zoneName zone now person attributes schema =
    { editor = 
        fixMissing
            (initComponent zoneName zone now person Nothing)
            schema
        <| Dict.fromList
        <| List.filterMap
            (\attribute ->
                Maybe.map
                    (Tuple.pair attribute.key)
                <| Maybe.andThen
                    (initComponent
                        zoneName zone now
                        person
                        (Just attribute)
                        attribute.key
                    )
                <| Dict.get attribute.key schema.attributes
            )
        <| attributes
    , person = person
    , schema = schema
    }

initSingle : Maybe String -> Zone -> Posix 
    -> Id -> Attribute -> Data.Schema.Attribute -> Editor
initSingle zoneName zone now person data schema =
    { editor =
        Maybe.withDefault Dict.empty
        <| Maybe.map (Dict.singleton data.key)
        <| initComponent
            zoneName zone now
            person (Just data) data.key
            schema
    , person = person
    , schema =
        { name = ""
        , abstract = False
        , parent = Nothing
        , attributes = Dict.singleton data.key schema
        }
    }

fixMissing : (String -> Data.Schema.Attribute -> Maybe AttrEditor)
    -> Role -> Dict String AttrEditor
    -> Dict String AttrEditor
fixMissing mapper role existing =
    role.attributes
    |> Dict.filter
        (\x _ -> not <| Dict.member x existing)
    |> Dict.foldl
        (\key schema result ->
            mapper key schema
            |> logFail "fixMissing:mapper" (key, schema)
            |> Maybe.map
                (\x -> Dict.insert key x result)
            |> Maybe.withDefault result
        )
        existing

initComponent : Maybe String -> Zone -> Posix
    -> Id -> Maybe Attribute -> String -> Data.Schema.Attribute -> Maybe AttrEditor
initComponent zoneName zone now person data key schema =
    Maybe.map
        (\comp ->
            { component = comp
            , data = data
            , key = key
            , schema = schema
            }
        )
    <| case schema.type_ of
        "DateTime" ->
            Just
            <| Editor.initDateTime
                (Editor.TargetAttribute person key)
                zoneName
                zone
                now
            <| Maybe.withDefault now
            <| Maybe.andThen
                ( Result.toMaybe
                    << JD.decodeValue Iso8601.decoder
                    << .value
                    << .entry
                )
            <| data
        "SingleLine" ->
            Just
            <| Editor.initSingleLine
                (Editor.TargetAttribute person key)
            <| Maybe.withDefault ""
            <| Maybe.andThen
                ( Result.toMaybe
                    << JD.decodeValue JD.string
                    << .value << .entry
                )
            <| data
        "MultiLine" ->
            Just
            <| Editor.initMultiLine
                (Editor.TargetAttribute person key)
            <| Maybe.withDefault ""
            <| Maybe.andThen
                ( Result.toMaybe
                    << JD.decodeValue JD.string
                    << .value << .entry
                )
            <| data
        "Number" ->
            Just
            <| Editor.initNumber
                (Editor.TargetAttribute person key)
            <| Maybe.withDefault 0
            <| Maybe.andThen
                ( Result.toMaybe
                    << JD.decodeValue JD.float
                    << .value << .entry
                )
            <| data
        _ -> Nothing

view : Bool -> Posix -> Editor -> Html Msg
view readonly now model =
    div [ class "utils-edit-grid" ]
    <| List.map
        (\(key, editor) ->
            div [ class "utils-edit-grid-row" ]
                [ div [ class "utils-edit-grid-name" ]
                    [ text key 
                    , if editor.schema.protected
                        then Html.img
                            [ HA.src "/img/svgrepo/essential-set-2/locked-22140.svg"
                            , HA.title <| "This is a protected attribute and is only "
                                ++ "available as long as the confidential database is "
                                ++ "attached"
                            ] []
                        else text ""
                    ]
                , div [ class "utils-edit-grid-cell" ]
                    [ Html.map (Wrap key)
                        <| Editor.view readonly now editor.component
                    ]
                , div [ class "utils-edit-grid-status" ]
                    <| List.filterMap identity
                    -- [ if editor.schema.protected
                    --     then Just
                    --         <| div [ class "utils-edit-grid-status-protected" ]
                    --             [ Html.img
                    --                 [ HA.src "/img/svgrepo/essential-set-2/locked-22140.svg"
                    --                 , HA.title <| "This is a protected attribute and is only "
                    --                     ++ "available as long as the confidential database is "
                    --                     ++ "attached"
                    --                 ] []
                    --             ]
                    --     else Nothing
                    [ if editor.schema.history && editor.data /= Nothing
                        then Just
                            <| div 
                                [ class "utils-edit-grid-status-history"
                                , HE.onClick <| 
                                    case editor.data of
                                        Nothing -> None
                                        Just data ->
                                            SendEvent <| Panes.AttributeHistory
                                                { person = model.person
                                                , data = data
                                                , schema = editor.schema
                                                }
                                ]
                                [ Html.img
                                    [ HA.src "/img/svgrepo/interaction-set/archive-149430.svg"
                                    , HA.title <| "View history"
                                    ] []
                                ]
                        else Nothing
                    , if editor.data == Nothing
                        then Just
                            <| div [ class "utils-edit-grid-status-new" ]
                                [ Html.img
                                    [ HA.src "/img/svgrepo/interaction-set/archive-69514.svg"
                                    , HA.title <| "New"
                                    ] []
                                ]
                        else Nothing
                    ]
                ]
        )
    <| Dict.toList model.editor

viewSingle : Bool -> Posix -> Editor -> Html Msg
viewSingle readonly now model =
    Maybe.withDefault
        (div [ class "editor-attr-no-editor" ]
            [ text "No Editor to show" ]
        )
    <| Maybe.map
        (\(key, editor) ->
            Html.map (Wrap key)
                <| Editor.view readonly now editor.component
        )
    <| List.head
    <| Dict.toList model.editor

setDenied : String -> EditDeniedData -> Editor -> Editor
setDenied key denied model =
    { model
    | editor = Dict.update key
        (Maybe.map 
            (\x ->
                { x | component = Editor.setDenied denied x.component }
            )
        )
        model.editor
    }

update : Posix -> Msg -> Editor -> Triple Editor (Cmd Msg) (List Event)
update now msg model =
    case msg of
        None -> triple model Cmd.none []
        SendEvent event -> triple model Cmd.none [ event ]
        Wrap key sub ->
            Dict.get key model.editor
            |> Maybe.map
                (\x -> Editor.update now sub x.component
                    |> Triple.mapFirst
                        (\y -> { x | component = y })
                )
            |> Maybe.map
                (Triple.mapAll
                    (\x ->
                        { model | editor = Dict.insert key x model.editor }
                    )
                    (Cmd.map <| Wrap key)
                    identity
                )
            |> Maybe.withDefault
                (triple model Cmd.none [])

save : Editor -> List Event
save { editor } =
    Dict.foldl
        (\_ { component } list ->
            Editor.save component ++ list
        )
        []
        editor
