module Language.Modelica.Parser.Equation where

import Language.Modelica.Syntax.Modelica

import Language.Modelica.Parser.Parser (Parser)
import Language.Modelica.Parser.Lexer
import Language.Modelica.Parser.Expression
import Language.Modelica.Parser.Modification
import Language.Modelica.Parser.Utility (eitherOr)

import Control.Applicative
  (liftA2, liftA3, (*>), (<$>), (<*>), (<*))

import Text.ParserCombinators.Parsec ((<|>), many, optionMaybe, try)



equations :: Parser [Equation]
equations = many (try equation <* semicolon)

equation_section :: Parser EquationSection
equation_section = liftA2 EquationSection
  (optionMaybe init_) (equation_ *> equations)

equation :: Parser Equation
equation = liftA2 Equation
  ( if_equation
    <|> for_equation
    <|> connect_clause
    <|> when_equation
    <|> try (liftA2 Eqn simple_expression (assign *> expression) )
    <|> liftA2 EqFunctionCall name function_call_args )
  comment

if_equation :: Parser Eqn
if_equation = IfEquation <$>
  (if_ *> expression <* then_) <*>
  equations <*>
  (many else_if_equation) <*>
  (optionMaybe (else_ *> equations) <* end_if_)

else_if_equation :: Parser ElseIfEquation
else_if_equation = liftA2 ElseIfEquation
  (elseif_ *> expression <* then_) equations


for_equation :: Parser Eqn
for_equation = liftA2 ForEquation
  (for_ *> for_indices <* loop_)
  (equations <* end_for_)

when_equation :: Parser Eqn
when_equation = liftA3 WhenEquation
  (when_ *> expression <* then_)
  equations
  (many else_when_equation <* end_when_)

else_when_equation :: Parser ElseWhenEquation
else_when_equation = liftA2 ElseWhenEquation
  (elsewhen_ *> expression <* then_)
  equations

connect_clause :: Parser Eqn
connect_clause = 
  connect_ *> (parens $
  liftA2 ConnectClause component_reference (comma *> component_reference))


statements :: Parser [Statement]
statements = many (try statement <* semicolon)

algorithm_section :: Parser AlgorithmSection
algorithm_section = liftA2 AlgorithmSection
  (optionMaybe init_) (algorithm_ *> statements)

statement :: Parser Statement
statement = liftA2 Statement
  ( break_
    <|> return_
    <|> if_statement
    <|> for_statement
    <|> when_statement
    <|> while_statement
    <|> comp_ref_statement
    <|> output_list_statement )
  comment


if_statement :: Parser Stmt
if_statement = IfStatement <$>
  (if_ *> expression <* then_) <*>
  statements <*>
  (many else_if_statement) <*>
  (optionMaybe (else_ *> statements) <* end_if_)

else_if_statement :: Parser ElseIfStatement
else_if_statement = liftA2 ElseIfStatement
  (elseif_ *> expression <* then_) statements


for_statement :: Parser Stmt
for_statement = liftA2 ForStatement
  (for_ *> for_indices <* loop_)
  (statements <* end_for_)


when_statement :: Parser Stmt
when_statement = liftA3 WhenStatement
  (when_ *> expression <* then_)
  statements
  (many else_when_statement <* end_when_)

else_when_statement :: Parser ElseWhenStatement
else_when_statement = liftA2 ElseWhenStatement
  (elsewhen_ *> expression <* then_)
  statements

while_statement :: Parser Stmt
while_statement = liftA2 WhileStatement
  (while_ *> expression <* loop_)
  (statements <* end_while_)


comp_ref_statement :: Parser Stmt
comp_ref_statement = liftA2 CompRefStatement
  component_reference
  ((colon_assign *> expression) `eitherOr` function_call_args)

output_list_statement :: Parser Stmt
output_list_statement = liftA3 OutputListStatement
  (parens output_expression_list)
  (colon_assign *> component_reference)
  function_call_args