module Language.ImProve.Core
  ( E (..)
  , V (..)
  , Name
  , Path
  , PathName (..)
  , AllE (..)
  , NumE
  , Const (..)
  , Statement (..)
  , VarInfo
  , varInfo
  , stmtVars
  , exprVars
  ) where

import Data.List
import Data.Ratio

type Name = String

type Path = [Name]

-- | A mutable variable.
data V a = V Bool [Name] a deriving Eq

class    PathName a         where pathName :: a -> String
instance PathName Path      where pathName = intercalate "."
instance PathName (V a)     where pathName (V _ path _) = pathName path
instance PathName VarInfo   where pathName (_, path, _) = pathName path

class AllE a where
  zero   :: (Name -> a -> m (V a)) -> a
  const' :: a -> Const

instance AllE Bool where
  zero = const False
  const' = Bool

instance AllE Int where
  zero = const 0
  const' = Int
  
instance AllE Float where
  zero = const 0
  const' = Float

class    AllE a => NumE a
instance NumE Int
instance NumE Float

-- | A logical, arithmetic, comparative, or conditional expression.
data E a where
  Ref   :: AllE a => V a -> E a
  Const :: AllE a => a -> E a
  Add   :: NumE a => E a -> E a -> E a
  Sub   :: NumE a => E a -> E a -> E a
  Mul   :: NumE a => E a -> a -> E a
  Div   :: NumE a => E a -> a -> E a
  Mod   :: E Int -> Int -> E Int
  Not   :: E Bool -> E Bool
  And   :: E Bool -> E Bool -> E Bool
  Or    :: E Bool -> E Bool -> E Bool
  Eq    :: AllE a => E a -> E a -> E Bool
  Lt    :: NumE a => E a -> E a -> E Bool
  Gt    :: NumE a => E a -> E a -> E Bool
  Le    :: NumE a => E a -> E a -> E Bool
  Ge    :: NumE a => E a -> E a -> E Bool
  Mux   :: AllE a => E Bool -> E a -> E a -> E a

instance Show (E a) where show = undefined 
instance Eq   (E a) where (==) = undefined

instance (Num a, AllE a, NumE a) => Num (E a) where
  (+) = Add
  (-) = Sub
  (*) = error "general multiplication not supported, use (*.)"
  negate a = 0 - a
  abs a = Mux (Lt a 0) (negate a) a
  signum a = Mux (Eq a 0) 0 $ Mux (Lt a 0) (-1) 1
  fromInteger = Const . fromInteger

instance Fractional (E Float) where
  (/) = error "general division not supported, use (/.)"
  recip a = 1 / a
  fromRational r = Const $ fromInteger (numerator r) / fromInteger (denominator r)

data Statement
  = AssignBool  (V Bool ) (E Bool )
  | AssignInt   (V Int  ) (E Int  )
  | AssignFloat (V Float) (E Float)
  | Branch      (E Bool) Statement Statement
  | Sequence    Statement Statement
  | Assert      (E Bool)
  | Assume      (E Bool)
  | Label       Name Statement
  | Null

data Const
  = Bool  Bool
  | Int   Int
  | Float Float
  deriving (Eq, Ord)

type VarInfo = (Bool, Path, Const)

varInfo :: AllE a => V a -> VarInfo
varInfo (V input path init) = (input, path, const' init)

-- Information about all of a program's variables.
stmtVars :: Statement -> [VarInfo]
stmtVars a = case a of
  AssignBool  a b -> nub $ varInfo a : exprVars b
  AssignInt   a b -> nub $ varInfo a : exprVars b
  AssignFloat a b -> nub $ varInfo a : exprVars b
  Branch a b c    -> nub $ exprVars a ++ stmtVars b ++ stmtVars c
  Sequence a b    -> nub $ stmtVars a ++ stmtVars b
  Assert a        -> exprVars a
  Assume a        -> exprVars a
  Label  _ a      -> stmtVars a
  Null            -> []

-- Information about all of an expressions's variables.
exprVars :: E a -> [VarInfo]
exprVars a = case a of
  Ref a     -> [varInfo a]
  Const _   -> []
  Add a b   -> exprVars a ++ exprVars b
  Sub a b   -> exprVars a ++ exprVars b
  Mul a _   -> exprVars a
  Div a _   -> exprVars a
  Mod a _   -> exprVars a
  Not a     -> exprVars a
  And a b   -> exprVars a ++ exprVars b
  Or  a b   -> exprVars a ++ exprVars b
  Eq  a b   -> exprVars a ++ exprVars b
  Lt  a b   -> exprVars a ++ exprVars b
  Gt  a b   -> exprVars a ++ exprVars b
  Le  a b   -> exprVars a ++ exprVars b
  Ge  a b   -> exprVars a ++ exprVars b
  Mux a b c -> exprVars a ++ exprVars b ++ exprVars c