{-|
Module      : EventLoop.Output.Graphical.Graphical
Description : Library of all the possible output 'Graphical' events in the example implementation.
Copyright   : (c) Sebastiaan la Fleur, 2014
License     : BSD3
Maintainer  : sebastiaan.la.fleur@gmail.com
Stability   : experimental
Portability : All

This module expresses how the outgoing 'Graphical' events are modelled in the example implementation.
Possible graphical events are 'Draw', 'MoveGroup', 'MoveElement', 'RemoveGroup' and 'RemoveElement'.
Respectively drawing a graphical object, moving an entire group, moving a single element, removing an entire group and removing a single element.
An element is a graphical object expressed through the name.
-}
module EventLoop.Output.Graphical.Graphical(
        Graphical(..),
        GObject(..),
        Primitive(..),
        Name,
        Groupname,
        Color,
        Font,
        Relative
) where

import EventLoop.Json
import EventLoop.Config
import EventLoop.CommonTypes
import FPPrac

-- | The name of a graphical object. Another synonym in the package used for this is 'EventLoop.CommonTypes.Element'.
type Name = [Char]

-- | The groupname of a set of graphical objects.
type Groupname = [Char]

-- | The color expressed in (red, green, blue) code where each value is between 0 <= 255.
type Color = (Float, Float, Float) -- (r, g, b)

-- | The font associated with a 'Text' primitive.
type Font = [Char]

-- | A boolean expressing if an event should be carried out relative to the old situation or to the absolute situation
--   Example of this is when moving an element. Should the move be relative to the old 'EventLoop.CommonTypes.Pos' or to the
--   absolute 'EventLoop.CommonTypes.Pos' on the screen.
type Relative = Bool -- Move relative to old spot or not

-- | Graphical Responses Out
data Graphical = Draw GObject Groupname            -- ^ Draw the graphical object with the given groupname.
                | MoveGroup Groupname Pos Relative -- ^ Move an entire group to a new position possibly relative to the old position.
                | MoveElement Name Pos Relative    -- ^ Move a single element to a new position possibly relative to the old position.
                | RemoveGroup Groupname            -- ^ Remove a group
                | RemoveElement Name               -- ^ Remove an element.

-- | Instance to express how a 'Graphical' event can be parsed to a JSON formatted 'String'.                
instance JSONAble Graphical where
    toJsonMessage (Draw gObject gName)              = JSONObject [(JSONMember modeS      (JSONString drawS)),
                                                               (JSONMember gobjectS   (toJsonMessage gObject)),     
                                                               (JSONMember groupnameS (JSONString gName))]
                                                               
    toJsonMessage (MoveGroup gName pos rel)      = JSONObject [(JSONMember modeS      (JSONString movegroupS)),
                                                               (JSONMember groupnameS (JSONString gName)), 
                                                               (JSONMember positionS  (positionToJsonMessage pos)), 
                                                               (JSONMember relativeS  (JSONBool rel))]
                                                                                      
    toJsonMessage (MoveElement name pos rel)     = JSONObject [(JSONMember modeS      (JSONString moveelementS)),
                                                               (JSONMember nameS      (JSONString name)),       
                                                               (JSONMember positionS  (positionToJsonMessage pos)), 
                                                               (JSONMember relativeS  (JSONBool rel))]
                                                               
    toJsonMessage (RemoveGroup gName)              = JSONObject [(JSONMember modeS       (JSONString removegroupS)),
                                                               (JSONMember groupnameS  (JSONString gName))]
    
    toJsonMessage (RemoveElement name)              = JSONObject [(JSONMember modeS (JSONString removeelementS)),
                                                               (JSONMember nameS (JSONString name))]
                


-- | A general graphical object containing the common attributes of each 'Primitive'.
data GObject = GObject  { name :: Name           -- ^ Name of the graphical object/element
                        , prim :: Primitive      -- ^ The graphical primitive that should be drawn
                        , children :: [GObject]  -- ^ Children of the graphical primitive. Can be used to create composited graphical components containing multiple 'GObject's.
                        } -- ^ Graphical Object
             | Container { children :: [GObject] -- ^ A set of 'GObject's. Can be used to create composited graphical components containing multiple 'GObject's with no real top 'GObject'.
                         } -- ^ A container for Graphical Objects
                        deriving (Show)

-- | Instance to express how a 'GObject' can be parsed to a JSON formatted 'String'. 
instance JSONAble GObject where
    toJsonMessage (GObject name prim children) = JSONObject [(JSONMember typeS     (JSONString gobjectS)), 
                                                             (JSONMember primS     (toJsonMessage prim)), 
                                                             (JSONMember nameS     (JSONString name)), 
                                                             (JSONMember childrenS (JSONArray (map toJsonMessage children)))]
                                                             
    toJsonMessage (Container children)         = JSONObject [(JSONMember childrenS (JSONArray (map toJsonMessage children)))]
                        
