-- Look at DragAndDrop first!

-- StayInTouch

-- Example of using Dictionary updates, dragging, getting values out of a dictionary.

-- We need this as the first comment line, because we use the Dict module
-- Functions starting with Dict. are from that module.  See
--     https://package.elm-lang.org/packages/elm/core/latest/Dict
import Dict exposing (Dict)

myShapes model =
  [ text "Drag me!" |> centered |> filled black |> move (0,-55)
  , polygon (Dict.values model.points) |> outlined (solid 1) black
  ]
  ++
  Dict.foldl drawAt [] model.points
  ++
  -- what we draw depends on the state, this code generated by PALDraw
  case model.state of
      Waiting  ->
          -- apply drawWord to two inputs, the index in list, and the value in the wordList
          []

      Dragging (idx,(x0,y0)) (dx,dy) (lx,ly) ->
          [ circle 5
              |> outlined (solid 2) purple
              |> move ( x0 + lx - dx
                      , y0 + ly - dy)
          , rect 191 127 |> filled (rgba 0 0 255 0.01)
              |> notifyMouseUpAt MUp
              |> notifyMouseMoveAt MMove
              |> notifyLeaveAt MUp
          ]

-- draws one point and attaches a notification to it
drawAt : Int -> (Float,Float) -> List (Shape Msg) -> List (Shape Msg)
drawAt idx pos shapes =
    ( circle 5 |> filled red |> move pos |> notifyMouseDownAt (MDown (idx,pos) ) )
    :: shapes


type Msg = Tick Float GetKeyState
         | MDown (Int,(Float,Float)) ( Float, Float )
         | MMove ( Float, Float )
         | MUp ( Float, Float )

type State = Waiting
           | Dragging ( Int, ( Float, Float ) ) ( Float, Float ) ( Float, Float )

update : Msg -> Model -> Model
update msg model =
    case msg of
        Tick t _ ->
            case model.state of
                Waiting -> { model | time = t }
                Dragging draggable downPos lastPos -> { model | time = t }
        MDown draggable newPos ->
            case model.state of
                Waiting  ->
                    { model | state = Dragging draggable newPos newPos }
                otherwise ->
                    model
        MMove newPos ->
            case model.state of
                Dragging (idx,(x,y)) (lx,ly) (cx,cy) ->
                    { model | state = Dragging (idx,(x+cx-lx,y+cy-ly)) (cx,cy) newPos
                            , points = Dict.update idx (mvPoint (lx,ly) (cx,cy)) model.points
                            }
                otherwise ->
                    model
        MUp newPos ->
            case model.state of
                Dragging (idx,pos) last current ->
                    { model | state = Waiting
                            , points = Dict.update idx (mvPoint last current) model.points
                            }
                otherwise ->
                    model

mvPoint : (Float,Float) -> (Float,Float) -> Maybe (Float,Float) -> Maybe (Float,Float)
mvPoint (lx,ly) (cx,cy) mPoint =
  case mPoint of
    Just (x,y) -> Just (x + cx - lx, y + cy - ly)
    Nothing -> Nothing

type alias Model =
    { time : Float
    , state : State
    -- a Dict lets us look up values, like a normal dictionary, but with any values, not just definitions
    , points : Dict Int (Float,Float)
    }

init : Model
init = { time = 0
       , state = Waiting
       -- creates a Dict from a list of pairs of indices and points
       , points = Dict.fromList <| List.map ( \ idx -> (idx, (cos (toFloat idx * 0.1 * pi) , sin (toFloat idx * 0.1 * pi))) )
                                   (List.range 0 20)
       }