-- | A binding to <https://facebook.github.io/react/index.html React> based on the
-- <https://facebook.github.io/flux/docs/overview.html Flux> design.  The flux design pushes state
-- and complicated logic out of the view, allowing the rendering functions and event handlers to be
-- pure Haskell functions.  When combined with React's composable components and the one-way flow of
-- data, React, Flux, and GHCJS work very well together.
--
-- __Prerequisites__: This module assumes you are familiar with the basics of React and Flux.  From
-- the <https://facebook.github.io/react/docs/tutorial.html React documentation>, you should read at
-- least \"Tutorial\", \"Displaying Data\", \"Multiple Components\", and \"Forms\".  Note that
-- instead of JSX we use a Writer monad, but it functions very similarly so the examples in the
-- React documentation are very similar to how you will write code using this module.  The other
-- React documentation you can skim, the Haddocks below link to specific sections of the React
-- documentation when needed.  Finally, you should read the
-- <https://facebook.github.io/flux/docs/overview.html Flux overview>, in particular the central
-- idea of one-way flow of data from actions to stores to views which produce actions.
--
-- __Organization__: Briefly, you should create one module to contain the dispatcher, one module for
-- each store, and modules for the view definitions.  These are then imported into a Main module,
-- which calls 'reactRender' and initializes any AJAX load calls to the backend. The source package
-- contains some <https://bitbucket.org/wuzzeb/react-flux/src/tip/example/ example applications>.
--
-- __Web Deployment__: 'reactRender' is used to render your application into the DOM.
-- Care has been taken to make sure closure with ADVANCED_OPTIMIZATIONS correctly
-- minimizes a react-flux application.  No externs are needed, instead all you need to do is
-- protect the @React@ variable (and @ReactDOM@ if you are using version >= 0.14).  The TODO example
-- does this as follows:
--
-- >(function(global, React, ReactDOM) {
-- >contents of all.js
-- >})(window, window['React'], window['ReactDOM']);
--
-- __Node Deployment__: 'reactRenderToString' is used to render the application to a string when
-- running in node (not the browser).  To execute with node, you need to get @global.React@ and
-- @global.ReactDOMServer@  before executing all.js.  The TODO example application does this by
-- creating a file @run-in-node.js@ with the contents
--
-- >React = require("react");
-- >ReactDOMServer = require("react-dom/server");
-- >require("../../js-build/install-root/bin/todo-node.jsexe/all.js");
--
-- __React Native__: This module also works with <https://facebook.github.io/react-native/ React-Native>
-- to create a standalone native applications.  When combined with <http://electron.atom.io/ electron>,
-- you can even create standalone desktop applications.  The workflow is to use 'reactRender' the
-- same as web deployment but use the resulting JavaScript file in react-native and/or electron.
-- <https://github.com/jyrimatti/hseverywhere Jyrimatti has an example using react-native>.
--
-- __Testing__:  I use the following approach to test my react-flux application.  First, I use unit
-- testing to test the dispatcher and store 'transform' functions.  Since the dispatcher and the
-- store transform are just data manipulation, existing Haskell tools like hspec, QuickCheck,
-- SmallCheck, etc. work well.  Note that stores and 'dispatch' work in GHC and GHCJS, so this unit
-- testing can be done either in GHC or GHCJS. I don't do any unit testing of the views, because any
-- complicated logic in event handlers is moved into the dispatcher and the
-- rendering function is difficult to test in isolation.  Instead, I test the rendering via
-- end-2-end tests using <https://hackage.haskell.org/package/hspec-webdriver hspec-webdriver>.
-- This tests the React frontend against the real backend and hspec-webdriver has many utilities for
-- easily checking that the DOM is what you expect.  I have found this much easier than trying to
-- unit test each view individually, and you can still obtain the same coverage for equal effort.
-- The file <https://bitbucket.org/wuzzeb/react-flux/src/tip/test/spec/TodoSpec.hs test\/spec\/TodoSpec.hs>
-- in the source code contains a hspec-webdriver test for the TODO example application.

