module Main exposing (..) import Browser exposing (Document) import Card exposing (CardData) import Database import Draft exposing (Draft) import Html exposing (Html, button, div, img, span, text) import Html.Attributes exposing (alt, class, classList, disabled, src) import Html.Events as Events exposing (onClick) import Html.Keyed as Keyed import Round import Zipper main = Browser.document { init = init , update = update , view = view , subscriptions = \_ -> Sub.none } type Model = Ready ReadyModel | Error ErrorModel type FocusStat = FocusALSA | FocusALPA | FocusGIHWR | FocusPickRate type alias ReadyModel = { draft : Draft , database : Database.Database , highlighted : Maybe Draft.PickCard , flipHighlighted : Bool , focusStat : FocusStat } type alias ErrorModel = { error : String } init : { setData : String, draftData : String } -> ( Model, Cmd Msg ) init flags = case ( Draft.decode flags.draftData, Database.decode flags.setData flags.draftData ) of ( Ok draftData, Ok database ) -> ( Ready { draft = draftData , database = database , highlighted = Nothing , flipHighlighted = False , focusStat = FocusPickRate } , Cmd.none ) ( Err err, Ok _ ) -> ( Error { error = "Error decoding draft data: " ++ err }, Cmd.none ) ( Ok _, Err err ) -> ( Error { error = "Error decoding set data: " ++ err }, Cmd.none ) ( Err draftError, Err databaseError ) -> ( Error { error = "Error decoding draft data: " ++ draftError ++ ", set data: " ++ databaseError }, Cmd.none ) type Msg = Increment | Decrement | Highlight Draft.PickCard | FlipHighlightedCard | SetFocusStat FocusStat update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case model of Ready mdl -> case msg of Increment -> ( Ready { mdl | draft = Zipper.moveRight mdl.draft }, Cmd.none ) Decrement -> ( Ready { mdl | draft = Zipper.moveLeft mdl.draft }, Cmd.none ) Highlight card -> ( Ready { mdl | highlighted = Just card , flipHighlighted = False } , Cmd.none ) FlipHighlightedCard -> ( Ready { mdl | flipHighlighted = not mdl.flipHighlighted }, Cmd.none ) SetFocusStat stat -> ( Ready { mdl | focusStat = stat }, Cmd.none ) Error mdl -> ( Error mdl, Cmd.none ) view : Model -> Document Msg view model = { title = "Drafter" , body = [ case model of Ready mdl -> viewReady mdl Error mdl -> viewError mdl ] } viewReady : ReadyModel -> Html Msg viewReady model = div [ class "grid grid-cols-12 gap-6 h-full bg-slate-100" ] [ viewSidebar model , viewDraft model , viewHighlightedCard model ] viewSidebar : ReadyModel -> Html Msg viewSidebar model = div [ class "col-span-2 p-6 bg-slate-600" ] [ button [ onClick Decrement , disabled (not (Zipper.hasLeft model.draft)) , class "bg-slate-900 rounded text-white p-2 shadow disabled:opacity-50" ] [ text "Previous" ] , span [ class "text-white px-3 font-medium" ] [ text ("Pick " ++ String.fromInt (Zipper.position model.draft)) ] , button [ onClick Increment , class "bg-slate-900 rounded text-white p-2 shadow disabled:opacity-50" , disabled (not (Zipper.hasRight model.draft)) ] [ text "Next" ] ] viewError : ErrorModel -> Html Msg viewError model = div [ class "error" ] [ text model.error ] viewDraft : ReadyModel -> Html Msg viewDraft model = let chosen = (Zipper.focus model.draft).chosen picks = List.map (\c -> viewKeyedCard model (chosen.name == c.name) c) (Zipper.focus model.draft).available viewFocusStatButton s = button [ onClick (SetFocusStat s) , classList [ ( "bg-slate-900 rounded text-white p-2 shadow mr-2 mb-2", True ), ( "bg-slate-500 shadow-inset", model.focusStat == s ) ] ] [ text <| case s of FocusPickRate -> "Pick rate" FocusALPA -> "ALPA" FocusGIHWR -> "GIHWR" FocusALSA -> "ALSA" ] in -- Making this a keyed node forces Elm to recreate each card when the card name changes, -- so the old image doesn't hang around during loading of the new card image div [ class "flex flex-col space-between col-span-8 " ] [ Keyed.node "div" [ class "flex-grow grid grid-cols-8 auto-rows-min gap-6 p-6" ] picks , div [] [ viewFocusStatButton FocusPickRate , viewFocusStatButton FocusALPA , viewFocusStatButton FocusGIHWR , viewFocusStatButton FocusALSA ] ] viewHighlightedCard : ReadyModel -> Html Msg viewHighlightedCard model = div [ class "col-span-2 shadow-xl bg-slate-600 p-6" ] [ case model.highlighted of Just highlighted -> let url = case ( model.flipHighlighted, highlighted.backImage ) of ( True, Just backUrl ) -> backUrl _ -> highlighted.frontImage in div [] [ img [ src url , alt highlighted.name , onClick FlipHighlightedCard ] [] , viewCardDetailedPerformance model.database highlighted ] Nothing -> div [] [] ] formatPercentage : Float -> String formatPercentage value = Round.round 2 (value * 100) ++ "%" viewCardDetailedPerformance : Database.Database -> Draft.PickCard -> Html Msg viewCardDetailedPerformance db { name } = let makeCardFacts : CardData -> List ( String, String ) makeCardFacts cardData = [ ( "Pick rate" , Just <| formatPercentage (Card.pickRate cardData) ) , ( "GIHWR" , Maybe.map formatPercentage (Card.gihwr cardData) ) , ( "ALPA" , Maybe.map (Round.round 2) (Card.alpa cardData) ) , ( "ALSA" , Maybe.map (Round.round 2) (Card.alsa cardData) ) ] |> List.filterMap (\( label, value ) -> Maybe.map (\v -> ( label, v )) value) viewFact : ( String, String ) -> Html Msg viewFact ( label, value ) = div [ class "" ] [ div [] [ text label ] , div [] [ text value ] ] in case Database.get name db of Just cardData -> div [ class "" ] (List.map viewFact (makeCardFacts cardData)) Nothing -> div [] [ text "Could not find card in database" ] viewKeyedCard : ReadyModel -> Bool -> Draft.PickCard -> ( String, Html Msg ) viewKeyedCard model wasChosen { name, frontImage, backImage } = let stats = Database.get name model.database focusStat = case model.focusStat of FocusALSA -> Maybe.map (Round.round 2) (Maybe.andThen Card.alsa stats) FocusALPA -> Maybe.map (Round.round 2) (Maybe.andThen Card.alpa stats) FocusGIHWR -> Maybe.map formatPercentage (Maybe.andThen Card.gihwr stats) FocusPickRate -> Maybe.map (formatPercentage << Card.pickRate) stats in ( name , div [ classList [ ( "relative", True ) , ( "border-4 border-green-500", wasChosen ) ] , Events.onMouseEnter (Highlight { name = name, frontImage = frontImage, backImage = backImage }) ] [ img [ src frontImage, alt name ] [] , span [ class "absolute top-0 left-0 bg-green-100" ] [ text <| Maybe.withDefault "?" focusStat ] ] )