-- | Given some 'Parameter' set and the arguments, match attempts to assign
--   local variables to these arguments base on the parameters described.
--   The result an action yielding an Int which is the zero-index of the 
--   alternative which succeeded. If there are no alternatives it returns 0.
--   If the match fails a PatternMatchError is thrown.
match :: Scope m => Parameter -> [Object] -> m (Int)
match = match' 0
  where
    match' :: Scope m => Int -> Parameter -> [Object] -> m (Int)
    match' i Empty [] = return i
    match' _ Empty xs = throwError "PatternMatchError: Too many parameters"
    match' _ (P _ _) [] = throwError "PatternMatchError: Too few parameters"
    match' i (P n next) (x:xs) = setLocal n x >> match' i next xs
    match' i (Asterisk n) xs = setLocal n (varray xs) >> return i
    match' i (Default n _ next) (x:xs) = setLocal n x >> match' i next xs
    match' i (Default n x next) [] = setLocal n x >> match' i next []
    match' _ (Pattern _ _ _) [] = throwError "PatternMatchError: Too few parameters"
    match' i (Pattern cls n next) (x:xs) = do
      r <- call (Just $ RO x) "kind_of" [fromString cls]
      case (o r) of 
         TrueClass -> setLocal n x >> match' i next xs
         otherwise -> throwError "PatternMatchError: Pattern failed" -- todo add information n is not a cls
    match' i (Alternatives xs) ps = alternatives i [] xs ps
  
    alternitives :: Scope m => Int -> [Object] -> [Parameter] -> [Object] -> m ()
    alternitives _ errs [] _ = throwError $ varray ("PatternMatchError: No matching pattern":errs)
    alternatives i [] [p] ps = match' i p ps
    alternatives i errs (x:xs) ps = match' i x ps `catchError` (\err -> alternatives (i+1) (err:errs) xs ps)

-- | determines the posible arity of a parameter set
match :: Scope m => Parameter -> [Object] -> m (Int)
match = match' 0
  where
    match' :: Scope m => Int -> Parameter -> [Object] -> m (Int)
    match' i Empty [] = return i
    match' _ Empty xs = throwError "PatternMatchError: Too many parameters"
    match' _ (P _ _) [] = throwError "PatternMatchError: Too few parameters"
    match' i (P n next) (x:xs) = setLocal n x >> match' i next xs
    match' i (Asterisk n) xs = setLocal n (varray xs) >> return i
    match' i (Default n _ next) (x:xs) = setLocal n x >> match' i next xs
    match' i (Default n x next) [] = setLocal n x >> match' i next []
    match' _ (Pattern _ _ _) [] = throwError "PatternMatchError: Too few parameters"
    match' i (Pattern cls n next) (x:xs) = do
      r <- call (Just $ RO x) "kind_of" [fromString cls]
      case (o r) of 
         TrueClass -> setLocal n x >> match' i next xs
         otherwise -> throwError "PatternMatchError: Pattern failed" -- todo add information n is not a cls
    match' i (Alternatives xs) ps = alternatives i [] xs ps
  
    alternitives :: Scope m => Int -> [Object] -> [Parameter] -> [Object] -> m ()
    alternitives _ errs [] _ = throwError $ varray ("PatternMatchError: No matching pattern":errs)
    alternatives i [] [p] ps = match' i p ps
    alternatives i errs (x:xs) ps = match' i x ps `catchError` (\err -> alternatives (i+1) (err:errs) xs ps)

-- | determines the posible arity of a parameter set
match = match' 0
  where
    match' :: Scope m => Int -> Parameter -> [Object] -> m (Int)
    match' i Empty [] = return i
    match' _ Empty xs = throwError "PatternMatchError: Too many parameters"
    match' _ (P _ _) [] = throwError "PatternMatchError: Too few parameters"
    match' i (P n next) (x:xs) = setLocal n x >> match' i next xs
    match' i (Asterisk n) xs = setLocal n (varray xs) >> return i
    match' i (Default n _ next) (x:xs) = setLocal n x >> match' i next xs
    match' i (Default n x next) [] = setLocal n x >> match' i next []
    match' _ (Pattern _ _ _) [] = throwError "PatternMatchError: Too few parameters"
    match' i (Pattern cls n next) (x:xs) = do
      r <- call (Just $ RO x) "kind_of" [fromString cls]
      case (o r) of 
         TrueClass -> setLocal n x >> match' i next xs
         otherwise -> throwError "PatternMatchError: Pattern failed" -- todo add information n is not a cls
    match' i (Alternatives xs) ps = alternatives i [] xs ps
  
    alternitives :: Scope m => Int -> [Object] -> [Parameter] -> [Object] -> m ()
    alternitives _ errs [] _ = throwError $ varray ("PatternMatchError: No matching pattern":errs)
    alternatives i [] [p] ps = match' i p ps
    alternatives i errs (x:xs) ps = match' i x ps `catchError` (\err -> alternatives (i+1) (err:errs) xs ps)

-- | determines the posible arity of a parameter set