-- | Primitive graphical structures                        
data Primitive = Text   { -- | 'EventLoop.CommonTypes.Color' of the edges of the text
                          edgeColor :: Color     
                        , edgeThickness :: Float -- ^ The edge thickness of the text (Should be 1 most of the time)
                        , color :: Color         -- ^ The 'EventLoop.CommonTypes.Color' of the fill of the text
                        , position :: Pos        -- ^ The position on the screen of the text
                        , size :: Float          -- ^ The height of the text in pixels
                        , font :: Font           -- ^ Which font to be used
                        , text :: [Char]         -- ^ The actual text to be displayed
                        , fromCenter :: Bool     -- ^ Is the position the topleft corner of the text or the center of the text
                        } -- ^ The text graphical primitive
                | Line  { edgeColor :: Color     -- ^ 'EventLoop.CommonTypes.Color' of the line
                        , edgeThickness :: Float -- ^ The thickness of the line
                        , positions :: [Pos]     -- ^ The list of positions the line should go through. A line will be drawn from point 1 to point 2 to point...
                        } -- ^ The line graphical primitive
                | Rect  { edgeColor :: Color      -- ^ 'EventLoop.CommonTypes.Color' of the edges of the rectangle
                        , edgeThickness :: Float  -- ^ The edge thickness of the rectangle
                        , color :: Color          -- ^ The 'EventLoop.CommonTypes.Color' of the fill of the rectangle
                        , position :: Pos         -- ^ The topleft corner of the rectangle on the screen
                        , dimensions :: Dimension -- ^ The dimensions of the rectangle
                        } -- ^ The rectangle graphical primitive
                | Arc   { edgeColor :: Color      -- ^ 'EventLoop.CommonTypes.Color' of the edges of the arc
                        , edgeThickness :: Float  -- ^ The edge thickness of the arc
                        , color :: Color          -- ^ The 'EventLoop.CommonTypes.Color' of the fill of the arc
                        , position :: Pos         -- ^ The position of the center of the arc
                        , radius :: Float         -- ^ The radius of the arc
                        , startAng :: Float       -- ^ The starting angle of the arc in degrees.
                        , endAng :: Float         -- ^ The ending angle of the arc in degrees.
                        } -- ^ The arc graphical primitive. This is the part of a circle. When startAng=0 and endAng=360 you get a full circle.
                    deriving (Show, Eq)

-- | Instance to express how a 'Primtive' can be parsed to a JSON formatted 'String'.
instance JSONAble Primitive where
    toJsonMessage (Text ec et color position size font text fromcenter) = JSONObject [(JSONMember typeS           (JSONString textS)), 
                                                                                      (JSONMember edgecolorS      (colorToJsonMessage ec)), 
                                                                                      (JSONMember edgethicknessS  (JSONFloat et)), 
                                                                                      (JSONMember colorS          (colorToJsonMessage color)), 
                                                                                      (JSONMember positionS       (positionToJsonMessage position)), 
                                                                                      (JSONMember sizeS           (JSONFloat size)), 
                                                                                      (JSONMember fontS           (JSONString font)), 
                                                                                      (JSONMember textS           (JSONString text)), 
                                                                                      (JSONMember fromcenterS     (JSONBool fromcenter))]
                                                                            
    toJsonMessage (Line ec et positions) = JSONObject [(JSONMember typeS           (JSONString lineS)), 
                                                       (JSONMember edgecolorS      (colorToJsonMessage ec)), 
                                                       (JSONMember edgethicknessS  (JSONFloat et)), 
                                                       (JSONMember positionsS      (JSONArray (map positionToJsonMessage positions)))]
                                                                                     
    toJsonMessage (Rect ec et color position dim) = JSONObject [(JSONMember typeS           (JSONString rectS)), 
                                                                         (JSONMember edgecolorS      (colorToJsonMessage ec)), 
                                                                         (JSONMember edgethicknessS  (JSONFloat et)), 
                                                                         (JSONMember colorS          (colorToJsonMessage color)), 
                                                                         (JSONMember positionS       (positionToJsonMessage position)),
                                                                         (JSONMember dimensionS      (dimensionToJsonMessage dim))]
                                                                
    toJsonMessage (Arc ec et color position radius startAng endAng) = JSONObject [(JSONMember typeS          (JSONString arcS)),  
                                                                                  (JSONMember edgecolorS     (colorToJsonMessage ec)), 
                                                                                  (JSONMember edgethicknessS (JSONFloat et)), 
                                                                                  (JSONMember colorS         (colorToJsonMessage color)), 
                                                                                  (JSONMember positionS      (positionToJsonMessage position)), 
                                                                                  (JSONMember radiusS        (JSONFloat radius)), 
                                                                                  (JSONMember startangS      (JSONFloat startAng)), 
                                                                                  (JSONMember endangS        (JSONFloat endAng))]

-- | Private                                                                                
colorToJsonMessage :: Color -> JSONMessage
colorToJsonMessage (r, g, b) = JSONObject [(JSONMember rS (JSONFloat r)), 
                                           (JSONMember gS (JSONFloat g)), 
                                           (JSONMember bS (JSONFloat b))]

-- | Private
positionToJsonMessage :: Pos -> JSONMessage
positionToJsonMessage (x, y) = JSONObject [(JSONMember xS (JSONFloat x)), 
                                           (JSONMember yS (JSONFloat y))] 

-- | Private
dimensionToJsonMessage :: Dimension -> JSONMessage                                           
dimensionToJsonMessage (w, h) = JSONObject [(JSONMember heightS (JSONFloat h)), 
                                            (JSONMember widthS  (JSONFloat w))]