{-# LANGUAGE OverloadedStrings, MultiParamTypeClasses, FunctionalDependencies,
             DeriveGeneric, TypeOperators, DefaultSignatures, FlexibleContexts,
             TemplateHaskell
  #-}

{- | Internal core module.
The purpose of this module is to define the most basic types
and write the necessary instances for them.
-}
module Graphics.Web.Processing.Core.Primal (
  -- * Types
  -- ** Singleton types
  -- | A list of singleton types, used to restrict the
  --   use of certain commands to specific contexts.
    Preamble (..), Setup (..), Draw (..)
  , MouseClicked (..), MouseReleased (..)
  , KeyPressed (..)
  -- ** Recursive types
  , Recursive (..)
  -- ** @Proc_*@ types
  -- *** Boolean
  , Proc_Bool (..), fromBool
  , true, false
  , pnot, (#||), (#&&)
  -- *** Int
  , Proc_Int (..), fromInt
  , pfloor, pround
  -- *** Float
  , Proc_Float (..), fromFloat
  , intToFloat
  , noisef
  -- *** Image
  , Proc_Image
  -- *** Char
  , Proc_Char , fromChar
  -- *** Text
  , Proc_Text, fromStText
  , (+.+)
  , Proc_Show (..)
  -- *** Keys
  , Proc_Key (..)
  , Proc_KeyCode (..)
  -- ** Type class of proc types
  , ProcType (..)
  , isVarInArg, isVarInAssign
  , assignVarName
  -- ** Conditionals
  , Proc_Eq (..)
  , Proc_Ord (..)
  -- ** Reductions
  , Reducible (..)
  -- ** Variables
  , Var, varName, varFromText
  , ArrayVar, arrayVarName, arrayVarFromText
  , arraySize
  , arrayVarToVar
  -- ** Script
  , ProcCode (..), ProcArg (..), ProcAssign (..)
  , ProcList (..)
  , emptyCode
  , (>>.)
  , ProcScript (..)
  , emptyScript
  ) where

import Prelude hiding (foldr)
import Data.Text (Text,lines,pack)
import Data.Text.Lazy (toStrict)
import qualified Data.Sequence as Seq
import Data.Monoid
import Data.String
import Data.Foldable (foldMap,foldr)
import Control.Applicative
-- Pretty
import Text.PrettyPrint.Mainland
-- QuickCheck
import Test.QuickCheck (Arbitrary (..), Gen, oneof, sized, resize, vectorOf)
import Test.QuickCheck.Instances()
-- Meta-programming
import GHC.Generics
import Graphics.Web.Processing.Core.TH

------------------------------------------------
-- QUICK CHECK DERIVING

{-

Some of the types defined in this module have a big
amount of data constructors. Creating Arbitrary instances
for each of them manually is a tedious and unnecessary work.

In order to be able to derive automatically instance for
the arbitrary typeclass, we create a new class, PArbitrary
(from Processing Arbitrary). Having access to the class
definition, we can provide a default instance based in our
generic deriving. Once an instance to PArbitrary is done,
the Arbitrary instance is trivial:

instance Arbitrary a where
 arbitrary = parbitrary

Given the number of data constructors, and being most of them
recursive, if we create totally random values, the chances of
creating (insanely) huge values is very high. To avoid it, we
set a maximum number of random steps (see 'sizeLimit'). When
this number is reached, we "travel" to the left-most constructor,
which by definition will be finite (something like Proc_Float Float).

-}

class PArbitrary a where
 parbitrary :: Gen a
 default parbitrary :: (Generic a, GArbitrary (Rep a)) => Gen a
 parbitrary = to <$> garbitrary

class GArbitrary f where
 garbitrary :: Gen (f a)

instance GArbitrary U1 where
 garbitrary = pure U1

instance (GArbitrary a, GArbitrary b) => GArbitrary (a :*: b) where
 garbitrary = sized $
   \n -> resize (n+1) $ (:*:) <$> garbitrary <*> garbitrary

sizeLimit :: Int
sizeLimit = 15

instance (GArbitrary a, GArbitrary b) => GArbitrary (a :+: b) where
 garbitrary = sized $
     \n -> if n > sizeLimit
              then L1 <$> garbitrary
              else oneof [L1 <$> garbitrary, R1 <$> garbitrary]

instance GArbitrary a => GArbitrary (M1 i c a) where
 garbitrary = M1 <$> garbitrary

instance PArbitrary a => GArbitrary (K1 i a) where
 garbitrary = K1 <$> parbitrary

------------------------------------------------
-- DEFAULT INSTANCES

instance Arbitrary a => PArbitrary (Maybe a) where
 parbitrary = arbitrary

instance Arbitrary a => PArbitrary (Seq.Seq a) where
 parbitrary = arbitrary

instance PArbitrary Int where
 parbitrary = arbitrary

instance PArbitrary Float where
 parbitrary = arbitrary

instance PArbitrary Text where
 parbitrary = pack <$> vectorOf 4 parbitrary

instance PArbitrary Char where
 parbitrary = oneof $ fmap pure [ 'a' .. 'z' ]

instance Arbitrary a => PArbitrary [a] where
 parbitrary = arbitrary

------------------------------------------------

{-
Processing.js code is divided in different sections.
Each section handles a different event. Naturally,
there are commands that may be called inside of a
particular context, but not within another. Most of
these commands are runnable in different kind of
events. Writing variables should be possible from
any event. To handle this situation, we annotate
the AST with a /context/. This context indicates
which event that portion of code belongs to. For
example, @ProcCode Draw@ indicates that the code
belongs to the draw loop. Now we can restrict
functions to work only under certain contexts.
For example, variables should be created only once.
Since events may be called several times, we restrict
the type of any function that creates variables,
annotating the type with 'Preamble'.
-}

-- | The /preamble/ is the code that is executed
--   at the beginning of the script.
data Preamble = Preamble

-- | In the /setup/ part, settings like /size/ or
--   /frame rate/ are supplied.
data Setup = Setup

-- | The drawing loop.
data Draw = Draw

-- | Code that is executed when the mouse is clicked.
data MouseClicked = MouseClicked

-- | Code that is executed when the mouse is released.
data MouseReleased = MouseReleased

-- | Code executed when a key is pressed.
data KeyPressed = KeyPressed

-- PRETTY-HELPERS

pfunction :: Text -> [Doc] -> Doc
pfunction n as = fromText n <> parens (commasep as)

-- TYPES

{-

Some Proc_* types can be seen as extensions
of some Haskell types. For example Proc_Float
may contain a Float under the Proc_Float data
constructor.  However, it has more data constructors
and, therefore, it may contain other different
values. The Extended class is created for
these types. It provides two methods that, once
defined, permit to extend functions and operators
from the type that has been extended to the extension.
For example, we can extend the sin function, which
is defined for Float values, to value of the type
Proc_Float.

The two methods required are extend and patmatch
(pattern match). The method extend should inject
a value from the extended type to the extension.
The method patmatch would do the opposite. However,
not every element in the extension belongs to the
extended type. We return Nothing in those cases.

Extended functions and operators behave the same
way as the originals for values in the extended
type. A supplied default function/operator
indicates what to do in the rest of cases.

-}

class Extended from to | to -> from where
 extend :: from -> to
 patmatch :: to -> Maybe from

-- | Function extension.
extendf :: (Extended from to, Extended from' to')
        => (from -> from') -> (to -> to') -> (to -> to')
extendf f g x =
  case patmatch x of
    Nothing -> g x
    Just a  -> extend $ f a

-- | Operator extension.
extendop :: (Extended from   to
            ,Extended from'  to'
            ,Extended from'' to'')
         => (from -> from' -> from'')
         -> (to   -> to'   -> to'')
         -> (to   -> to'   -> to'')
extendop f g x y =
  case (patmatch x, patmatch y) of
    (Just a, Just b) -> extend $ f a b
    _ -> g x y

{- | Class of recursive types.

The 'recursor' function applies the
given function to every subexpression
of the same type. For example, this would
be the recursor over lists:

recursor f [] = []
recursor f (x:xs) = x : f xs

Instances of Recursive can be derived
using $(deriveRecursive ''Type).
-}
class Recursive a where
 recursor :: (a -> a) -> a -> a

{- Proc_* types

Proc_* types are AST's for different kind
of expressions. For example, a value of type
Proc_Bool store an AST of a boolean expression.
The "Proc_" prefix indicates that the type
of the expression matches a type in Processing.

Proc_* types have a specialized version of
'extend'. This way, the Extended class can be
kept hidden to the user. If this is or not a
good idea is something to be discussed.
Note that the class use Functional Dependencies.

-}

-- | Boolean values.
data Proc_Bool =
   Proc_True
 | Proc_False
   -- Operations
 | Proc_Neg Proc_Bool
 | Proc_Or Proc_Bool Proc_Bool
 | Proc_And Proc_Bool Proc_Bool
   -- Variables
 | Bool_Var Text
   -- Comparisons
     -- Bool
   | Bool_Eq Proc_Bool Proc_Bool
   | Bool_NEq Proc_Bool Proc_Bool
     -- Int (with order)
   | Int_Eq Proc_Int Proc_Int
   | Int_NEq Proc_Int Proc_Int
   | Int_LE Proc_Int Proc_Int
   | Int_L Proc_Int Proc_Int
   | Int_GE Proc_Int Proc_Int
   | Int_G Proc_Int Proc_Int
     -- Float (with order)
   | Float_Eq Proc_Float Proc_Float
   | Float_NEq Proc_Float Proc_Float
   | Float_LE Proc_Float Proc_Float
   | Float_L Proc_Float Proc_Float
   | Float_GE Proc_Float Proc_Float
   | Float_G Proc_Float Proc_Float
     -- Char
   | Char_Eq Proc_Char Proc_Char
   | Char_NEq Proc_Char Proc_Char
     -- Text
   | Text_Eq Proc_Text Proc_Text
     -- Key
   | Key_Eq Proc_Key Proc_Key
   | KeyCode_Eq Proc_KeyCode Proc_KeyCode
   -- Conditional
 | Bool_Cond Proc_Bool Proc_Bool Proc_Bool
     deriving (Eq,Ord,Generic)

instance PArbitrary Proc_Bool

instance Arbitrary Proc_Bool where
 arbitrary = parbitrary

instance Extended Bool Proc_Bool where
 extend True  = Proc_True 
 extend False = Proc_False
 patmatch Proc_True = Just True
 patmatch Proc_False = Just False
 patmatch _ = Nothing

-- Doc helpers

docEq :: Doc
docEq = fromText "=="

docNEq :: Doc
docNEq = fromText "!="

docLE :: Doc
docLE = fromText "<="

docL :: Doc
docL = fromText "<"

docGE :: Doc
docGE = fromText ">="

docG :: Doc
docG = fromText ">"

-- | Processing.js syntax for conditionals.
--
-- > <bool> ? <a> : <a>
--
docCond :: Doc -> Doc -> Doc -> Doc
docCond _if _then _else = _if <+> fromText "?" <+> _then <+> fromText ":" <+> _else

-- | This constant indicates how many spaces
--   are added in each indentation. Events
--   and conditionals add indentation.
indentLevel :: Int
indentLevel = 3

instance Pretty Proc_Bool where
 ppr Proc_True = fromText "true"
 ppr Proc_False = fromText "false"
 ppr (Proc_Neg b) = fromText "!" <> ppr b
 ppr (Proc_Or b1 b2)  = parens $ ppr b1 <+> fromText "||" <+> ppr b2
 ppr (Proc_And b1 b2) = parens $ ppr b1 <+> fromText "&&" <+> ppr b2
 ppr (Bool_Var t) = fromText t
 -- Comparisons
 ppr (Bool_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
 ppr (Bool_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
 ppr (Int_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
 ppr (Int_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
 ppr (Int_LE x y) = parens $ ppr x <+> docLE <+> ppr y
 ppr (Int_L x y) = parens $ ppr x <+> docL <+> ppr y
 ppr (Int_GE x y) = parens $ ppr x <+> docGE <+> ppr y
 ppr (Int_G x y) = parens $ ppr x <+> docG <+> ppr y
 ppr (Float_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
 ppr (Float_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
 ppr (Float_LE x y) = parens $ ppr x <+> docLE <+> ppr y
 ppr (Float_L x y) = parens $ ppr x <+> docL <+> ppr y
 ppr (Float_GE x y) = parens $ ppr x <+> docGE <+> ppr y
 ppr (Float_G x y) = parens $ ppr x <+> docG <+> ppr y
 ppr (Char_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
 ppr (Char_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
 ppr (Text_Eq x y) = ppr x <> fromText "." <> pfunction "equals" [ppr y]
 ppr (Key_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
 ppr (KeyCode_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
 -- Conditional
 ppr (Bool_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)

-- | Value of 'True'.
true :: Proc_Bool
true = Proc_True

-- | Value of 'False'.
false :: Proc_Bool
false = Proc_False

-- | Negation.
pnot :: Proc_Bool -> Proc_Bool
pnot = extendf not Proc_Neg

infixr 2 #||

-- | Disjunction.
(#||) :: Proc_Bool -> Proc_Bool -> Proc_Bool
(#||) = extendop (||) Proc_Or

infixr 3 #&&

-- | Conjunction.
(#&&) :: Proc_Bool -> Proc_Bool -> Proc_Bool
(#&&) = extendop (&&) Proc_And

-- | Cast a 'Bool' value.
fromBool :: Bool -> Proc_Bool
fromBool = extend

-- | Integer numbers.
data Proc_Int =
   Proc_Int Int
   -- Operations
 | Int_Sum Proc_Int Proc_Int
 | Int_Substract Proc_Int Proc_Int
 | Int_Divide Proc_Int Proc_Int
 | Int_Mult Proc_Int Proc_Int
 | Int_Mod Proc_Int Proc_Int
   -- Variables
 | Int_Var Text
   -- Functions
 | Int_Abs Proc_Int
 | Int_Floor Proc_Float
 | Int_Round Proc_Float
   -- Conditional
 | Int_Cond Proc_Bool Proc_Int Proc_Int
   deriving (Eq,Ord,Generic)

instance PArbitrary Proc_Int

instance Arbitrary Proc_Int where
 arbitrary = parbitrary

instance Extended Int Proc_Int where
 extend = Proc_Int
 patmatch (Proc_Int a) = Just a
 patmatch _ = Nothing

instance Pretty Proc_Int where
 ppr (Proc_Int i) = ppr i
 ppr (Int_Sum n m) = parens $ ppr n <> fromText "+" <> ppr m
 ppr (Int_Substract n m) = parens $ ppr n <> fromText "-" <> ppr m
 ppr (Int_Divide n m) = parens $ ppr n <> fromText "/" <> ppr m
 ppr (Int_Mult n m) = parens $ ppr n <> fromText "*" <> ppr m
 ppr (Int_Mod n m) = parens $ ppr n <> fromText "%" <> ppr m
 ppr (Int_Var t) = fromText t
 ppr (Int_Abs n) = pfunction "abs" [ppr n]
 ppr (Int_Floor x) = pfunction "floor" [ppr x]
 ppr (Int_Round x) = pfunction "round" [ppr x]
 ppr (Int_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)

-- | Cast an 'Int' value.
fromInt :: Int -> Proc_Int
fromInt = extend

-- | Calculate the 'floor' of a 'Proc_Float'.
pfloor :: Proc_Float -> Proc_Int
pfloor = extendf floor Int_Floor

-- | Round a number to the closest integer.
pround :: Proc_Float -> Proc_Int
pround = extendf round Int_Round

instance Enum Proc_Int where
 toEnum = fromInt
 fromEnum n = case patmatch n of
  Nothing -> error "Proc_Int: fromEnum applied to a variable."
  Just i -> i
 succ n = n + 1
 pred n = n - 1

-- | WARNING: 'signum' method is undefined.
instance Num Proc_Int where
 fromInteger = fromInt . fromInteger
 (+) = extendop (+) Int_Sum
 (-) = extendop (-) Int_Substract
 (*) = extendop (*) Int_Mult
 abs = extendf abs Int_Abs
 signum = error "Proc_Int: signum method is undefined."

instance Real Proc_Int where
 toRational n = case patmatch n of
   Nothing -> error "Proc_Int: toRational applied to a variable."
   Just i -> toRational i

instance Integral Proc_Int where
 div = extendop div Int_Divide
 mod = extendop mod Int_Mod
 quotRem n d = (div n d, mod n d)
 divMod = quotRem
 toInteger n = case patmatch n of
   Nothing -> error "Proc_Int: toInteger applied to a variable."
   Just i  -> toInteger i

-- | Floating point numbers.
--   The provided 'Eq' instance checks the equality of the
--   internal expression, not the value.
data Proc_Float =
   Proc_Float Float
   -- Operations
 | Float_Sum Proc_Float Proc_Float
 | Float_Substract Proc_Float Proc_Float
 | Float_Divide Proc_Float Proc_Float
 | Float_Mult Proc_Float Proc_Float
 | Float_Mod Proc_Float Proc_Float
 | Float_Neg Proc_Float
   -- Variables
 | Float_Var Text
   -- Functions
 | Float_Abs Proc_Float
 | Float_Exp Proc_Float
 | Float_Sqrt Proc_Float
 | Float_Log Proc_Float
 | Float_Sine Proc_Float
 | Float_Cosine Proc_Float
 | Float_Arcsine Proc_Float
 | Float_Arccosine Proc_Float
 | Float_Arctangent Proc_Float
 | Float_Floor Proc_Float -- Applies floor but it treats the result
                          -- as a float. Only internal.
 | Float_Round Proc_Float -- Same observation for Float_Floor.
 | Float_Noise Proc_Float Proc_Float
 | Float_Random Proc_Float Proc_Float
   -- Conditional
 | Float_Cond Proc_Bool Proc_Float Proc_Float
   deriving (Eq,Ord,Generic)

instance PArbitrary Proc_Float

instance Arbitrary Proc_Float where
 arbitrary = parbitrary

instance Extended Float Proc_Float where
 extend = Proc_Float
 patmatch (Proc_Float x) = Just x
 patmatch _ = Nothing

instance Pretty Proc_Float where 
 ppr (Proc_Float f) = ppr f
 ppr (Float_Sum x y) = parens $ ppr x <> fromText "+" <> ppr y
 ppr (Float_Substract x y) = parens $ ppr x <> fromText "-" <> ppr y
 ppr (Float_Divide x y) = parens $ ppr x <> fromText "/" <> ppr y
 ppr (Float_Mult x y) = parens $ ppr x <> fromText "*" <> ppr y
 ppr (Float_Neg x) = fromText "-" <> ppr x
 ppr (Float_Mod x y) = parens $ ppr x <> fromText "%" <> ppr y
 ppr (Float_Var t) = fromText t
 ppr (Float_Abs x) = pfunction "abs" [ppr x]
 ppr (Float_Exp x) = pfunction "exp" [ppr x]
 ppr (Float_Sqrt x) = pfunction "sqrt" [ppr x]
 ppr (Float_Log x) = pfunction "log" [ppr x]
 ppr (Float_Sine x) = pfunction "sin" [ppr x]
 ppr (Float_Cosine x) = pfunction "cos" [ppr x]
 ppr (Float_Arcsine x) = pfunction "asin" [ppr x]
 ppr (Float_Arccosine x) = pfunction "acos" [ppr x]
 ppr (Float_Arctangent x) = pfunction "atan" [ppr x]
 ppr (Float_Floor x) = pfunction "floor" [ppr x]
 ppr (Float_Round x) = pfunction "round" [ppr x]
 ppr (Float_Noise x y) = pfunction "noise" [ppr x,ppr y]
 ppr (Float_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
 ppr (Float_Random x y) = pfunction "random" [ppr x,ppr y]

-- | Cast a 'Float' value.
fromFloat :: Float -> Proc_Float
fromFloat = extend

-- | Noise random function.
noisef :: Proc_Float -> Proc_Float -> Proc_Float
noisef = Float_Noise

-- | Cast a 'Proc_Int' to a 'Proc_Float'.
intToFloat :: Proc_Int -> Proc_Float
intToFloat (Proc_Int i) = Proc_Float $ fromIntegral i
intToFloat (Int_Sum n m) = Float_Sum (intToFloat n) (intToFloat m)
intToFloat (Int_Substract n m) = Float_Substract (intToFloat n) (intToFloat m)
intToFloat (Int_Divide n m) = Float_Divide (intToFloat n) (intToFloat m)
intToFloat (Int_Mult n m) = Float_Mult (intToFloat n) (intToFloat m)
intToFloat (Int_Mod n m) = Float_Mod (intToFloat n) (intToFloat m)
intToFloat (Int_Var t) = Float_Var t
intToFloat (Int_Abs n) = Float_Abs $ intToFloat n
intToFloat (Int_Floor x) = Float_Floor x
intToFloat (Int_Round x) = Float_Round x
intToFloat (Int_Cond b x y) = Float_Cond b (intToFloat x) (intToFloat y)

-- | WARNING: 'signum' method is undefined.
instance Num Proc_Float where
 fromInteger = fromFloat . fromInteger
 (+) = extendop (+) Float_Sum
 (-) = extendop (-) Float_Substract
 (*) = extendop (*) Float_Mult
 abs = extendf abs Float_Abs
 negate = extendf negate Float_Neg
 signum = error "Proc_Float: signum method is undefined."

instance Fractional Proc_Float where
 (/) = extendop (/) Float_Divide
 fromRational = fromFloat . fromRational

-- | WARNING: 'sinh', 'cosh', 'asinh', 'acosh' and 'atanh'
--   methods are undefined. They are not present in
--   processing.js.
instance Floating Proc_Float where
 pi = extend pi
 exp = extendf exp Float_Exp
 sqrt = extendf sqrt Float_Sqrt
 log = extendf log Float_Log
 sin = extendf sin Float_Sine
 cos = extendf cos Float_Cosine
 asin = extendf asin Float_Arcsine
 acos = extendf acos Float_Arccosine
 atan = extendf atan Float_Arctangent
 -- UNDEFINED
 sinh = error "Proc_Float: sinh method is undefined."
 cosh = error "Proc_Float: cosh method is undefined."
 asinh = error "Proc_Float: asinh method is undefined."
 acosh = error "Proc_Float: acosh method is undefined."
 atanh = error "Proc_Float: atanh method is undefined."

-- | Type of images.
data Proc_Image =
   Image_Var Text
 | Image_Cond Proc_Bool Proc_Image Proc_Image
   deriving (Eq,Generic)

instance PArbitrary Proc_Image

instance Arbitrary Proc_Image where
 arbitrary = parbitrary

instance Pretty Proc_Image where
 ppr (Image_Var t) = fromText t
 ppr (Image_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)

-- | Type of characters.
data Proc_Char =
   Proc_Char Char
 | Char_Var Text
 | Char_Cond Proc_Bool Proc_Char Proc_Char
   deriving (Eq,Ord,Generic)

instance PArbitrary Proc_Char

instance Arbitrary Proc_Char where
 arbitrary = parbitrary

instance Extended Char Proc_Char where
 extend = Proc_Char
 patmatch (Proc_Char c) = Just c
 patmatch _ = Nothing

-- | Cast a 'Char' value.
fromChar :: Char -> Proc_Char
fromChar = extend

instance Pretty Proc_Char where
 ppr (Proc_Char c) = enclose squote squote (char c)
 ppr (Char_Var n) = fromText n
 ppr (Char_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)

-- | Type of textual values.
--
--   It is recommended to enable the @OverloadedStrings@ extension.
--   Note that 'Proc_Text' is an instance of the 'IsString' class.
data Proc_Text =
   Proc_Text Text
 | Text_Var Text
   -- Proc_Show
   -- See <http://processingjs.org/reference/str_> for avaiable
   -- types.
 | Show_Bool Proc_Bool
 | Show_Char Proc_Char
 | Show_Float Proc_Float
 | Show_Int Proc_Int
   -- String appending
 | Text_Append Proc_Text Proc_Text
   -- Conditional
 | Text_Cond Proc_Bool Proc_Text Proc_Text
   deriving (Eq,Ord,Generic)
 
infixr 5 +.+

-- | Append two text strings.
(+.+) :: Proc_Text -> Proc_Text -> Proc_Text
(+.+) = Text_Append

instance PArbitrary Proc_Text

instance Arbitrary Proc_Text

instance Extended Text Proc_Text where
 extend = Proc_Text
 patmatch (Proc_Text t) = Just t
 patmatch _ = Nothing

instance Pretty Proc_Text where
 -- Wrong pretty-printer for text values. Fix it to
 -- escape characters.
 ppr (Proc_Text t) = enclose dquote dquote (fromText t)
 ppr (Text_Var n) = fromText n
 --
 ppr (Show_Bool  x) = pfunction "str" [ppr x]
 ppr (Show_Char  x) = pfunction "str" [ppr x]
 ppr (Show_Float x) = pfunction "str" [ppr x]
 ppr (Show_Int   x) = pfunction "str" [ppr x]
 -- No parenthesis are included since appending is the
 -- only operation over text.
 ppr (Text_Append x y) = ppr x <+> fromText "+" <+> ppr y
 --
 ppr (Text_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)

-- | Cast a strict 'Text' value.
fromStText :: Text -> Proc_Text
fromStText = extend

instance IsString Proc_Text where
 fromString = fromStText . fromString

-- | Similar to the 'Show' class, but for @Proc_*@ types.
class Proc_Show a where
  -- | Render a value as a 'Proc_Text'.
  pshow :: a -> Proc_Text

instance Proc_Show Proc_Bool where
  pshow = Show_Bool

instance Proc_Show Proc_Char where
  pshow = Show_Char

instance Proc_Show Proc_Float where
  pshow = Show_Float

instance Proc_Show Proc_Int where
  pshow = Show_Int

-- | Type of keyboard keys.
data Proc_Key =
   Key_Var
 | Key_CODED
 | Key_Char Char
   deriving (Eq,Ord,Generic)

instance PArbitrary Proc_Key

instance Pretty Proc_Key where
 ppr Key_Var = fromText "key"
 ppr Key_CODED = fromText "CODED"
 ppr (Key_Char c) = ppr $ fromChar c

-- | Type of keyboard key codes.
data Proc_KeyCode =
   KeyCode_Var
 | KeyCode_UP
 | KeyCode_DOWN
 | KeyCode_LEFT
 | KeyCode_RIGHT
 | KeyCode_ALT
 | KeyCode_CONTROL
 | KeyCode_SHIFT
 | KeyCode_BACKSPACE
 | KeyCode_TAB
 | KeyCode_ENTER
 | KeyCode_RETURN
 | KeyCode_ESC
 | KeyCode_DELETE
   deriving (Eq,Ord,Generic)

instance PArbitrary Proc_KeyCode

instance Pretty Proc_KeyCode where
 ppr KeyCode_Var = fromText "keyCode"
 ppr KeyCode_UP = fromText "UP"
 ppr KeyCode_DOWN = fromText "DOWN"
 ppr KeyCode_LEFT = fromText "LEFT"
 ppr KeyCode_RIGHT = fromText "RIGHT"
 ppr KeyCode_ALT = fromText "ALT"
 ppr KeyCode_CONTROL = fromText "CONTROL"
 ppr KeyCode_SHIFT = fromText "SHIFT"
 ppr KeyCode_BACKSPACE = fromText "BACKSPACE"
 ppr KeyCode_TAB = fromText "TAB"
 ppr KeyCode_ENTER = fromText "ENTER"
 ppr KeyCode_RETURN = fromText "RETURN"
 ppr KeyCode_ESC = fromText "ESC"
 ppr KeyCode_DELETE = fromText "DELETE"


-- END OF PROC_* TYPES
----------------------------------------------
----------------------------------------------

{- Proc_* type mechanics

Three types are automatically generated for the
Proc_* types. These are ProcArg, ProcAssign and ProcList.
A processing command may receive several arguments
of the same or different Proc_* types.
We encode arguments under the ProcArg type. The
ProcArg type is the disjoint union of the different
Proc_* types. For a complete list, see the
Graphics.Web.Processing.Core.TH module.
The list is called procTypeNames.

In the other hand, variable assignments are also
encoded in a particular type, named ProcAssign.
The ProcAssign type is the disjoint union of the
product of each Proc_* type with Text. This means
that a value of this type contains a value of any
of the different Proc_* types together with a value
of type Text. This text represents the name of the
variable in the assignment.

ProcList is a type for lists of processing values,
used in the implementation of arrays. The ProcList
type is the disjoint union of each Proc_* type
embedded into a list.

All ProcArg, ProcAssign and ProcList are generated
automatically, together with instances of the
Pretty class, by procTypeMechs.
Compile with "-f info" to see the generated
code. The structure for each Proc_* type is as
follows:

data ProcArg = (union in *) *Arg Proc_*

instance Pretty ProcArg where
  (for each *) ppr (*Arg x) = ppr x

data ProcAssign = (union in *) *Assign Text Proc_*

instance Pretty ProcAssign where
  (for each *) ppr (*Assign t x) =
     fromText t <+> fromText "=" <+> ppr x

data ProcList = (union in *) *List [Proc_*]

instance Pretty ProcList where
  (for each *) ppr (*List xs) =
     fromText "{" <> commasep (fmap ppr xs) <> fromText "}"

In addition, the following two functions are defined.

ptype :: ProcAssign -> Doc
(for each *) ptype (*Assign _ _) = fromText "Name of * in processing.js"

ltype :: ProcList -> Doc
(for each *) ltype (*List _) = fromText $ "Name of * in processing.js" ++ "[]"

-}

$(procTypeMechs)

instance PArbitrary ProcArg

instance Arbitrary ProcArg where
 arbitrary = parbitrary

instance PArbitrary ProcAssign

instance PArbitrary ProcList

-- CODE

-- | A piece of Processing code.
--   The type parameter indicates what the
--   context of the code is.
--   This context will allow or disallow
--   the use of certain commands along
--   different events.
data ProcCode c = 
   Command Text [ProcArg] 
 | CreateVar ProcAssign
 | CreateArrayVar Text ProcList
 | Assignment ProcAssign
 | Conditional Proc_Bool   -- IF
              (ProcCode c) -- THEN
              (ProcCode c) -- ELSE
 | Comment Text
 | Sequence (Seq.Seq (ProcCode c))
   deriving (Generic,Eq)

instance PArbitrary (ProcCode c)

instance Arbitrary (ProcCode c) where
 arbitrary = parbitrary

instance Pretty (ProcCode c) where
 ppr (Command n as) = pfunction n (fmap ppr as) <+> fromText ";"
 -- ptype is defined by $(procTypeMechs).
 ppr (CreateVar a) = ptype a <+> ppr a <+> fromText ";"
 -- ltype is defined by $(procTypeMechs).
 ppr (CreateArrayVar n xs) = ltype xs <+> fromText n <+> fromText "="
                         <+> ppr xs <+> fromText ";"
 ppr (Assignment a) = ppr a <+> fromText ";"
 ppr (Conditional b e1 e2) =
   let c1 = indent indentLevel $ ppr e1
       c2 = indent indentLevel $ ppr e2
   in  pfunction "if" [ppr b]
   <+> enclose lbrace rbrace (line <> c1)
   <+> if e2 == mempty then mempty
                       else fromText "else" <+> enclose lbrace rbrace (line <> c2)
 ppr (Comment t) = stack $ fmap (fromText . ("// " <>)) $ Data.Text.lines t
 ppr (Sequence sq) =
   if Seq.null sq then Text.PrettyPrint.Mainland.empty
                  else foldMap ((<> line) . ppr) sq

-- | Sequence to pieces of code with the same
--   context type. This way, code that belongs
--   to different parts of the program will
--   never get mixed.
(>>.) :: ProcCode c -> ProcCode c -> ProcCode c
(Sequence xs) >>. (Sequence ys) = Sequence $ xs Seq.>< ys
(Sequence xs) >>. p = if Seq.null xs 
                         then p
                         else Sequence $ xs Seq.|> p
p >>. (Sequence xs) = if Seq.null xs
                         then p
                         else Sequence $ p Seq.<| xs
p >>. q = Sequence $ Seq.fromList [p,q]

-- | An empty piece of code.
emptyCode :: ProcCode a
emptyCode = Sequence $ Seq.empty

instance Monoid (ProcCode a) where
 mempty = emptyCode
 mappend = (>>.)

---- ProcType class and instances

-- | Type of variables.
data Var a = Var { -- | Get the name of a variable.
                   varName :: Text }

-- | Internal function to create variables.
varFromText :: Text -> Var a
varFromText = Var

-- | Type of variables storing arrays.
data ArrayVar a =
  ArrayVar { -- | Size of the array.
             arraySize :: Int
           , innerVar :: Var a }

-- | Get the name of a variable storing an array.
arrayVarName :: ArrayVar a -> Text
arrayVarName = varName . innerVar

-- | Internal function to create array variables.
arrayVarFromText :: Int -> Text -> ArrayVar a
arrayVarFromText n t = ArrayVar n (Var t)

-- | Translate an Array variable to the correspondent
--   component variable.
arrayVarToVar :: ArrayVar a -> Proc_Int -> Var a
arrayVarToVar v n = varFromText $ arrayVarName v <> "[" <> f n <> "]"
  where
   f = toStrict . prettyLazyText 80 . ppr

----- CLASSES

-- | Class of Processing value types (@Proc_*@ types).
--
--   @Proc_*@ types are types from the world of Processing.
--   Some of them are similar to Haskell types, like 'Proc_Bool'
--   and 'Bool'. However, they are not equal. @Proc_*@ types
--   are instance of 'Eq'. However, you should instead use methods from
--   the analog 'Proc_Eq' class. @Proc_*@ types contain expressions instead
--   of values. Think of @2+2@ instead of @4@. Under this situation,
--   @2+2 /= 3+1@, since they are different expressions, even if they
--   evaluate to the same value. Actually, you will get 'True'
--   from the evaluation of @2+2 == 3+1@, since the library is smart
--   enough to figure out they have the same value. But, please, don't
--   rely on this. Use the 'Proc_Eq' and 'Proc_Ord' classes instead.
--   They return Processing boolean expressions instead of 'Bool' values.
--   Anyway, the types of the library will try to force you to use @Proc_*@
--   types everywhere.
--
--   The reason this library stores expressions instead of values is that
--   it needs to handle things like @2+x@, where @x@ is an unknown value.
--   However, an effort is done to ensure that each expression is reduced
--   to its minimum extension.
class ProcType a where
 -- | Create a variable assignment, provided
 --   the name of the variable and the value to asign.
 proc_assign :: Text -> a -> ProcAssign
 -- | Create a list.
 proc_list :: [a] -> ProcList
 -- | Create an argument for a command.
 proc_arg :: a -> ProcArg
 -- | Variable reading.
 proc_read :: Var a -> a
 -- | Conditional value.
 proc_cond :: Proc_Bool -> a -> a -> a
 -- | Check if a variable is contained in an expression.
 checkForVar :: Text -> a -> Bool

{- Template Haskell and Proc_* types.

Template Haskell is used in order to derive instances
of the ProcType class. These instances consist merely
in select the appropiate data constructor of the
appropiate datatype. Use "-f info" when compiling
with cabal to see the generated instances. This is
the general format for each Proc_* type:

instance ProcType Proc_* where
  proc_assign = *Assign
  proc_list = *List
  proc_arg = *Arg
  proc_read (Var v) = *_Var v
  proc_cond = *_Cond

-}

$(deriveProcTypeInsts)

{- Eq and Ord classes for Proc_* types

Since Proc_* types represent expressions,
they cannot be compared in the usual way.
We cannot decide if two expressions will reduce
to the same value. In fact, sometimes they will,
and sometimes they will not. What we can do is
to, given two expressions, return a boolean
expression, which will evaluate to the correct
value in the appropiate context.

Therefore, we define the Proc_Eq and Proc_Ord
classes similarly to Eq and Ord, but returning
a Proc_Bool value instead of a Bool value.

Operators have the same name than their
analagous, but preceded with #.

-}

infix 4 #==, #/=

-- | 'Eq' class for @Proc_*@ values.
class Proc_Eq a where
 (#==) :: a -> a -> Proc_Bool
 (#/=) :: a -> a -> Proc_Bool
 -- Minimal default instance: (#==) or (#/=).
 x #== y = pnot $ x #/= y
 x #/= y = pnot $ x #== y

instance Proc_Eq Proc_Bool where
 (#==) = Bool_Eq
 (#/=) = Bool_NEq

instance Proc_Eq Proc_Int where
 (#==) = Int_Eq
 (#/=) = Int_NEq

instance Proc_Eq Proc_Float where
 (#==) = Float_Eq
 (#/=) = Float_NEq

instance Proc_Eq Proc_Char where
 (#==) = Char_Eq
 (#/=) = Char_NEq

instance Proc_Eq Proc_Text where
 (#==) = Text_Eq

instance Proc_Eq Proc_Key where
 (#==) = Key_Eq

instance Proc_Eq Proc_KeyCode where
 (#==) = KeyCode_Eq

infix 4 #<=, #<, #>=, #>

-- | 'Ord' class for @Proc_*@ values.
class Proc_Ord a where
 (#<=) :: a -> a -> Proc_Bool
 (#<)  :: a -> a -> Proc_Bool
 (#>=) :: a -> a -> Proc_Bool
 (#>)  :: a -> a -> Proc_Bool

instance Proc_Ord Proc_Int where
 (#<=) = Int_LE
 (#<)  = Int_L
 (#>=) = Int_GE
 (#>)  = Int_G

instance Proc_Ord Proc_Float where
 (#<=) = Float_LE
 (#<)  = Float_L
 (#>=) = Float_GE
 (#>)  = Float_G

{- Proc_* types and recursion

Since some of the Proc_* types are recursive,
we derive the correspondent instances using
Template Haskell. Otherwise, they would be
long and tedious.

-}

$(deriveRecursive ''Proc_Bool)
$(deriveRecursive ''Proc_Int)
$(deriveRecursive ''Proc_Float)

-- | Class of reducible types. Values of these
--   types contain expressions that can be
--   reducible.
class Eq a => Reducible a where
 reduce :: a -> a

-- | Find a fix point of the 'reduce' function from
--   any value. If 'reduce' is well defined, this function
--   must end.
iteratedReduce :: Reducible a => a -> a
iteratedReduce = fst . firstWith (uncurry (==)) . pairing . iterate reduce
 where
  pairing (x:y:xs) = (x,y) : pairing (y:xs)
  pairing _ = []
  firstWith f (x:xs) = if f x then x else firstWith f xs
  firstWith _ _ = error "Error in iterated reduction. Report this as a bug."

{-

FLOAT REDUCTION

Float is probably the most common argument type.
Below a case-by-case analysis tries to reduce a
float expression to its minimal extension.
The more we reduce, the more effective
will be the Processing output code, since we save
operations.

-}

instance Reducible Proc_Float where
 -- Distribution
 reduce f@(Float_Sum (Float_Mult x y) (Float_Mult x' y'))
   | x == x' = reduce $ x * (y + y')
   | y == y' = reduce $ y * (x + x')
   | otherwise = recursor reduce f
 -- x + (-y) = x - y
 reduce (Float_Sum x (Float_Neg y)) = reduce $ Float_Substract x y
 -- (-x) + y = y - x
 reduce (Float_Sum (Float_Neg x) y) = reduce $ Float_Substract y x
 --
 reduce (Float_Sum x y)
   | x == y = 2 * reduce x
   | x == 0 = reduce y
   | y == 0 = reduce x
   | otherwise = reduce x + reduce y
 -- x - (-y) = x + y
 reduce (Float_Substract x (Float_Neg y)) = reduce $ Float_Sum x y
 reduce (Float_Substract x y)
   | x == y = 0
   | x == 0 = reduce $ negate y
   | y == 0 = reduce x
   | otherwise = reduce x - reduce y
 reduce (Float_Mult x y)
   | x == y = reduce x ** 2
   | x == 1 = reduce y
   | y == 1 = reduce x
   | otherwise = reduce x * reduce y
 -- (x*y)/z = y*(x/z)
 reduce (Float_Divide (Float_Mult (Proc_Float x) y) (Proc_Float z)) =
   reduce $ (y*) $ Proc_Float $ x / z
 -- (x*y)/z = x*(y/z)
 reduce (Float_Divide (Float_Mult x (Proc_Float y)) (Proc_Float z)) =
   reduce $ (x*) $ Proc_Float $ y / z
 reduce x = recursor reduce x

instance Reducible ProcArg where
 reduce (FloatArg x) = FloatArg $ iteratedReduce x
 reduce x = x

instance Reducible (ProcCode c) where
 reduce (Sequence sq) = Sequence $ fmap reduce $ foldr (
   \x xs -> 
     case Seq.viewl xs of
      y Seq.:< ys -> case reduceProcPair x y of
                       Nothing -> x Seq.<| xs
                       Just z  -> z Seq.<| ys
      Seq.EmptyL -> Seq.singleton x
        ) Seq.empty sq
 reduce (Command n xs) = Command n $ fmap reduce xs
 reduce x = x

reduceProcPair :: ProcCode c -> ProcCode c -> Maybe (ProcCode c)
-- Translations
reduceProcPair (Command "translate" [FloatArg 0,FloatArg 0]) x = Just x
reduceProcPair x (Command "translate" [FloatArg 0,FloatArg 0]) = Just x
reduceProcPair (Command "translate" [FloatArg x,FloatArg y])
               (Command "translate" [FloatArg x',FloatArg y'])
                 = Just $ Command "translate" [ FloatArg $ x+x'
                                              , FloatArg $ y+y']
-- Rotations
reduceProcPair (Command "rotate" [FloatArg 0]) x = Just x
reduceProcPair x (Command "rotate" [FloatArg 0]) = Just x
reduceProcPair (Command "rotate" [FloatArg x])
               (Command "rotate" [FloatArg x'])
                 = Just $ Command "rotate" [FloatArg $ x+x']
reduceProcPair _ _ = Nothing

----

-- | A complete Processing script.
--
-- It consists in several parts, most of them optional.
--
-- To generate each part of the code, use the 'ProcM' monad
-- and the functions from the "Graphics.Web.Processing.Interface"
-- module. Then, run 'runProcM' or 'execProcM' to get the
-- code result.
--
-- More abstract functions generate 'ProcScript' values as well.
-- See modules "Graphics.Web.Processing.Mid" and "Graphics.Web.Processing.Simple"
-- for two alternative ways.
data ProcScript = ProcScript
 { proc_preamble :: ProcCode Preamble
 , proc_setup :: ProcCode Setup
 , proc_draw  :: Maybe (ProcCode Draw)
 , proc_mouseClicked :: Maybe (ProcCode MouseClicked)
 , proc_mouseReleased :: Maybe (ProcCode MouseReleased)
 , proc_keyPressed :: Maybe (ProcCode KeyPressed)
   } deriving (Eq,Generic)

instance PArbitrary ProcScript

instance Arbitrary ProcScript where
 arbitrary = parbitrary

-- | Empty script.
emptyScript :: ProcScript
emptyScript = ProcScript {
   proc_preamble = emptyCode
 , proc_setup = emptyCode
 , proc_draw = Nothing
 , proc_mouseClicked = Nothing
 , proc_mouseReleased = Nothing
 , proc_keyPressed = Nothing
   }

pvoid :: Pretty a => Text -> Maybe a -> Doc
pvoid _ Nothing = Text.PrettyPrint.Mainland.empty
pvoid n (Just c) = fromText "void" <+> fromText n <> fromText "()" <+> enclose lbrace rbrace inside
 where
  inside = line <> indent indentLevel (ppr c) <> line

instance Pretty ProcScript where
 ppr ps = stack [ 
     ppr $ proc_preamble ps
   , pvoid "setup" $ Just $ proc_setup ps
   , pvoid "draw" $ proc_draw ps
   , pvoid "mouseClicked" $ proc_mouseClicked ps
   , pvoid "mouseReleased" $ proc_mouseReleased ps
   , pvoid "keyPressed" $ proc_keyPressed ps
     ]