{-# OPTIONS_GHC -fno-warn-duplicate-exports #-} -- ArgumentsToProps is exported twice, once by React.Flux.PropertiesAndEvents and once here
module React.Flux (
  -- * Dispatcher
  -- $dispatcher

  -- * Stores
    ReactStore
  , StoreData(..)
  , mkStore
  , getStoreData
  , alterStore
  , SomeStoreAction(..)
  , executeAction

  -- * Views
  , ReactView
  , defineControllerView
  , defineView
  , defineStatefulView
  , ViewEventHandler
  , StatefulViewEventHandler

  -- * Elements
  , ReactElement
  , ReactElementM(..)
  , elemString
  , elemText
  , elemJSString
  , elemShow
  , view
  , viewWithSKey
  , viewWithIKey
  , childrenPassedToView
  , foreignClass
  , rawJsRendering
  , transHandler
  , liftViewToStateHandler
  , module React.Flux.DOM
  , module React.Flux.PropertiesAndEvents
  , module React.Flux.Combinators

  -- * Main
  , reactRender
  , reactRenderToString
  , exportViewToJavaScript
  , ArgumentsToProps
  , ReturnProps(..)

  -- * Performance
  -- $performance

  -- * Depracated
  , viewWithKey
  , ReactViewKey
) where

import Data.Typeable (Typeable)
import Data.Text (Text)

import React.Flux.Views
import React.Flux.DOM
import React.Flux.Internal
import React.Flux.PropertiesAndEvents
import React.Flux.Combinators
import React.Flux.Store

#ifdef __GHCJS__
import GHCJS.Types (JSVal, nullRef)
import GHCJS.Marshal (fromJSVal)
#endif

----------------------------------------------------------------------------------------------------
-- reactRender has two versions
----------------------------------------------------------------------------------------------------

-- | Render your React application into the DOM.  Use this from your @main@ function, and only in the browser.
-- 'reactRender' only works when compiled with GHCJS (not GHC), because we rely on the React javascript code
-- to actually perform the rendering.
reactRender :: Typeable props
            => String -- ^ The ID of the HTML element to render the application into.
                      -- (This string is passed to @document.getElementById@)
            -> ReactView props -- ^ A single instance of this view is created
            -> props -- ^ the properties to pass to the view
            -> IO ()

#ifdef __GHCJS__

reactRender htmlId rc props = do
    (e, _) <- mkReactElement id (ReactThis nullRef) $ view rc props mempty
    js_ReactRender e (toJSString htmlId)

foreign import javascript unsafe
    "(typeof ReactDOM === 'object' ? ReactDOM : React)['render']($1, document.getElementById($2))"
    js_ReactRender :: ReactElementRef -> JSString -> IO ()

#else

reactRender _ _ _ = error "reactRender only works when compiled with GHCJS, because we rely on the javascript React code."

#endif

-- | Render your React application to a string using either @ReactDOMServer.renderToString@ if the first
-- argument is false or @ReactDOMServer.renderToStaticMarkup@ if the first argument is true.
-- Use this only on the server when running with node.
-- 'reactRenderToString' only works when compiled with GHCJS (not GHC), because we rely on the React javascript code
-- to actually perform the rendering.
--
-- If you are interested in isomorphic React, I suggest instead of using 'reactRenderToString' you use
-- 'exportViewToJavaScript' and then write a small top-level JavaScript view which can then integrate with
-- all the usual isomorphic React tools.
reactRenderToString :: Typeable props
                    => Bool -- ^ Render to static markup?  If true, this won't create extra DOM attributes
                            -- that React uses internally.
                    -> ReactView props -- ^ A single instance of this view is created
                    -> props -- ^ the properties to pass to the view
                    -> IO Text

#ifdef __GHCJS__

reactRenderToString includeStatic rc props = do
    (e, _) <- mkReactElement id (ReactThis nullRef) $ view rc props mempty
    sRef <- (if includeStatic then js_ReactRenderStaticMarkup else js_ReactRenderToString) e
    --return sRef
    --return $ JSS.unpack sRef
    mtxt <- fromJSVal sRef
    maybe (error "Unable to convert string return to Text") return mtxt

foreign import javascript unsafe
    "(typeof ReactDOMServer === 'object' ? ReactDOMServer : (typeof ReactDOM === 'object' ? ReactDOM : React))['renderToString']($1)"
    js_ReactRenderToString :: ReactElementRef -> IO JSVal

foreign import javascript unsafe
    "(typeof ReactDOMServer === 'object' ? ReactDOMServer : (typeof ReactDOM === 'object' ? ReactDOM : React))['renderToStaticMarkup']($1)"
    js_ReactRenderStaticMarkup :: ReactElementRef -> IO JSVal

#else

reactRenderToString _ _ _ = error "reactRenderToString only works when compiled with GHCJS, because we rely on the javascript React code."

#endif

-- $performance 
--
-- React obtains high <https://facebook.github.io/react/docs/advanced-performance.html performance> from two techniques: the
-- <https://facebook.github.io/react/docs/reconciliation.html virtual DOM/reconciliation> and 
-- <https://facebook.github.io/react/docs/events.html event handlers> registered on the document.
--
-- __Reconciliation__
--
-- To support fast reconciliation, React uses key properties (set by 'viewWithKey') and a
-- @shouldComponentUpdate@ lifetime class method.  The React documentation on
-- <https://facebook.github.io/react/docs/advanced-performance.html performance and immutable-js> talks
-- about using persistent data structures, which is exactly what Haskell does.  Therefore, we
-- implement a @shouldComponentUpdate@ method which compares if the javascript object representing
-- the Haskell values for the @props@, @state@, and/or @storeData@ have changed.  Thus if you do not
-- modify a Haskell value that is used for the @props@ or @state@ or @storeData@, React will skip
-- re-rendering that view instance.  Note that we are not checking equality, just if the javascript
-- object representing a Haskell object has changed, with some special support for pairs and tuples
-- of size three.
--
-- There is subtle issue: this check only works if the props are not a thunk but are an actual data
-- constructor.  Consider the following
--
-- >data MyStoreData = MyStoreData {
-- >   myA :: !A
-- > , myB :: !B
-- > , myC :: !C
-- > , myD :: !D
-- >} deriving (Show, Typeable)
-- >
-- >myAview :: ReactView A
-- >myAview = defineView ....
-- >
-- >myStoreView :: ReactView ()
-- >myStoreView = defineControllerView "my store" myStore $ \myData () ->
-- >    div_ $ view myAview (myA myData) mempty
-- >    div_ ....
--
-- In @myStoreView@, note that @myA myData@ is passed as the props to @myAview@.  So consider the
-- situtation when say an action changes @C@ but leaves @A@ unchanged.  We would like for the
-- rendering of @myAview@ to be skipped, but unfortunately it will be re-rendered.  The reason is
-- that the props passed to @myAview@ is an unevaluated thunk @myA myData@.  Sure, the @A@
-- constructor has not changed and if the thunk is forced it will return this unchanged @A@ data
-- constructor, but the @shouldComponentUpdate@ test does not do any computation or evaluation, it
-- just checks if the passed in javascript object is the same as it was the last time the view was
-- rendered.  We can fix this by forcing the thunk before passing it to 'view', which I do via bang
-- patterns.  Instead of ever calling 'view' directly from a rendering function, for each
-- 'ReactView' I create a combinator as follows:
--
-- >myAview_ :: A -> ReactElementM handler ()
-- >myAview_ !a = view myAview a mempty
-- >
-- >myStoreView :: ReactView ()
-- >myStoreView = defineControllerView "my store" myStore $ \myData () ->
-- >    div_ $ myAview_ (myA myData)
-- >    div_ ....
--
-- Note the bang pattern on the @a@ parameter to @myAview_@.  What now happens is that the bang pattern
-- forces the thunk @myA myData@ to turn into the @A@ data constructor.  If an action does not edit the @A@ portion
-- of the store data, this will still be represented by the same javascript object as before and
-- React will not re-render the @myAview@.
--
-- Now consider another situtation where you would like a view that takes A and B.
--
-- >myAandBview :: ReactView (A, B)
-- >myAandBview = defineView ....
-- >
-- >myAandBview_ :: A -> B -> ReactElementM handler ()
-- >myAandBview_ !a !b = view myAandBview (a, b) mempty
-- >
-- >myStoreView :: ReactView ()
-- >myStoreView = defineControllerView "my store" myStore $ \myData () ->
-- >    div_ $ myAview_ (myA myData)
-- >    div_ $ myAandBview_ (myA myData) (myB myData)
-- >    div_ ....
--
-- Again, if you have an action that just changes @C@ you would like @myAandBview@ to not be
-- re-rendered.  With the simple javascript object check, it would be re-rendered because the props
-- are a tuple and the Haskell value (and thus javascript object) for the tuple is being recreated each
-- time @myStoreView@ is rendered.  To overcome this obstacle, @react-flux@ contains special code to check pairs
-- and tuples of size three.  If the props are a pair or a tuple of size three, the components of
-- the tuple will be compared to see if they are the same javascript object.  Thus similar to the
-- above we need to make sure each component of the tuple is not a thunk but a data constructor,
-- which happens via the bang patterns in @myAandBview_@.  The end result is that if an action just
-- changes @C@ or @D@ and leaves @A@ and @B@ unchanged, the above code will cause React to not
-- re-render @myAandBview@ because the two components of the pair are forced and are still the same
-- unchanged data value/javascript object.  You can see this in action inside the test suite if you
-- would like an example.
--
-- So far we have been focusing on making sure the new props are not a thunk by forcing it before
-- passing it into 'view'.  But we also need to make sure the initial props are not a thunk.  This
-- is not quite as bad since the check will only fail the next time a re-render occurs and after
-- that everything will be OK so we will still mostly skip re-rendering, but is still a small
-- annoyance.  There are several ways to fix this, but the easiest is to add bang patterns to the
-- definition of @MyStoreData@.  If you scroll up you can see that each member of @MyStoreData@ has
-- a bang pattern.  Thus when an action does change @A@, whatever a new value is set into @myA@, it
-- will not be a thunk but an actual data constructor.  Then the initial props passed into the view
-- will not be a thunk.
--
-- In summary, you should follow these rules:
--
--  1. Use bang patterns on each member in your store data.  In fact, once GHC 8 is released, I
--  plan on turning on the new @StrictData@ extension and then all these bang patterns can be
--  dropped.
--
--  2. Try and keep your view parameters as part of the store that will be unchanged by some
--  actions.  Use tuples of size two or three to combine multiple parts of the store data or even
--  data from multiple stores. (Tuples of larger size could be supported without much effort if
--  required.)
--
--  3. For each view, make a combinator with a underscore suffix which uses bang patterns to force
--  the props before passing it to the 'view' function.
--
-- __Events__
--
-- For events, React registers only global event handlers and also keeps event objects (the object
-- passed to the handlers) in a pool and re-uses them for successive events.  We want to parse this
-- event object lazily so that only properties actually accessed are parsed, but this is a problem
-- because lazy access could occur after the event object is reused.  Instead of making a copy of
-- the event, we use the 'NFData' instance on 'SomeStoreAction' to force the evaluation of the store
-- action(s) resulting from the event.  We therefore compute the action before the event object
-- returns to the React pool, and rely on the type system to prevent the leak of the event object
-- outside the handlers.  Thus, you cannot "cheat" in the 'NFData' instance on your store actions;
-- the event objects dilerbertly do not have a 'NFData' instance, so that you must pull all your
-- required data out of the event object and into an action in order to properly implement 'NFData'.
-- Of course, the easiest way to implement 'NFData' is to derive it with Generic and DeriveAnyClass,
-- as @TodoAction@ does above.

-- $dispatcher
-- The dispatcher is the central hub that manages all data flow in a Flux application.  It has no
-- logic of its own and all it does is distribute actions to stores.  There is no special support
-- for a dispatcher in this module, since it can be easily implemented directly using Haskell
-- functions.  The event handlers registered during rendering are expected to produce a list of 'SomeStoreAction'.
-- The dispatcher therefore consists of Haskell functions which produce these lists of
-- 'SomeStoreAction'.  Note that this list of actions is used instead of @waitFor@ to sequence
-- actions to stores: when dispatching, we wait for the 'transform' of each action to complete
-- before moving to the next action.
--
-- In the todo example application there is only a single store, so the dispatcher just
-- passes along the action to the store.  In a larger application, the dispatcher could have its
-- own actions and produce specific actions for each store.
--
-- >dispatchTodo :: TodoAction -> [SomeStoreAction]
-- >dispatchTodo a = [SomeStoreAction todoStore a]