#!/usr/bin/env python
# -*- coding: utf-8 -*-

#-------------------------------------------------------------------------------

# This file is part of Code_Saturne, a general-purpose CFD tool.
#
# Copyright (C) 1998-2019 EDF S.A.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
# Street, Fifth Floor, Boston, MA 02110-1301, USA.

#-------------------------------------------------------------------------------

import os
import re

from code_saturne.model.NotebookModel import NotebookModel
from code_saturne.model.SolutionDomainModel import getRunType

#===============================================================================
# Code block templates
#===============================================================================

#-------------------------------------------------------------------------------

_file_header = \
"""/*----------------------------------------------------------------------------*/
/*
  This file is generated by Code_Saturne, a general-purpose CFD tool.
*/
/*----------------------------------------------------------------------------*/

#include "cs_defs.h"

/*----------------------------------------------------------------------------
 * Standard C library headers
 *----------------------------------------------------------------------------*/

#include <assert.h>
#include <math.h>

#if defined(HAVE_MPI)
#include <mpi.h>
#endif

/*----------------------------------------------------------------------------
 *  Local headers
 *----------------------------------------------------------------------------*/

#include "cs_headers.h"
"""

_file_header2 = \
"""#include "nc_phase.h"

"""

_file_header3 = \
"""/*----------------------------------------------------------------------------*/

BEGIN_C_DECLS

/*----------------------------------------------------------------------------*/

"""

_file_footer = \
"""}

/*----------------------------------------------------------------------------*/

END_C_DECLS

"""

_function_header = { \
'vol':"""void
cs_meg_volume_function(const cs_zone_t  *zone,
                       cs_field_t       *f[])
{
""",
'bnd':"""cs_real_t *
cs_meg_boundary_function(const cs_zone_t *zone,
                         const char      *field_name,
                         const char      *condition)
{
  cs_real_t *new_vals = NULL;

""",
'src':"""cs_real_t *
cs_meg_source_terms(const cs_zone_t *zone,
                    const char      *name,
                    const char      *source_type)
{
  cs_real_t *new_vals = NULL;

""",
'ini':"""cs_real_t *
cs_meg_initialization(const cs_zone_t *zone,
                      const char      *field_name)
{
  cs_real_t *new_vals = NULL;

"""
}

_function_names = {'vol':'cs_meg_volume_function.c',
                   'bnd':'cs_meg_boundary_function.c',
                   'src':'cs_meg_source_terms.c',
                   'ini':'cs_meg_initialization.c'}

_block_comments = {'vol':'User defined formula for variable(s) %s over zone %s',
                   'bnd':'User defined formula for "%s" over BC=%s',
                   'src':'User defined source term for %s over zone %s',
                   'ini':'User defined initialization for variable %s over zone %s'}

_func_short_to_long = {'vol':'volume zone',
                       'bnd':'boundary',
                       'src':'source term',
                       'ini':'initialization'}

#-------------------------------------------------------------------------------

_cs_math_internal_name = {'abs':'cs_math_fabs',
                          'min':'cs_math_fmin',
                          'max':'cs_math_fmax'}

_pkg_fluid_prop_dict = {}
_pkg_fluid_prop_dict['code_saturne'] = {'rho0':'ro0',
                                        'mu0':'viscl0',
                                        'p0':'p0',
                                        'cp0':'cp0'}

_pkg_fluid_prop_dict['neptune_cfd'] = {'rho0':'ro0',
                                       'mu0':'viscl0',
                                       'cp0':'cp0',
                                       'lambda0':'lambda0'}

_pkg_glob_struct = {'code_saturne':'cs_glob_fluid_properties',
                    'neptune_cfd':'nc_phases->p_ini[PHASE_ID]'}

_ref_turb_values = {'uref':'uref',
                    'almax':'almax'}

#===============================================================================
# Utility functions
#===============================================================================

#---------------------------------------------------------------------------
def create_req_field(name, dim=0):

    r = {'name':name,
         'dim':dim,
         'components':[]}

    return r

def rfield_add_comp(rf, c):

    rf['components'].append(c)
    rf['dim'] += 1

def split_req_components(req_list):
    """
    Look at a list of field names used in the formula.
    Check if its a component (f[X], f[XY], ..) or a field (f).
    return a list with it
    """
    req_fields = []
    for r in req_list:

        rf = r
        if bool(re.search('\[[A-Za-z0-9]\]', r)):
            rf = re.sub('\[[A-Za-z0-9]\]', '', r)
        elif bool(re.search('\[[A-Za-z0-9][A-Za-z0-9]\]', r)):
            rf = re.sub('\[[A-Za-z0-9][A-Za-z0-9]\]', '', r)

        if rf == r:
            req_fields.append(create_req_field(r,1))

        else:
            new_field = True
            for f in req_fields:
                if f['name'] == rf:
                    rfield_add_comp(f,r)
                    new_field = False
                    break

            if new_field:
                req_fields.append(create_req_field(rf))
                rfield_add_comp(req_fields[-1], r)

    return req_fields

def get_req_field_info(req_fields, r):

    for i in range(len(req_fields)):
        if req_fields[i]['dim'] == 1:
            if req_fields[i]['name'] == r:
                return i, -1, req_fields[i]['dim']

        else:
            if r in req_fields[i]['components']:
                return i, req_fields[i]['components'].index(r), req_fields[i]['dim']

    return None, None, None

def dump_req_fields(req_fields):

    print("===========================")
    for f in req_fields:
        print("Name: %s" % (f['name']))
        print("Dim: %d" % (f['dim']))
        print(f['components'])
        print("===========================")


#---------------------------------------------------------------------------

def break_expression(exp):

    expression_lines = []

    for line in exp.split('\n'):
        line_comp = []
        for elt in re.split('=|\+|-|\*|\/|\(|\)|;|,|\^|<|>|\&\&|\|\|',line):
            if elt != '':
                line_comp.append(elt.strip())

        expression_lines.append(line_comp)

    return expression_lines

#---------------------------------------------------------------------------

def find_c_comment_close(l, c_id, quotes):
    """
    Return index in given string of closing C-style comment,
    or -1 if not found after given c_id column.
    Escape characters or strings are handled.
    """

    w = len(l)

    while c_id < w:

        # Quoting (highest priority);
        # left inside expressions, but separators in quoted
        # regions are not considered as separators.

        if l[c_id] == "\\": # use 'e' to represent escape character
            if quotes[0] == "e":
                quotes.pop(0)
            else:
                quotes.insert(0, "e")
        elif l[c_id] == "'":
            if quotes[0] == "'":
                quotes.pop(0)
            else:
                quotes.insert(0, "'")
        elif l[c_id] == '"':
            if quotes[0] == '"':
                quotes.pop(0)
            else:
                quotes.insert(0, "'")

        elif quotes[0] == ' ': # found
            if l[c_id] == '*':
                if c_id+1 < w:
                    if l[c_id+1] == '/':
                        return c_id

        c_id += 1

    return -1

#---------------------------------------------------------------------------

def separate_segments(lines):
    """
    Separate segments based on expected separators.
    This stage is not a parser, but simply splits lines into segments,
    separating comments (allowing for Python, C, and C++ style comments),
    and checking for quoting or escape characters.
    Returns a list of tuples containing the segments, and matching
    start line and column indexes in the original expression.
    """

    whitespace = (' ', '\t', '\n', '\l')
    separators = ('{', '}', ';')
    segments = []

    in_multiline_comment = False
    quotes = [' ']

    # Loop on lines

    l_id = 0
    for l in lines:

        w = len(l)

        # Loop on columns

        s_id = 0
        c_id = 0
        while c_id < w:

            # Quoting (highest priority);
            # left inside segments, but separators in quoted
            # regions are not considered as separators.

            if l[c_id] == "\\": # use 'e' to represent escape character
                if quotes[0] == "e":
                    quotes.pop(0)
                else:
                    quotes.insert(0, "e")
            elif l[c_id] == "'":
                if quotes[0] == "'":
                    quotes.pop(0)
                else:
                    quotes.insert(0, "'")
            elif l[c_id] == '"':
                if quotes[0] == '"':
                    quotes.pop(0)
                else:
                    quotes.insert(0, "'")

            if quotes[0] != ' ':
                if quotes[0] == 'e' and l[c_id] in whitespace:
                    # escape character may be a line continuation character
                    # in this case; handle it like a separator
                    segments.append(('\\', l_id, c_id))
                    c_id += 1
                    s_id = c_id
                continue

            # In multiline C-style comment
            # (transform to '#' for easier testing)

            elif in_multiline_comment:
                j = find_c_comment_close(l, c_id, quotes)
                if j >= 0: # on same line
                    j += 2
                    in_multiline_comment = False
                else:
                    j = w
                segments.append(('# ' + l[c_id:j].strip(), l_id, c_id))
                c_id = j
                s_id = c_id

            # Whitespace (handle here rather than using strip()
            # type functions to keep track of expression start columns

            elif l[c_id] in whitespace:
                if s_id == c_id:
                    s_id += 1
                c_id += 1
                continue

            # Comments (allow C/C++ style, transform all to '#'
            # for easier testing)

            elif l[c_id] == '#':
                e = l[s_id:c_id].strip()
                if len(e):
                    segments.append((e, l_id, s_id))
                segments.append((l[c_id:].strip(), l_id, c_id))
                c_id = w
                s_id = c_id

            elif l[c_id:c_id+2] in ('//', '/*'):
                e = l[s_id:c_id].strip()
                if len(e):
                    segments.append((e, l_id, s_id))
                if l[c_id:c_id+2] == '//':
                    segments.append(('# ' + l[c_id+2:].strip(),
                                     l_id, c_id))
                    c_id = w
                    s_id = c_id
                else:
                    j = find_c_comment_close(l, c_id+2, quotes)
                    if j >= 0: # on same line
                        segments.append(('# ' + l[c_id+2:j].strip(),
                                         l_id, c_id))
                        j += 2
                    else:
                        j = w
                        segments.append(('# ' + l[c_id+2:j].strip(),
                                         l_id, c_id))
                        in_multiline_comment = True
                    c_id = j
                    s_id = c_id

            else:

                if l[c_id] in separators:
                    e = l[s_id:c_id].strip()
                    if len(e):
                        segments.append((e, l_id, s_id))
                    segments.append((l[c_id:c_id+1], l_id, c_id))
                    c_id += 1
                    s_id = c_id
                else:
                    c_id += 1

        # End of loop on line:

        if s_id < c_id:
            e = l[s_id:c_id].strip()
            if len(e):
                segments.append((e, l_id, s_id))

        l_id += 1

    return segments

#---------------------------------------------------------------------------

def parse_parentheses(line):

    istart = []
    d = {}

    for i, c in enumerate(line):
        if c == '(':
            istart.append(i)
        if c == ')':
            try:
                d[istart.pop()] = i
            except IndexError:
                print('There are too many closing parentheses')

    if istart:
        print('A closing parenthese is missing!')

    return d

#---------------------------------------------------------------------------

def get_start_lc(expr):
    """
    Return start line and column for a given expression
    """
    if isinstance(expr, (list,)):
        return get_start_lc(expr[0])

    else:
        return expr[1], expr[2]

#---------------------------------------------------------------------------

def update_expressions_syntax(expressions):
    """
    Update legacy expressions, such as "mod"
    """

    new_exp = []
    skip_to = 0

    for i, e in enumerate(expressions):

        if i < skip_to:
            continue

        if isinstance(e, (list,)):
            a = update_expressions_syntax(e)
            new_exp.append(update_expressions_syntax(e))

        else:

            # Translate "modulus" syntax
            if e[0] == 'mod':
                valid = True
                sub_expr = None
                ic = -1 # start of comma separating x and y,
                iy = -1 # start of second argument
                pc = -1 # position of closing parenthesis
                try:
                    sub_expr = expressions[i+1]
                    po = sub_expr[0][0]
                    if po != '(':
                        valid = False
                    # Check position of comma (if x is complex, might not be at i+3)
                    j = 1
                    while ic < 0:
                        if isinstance(sub_expr[j], (list,)):
                            j += 1
                        elif sub_expr[j][0] != ',':
                            j += 1
                        else:
                            ic = j
                    iy = ic+1
                    j = iy+1
                    while pc < 0:
                        if isinstance(sub_expr[j], (list,)):
                            j += 1
                        elif sub_expr[j][0] != ')':
                            j += 1
                        else:
                            pc = j
                    if pc+1 != len(sub_expr):
                        valid = False
                except Exception:
                   valid = False

                if valid:
                    skip_to = i+2
                    j = 1
                    new_sub = []
                    x = sub_expr[j]
                    li, ci = get_start_lc(x)
                    new_sub.append(('((int)', li, ci))
                    if ic > 2:
                        sub_sub = []
                        sub_sub.append(('(', li, ci))
                        while j < ic:
                            x = sub_expr[j]
                            sub_sub.append(x)
                            j += 1
                        li, ci = get_start_lc(x)
                        sub_sub.append((')', li, ci))
                        new_sub.append(sub_sub)
                    else:
                        new_sub.append(x)
                    new_sub.append(('% (int)', li, ci))
                    j = ic+1
                    x = sub_expr[j]
                    if pc - j > 1:
                        sub_sub = []
                        li, ci = get_start_lc(x)
                        sub_sub.append(('(', li, ci))
                        while j < pc:
                            x = sub_expr[j]
                            sub_sub.append(x)
                            j += 1
                        li, ci = get_start_lc(x)
                        sub_sub.append((')', li, ci))
                        new_sub.append(sub_sub)
                    else:
                        new_sub.append(x)
                    li, ci = get_start_lc(x)
                    new_sub.append((')', li, ci))
                    new_exp.append(new_sub)
                else:
                    new_exp.append(e)

            else:
                new_exp.append(e)

    return new_exp

#---------------------------------------------------------------------------

def recurse_expressions_syntax(expressions):
    """
    Recursively Update expressions
    """

    new_exp = []
    skip_to = 0

    for i, e in enumerate(expressions):

        if i < skip_to:
            continue

        if isinstance(e, (list,)):
            new_exp.append(recurse_expressions_syntax(e))

        else:

            # Translate "power" syntax
            if e[0] in ('^', '**') and i > 0:
                valid = True
                x = new_exp.pop()
                y = None
                try:
                    y = expressions[i+1]
                except Exception:
                    valid = False
                    new_exp.append(x) # replace after pop() above
                if valid:
                    sub_exp = []
                    li, ci = get_start_lc(x)
                    sub_exp.append(('(', li, ci))
                    sub_exp.append(x)
                    if y[0] in ('2', '3', '4'):
                        new_exp.append(('cs_math_pow'+y[0], li, ci))
                    else:
                        new_exp.append(('pow', li, ci))
                        sub_exp.append((',', li, ci))
                        li, ci = get_start_lc(x)
                        sub_exp.append(y)
                    sub_exp.append((')', li, ci))
                    new_exp.append(sub_exp)
                    skip_to = i+2

            else:
                new_exp.append(e)

    return new_exp

#---------------------------------------------------------------------------

def rebuild_text(expressions, comments,
                 level=0, s_line=0, s_col=0, t_prev=''):
    """
    Rebuild source code from expressions and comments.
    Reinsert comments at recorderd lines in expressions.
    Comments are never inserted before an active token on a given
    line, event if this was the case in the original expression,
    both for simplification and because it is not recommeneded.
    """

    text = ''

    new_exp = []

    # operators adding spacing or not (minimalist prettyfier).
    spacing_operators = ('+', '-', '*', '%', '/', '=', '>', '<',
                         '==', '>=', '<=', 'if', 'then', 'else')
    no_spacing_operators = ('^', '**')

    for i, e in enumerate(expressions):

        li, ci = get_start_lc(e)

        # Restore comments from previous lines

        # column info does not need to be fully updated here,
        # as comments are always added at the end of a line,
        # and new lines reset column info.

        if comments:
            line_ref = comments[0][1]
            while comments:
                if comments[0][1] >= li:
                    break
                c = comments.pop(0)
                while s_line < c[1]:
                    text += '\n'
                    s_line += 1
                    s_col = 0
                if s_col > 0: # add space to nonempty column
                    text += ' '
                if s_col < c[2]:
                    for j in range(c[2]):
                        text += ' '
                text += '//' + c[0][1:]

        # Recursive handling of code

        if isinstance(e, (list,)):
            sub_text, comments, e_line, e_col \
                = rebuild_text(e, comments, level+1,
                               s_line, s_col, t_prev)
            text += sub_text
            t_prev = sub_text[-1:]
            if e_line > s_line:
                s_col = 0
            else:
                s_col = e_col
            s_line = e_line

        else:

            if s_line < li:
                text += '\n'
                s_col = 0
                for i in range(ci):
                    text += ' '
                    s_col += 1

            else:      # Try to put spaces in recommended places
                add_space = True
                if t_prev in ('(', '[', '{', '', '% (int)'):
                    add_space = False
                elif e[0] in (';', ':', ',', ')', ']', '}') \
                   or e[0] in no_spacing_operators:
                    add_space = False
                elif e[0] in ('(', '['):
                    if t_prev not in spacing_operators:
                        add_space = False
                if add_space:
                    text += ' '
                    s_col += 1

            # Add actual token

            text += e[0]
            t_prev = e[0]
            s_line = li
            s_col += len(e[0])

    # Restore comments after code

    """
    if len(comments) > 0:
        if comments[0][1]:
        iwhile comments[0][1] < e[1]:
            c = comments.pop(0)
            line_cur = c[1]
            while line_cur < line_ref:
                text += '\n'
                line_cur += 1
                for j in range(c[2]):
                    text += ' '
                    text += c[0]
                line_ref = line_cur
            text += '\n\n'
    """


    return text, comments, s_line, s_col

#---------------------------------------------------------------------------

def tokenize(segments):
    """
    Tokenize segments and separate comments.
    """

    whitespace = (' ', '\t', '\n', '\l', '\r')
    sep2 = ('<=', '>=', '!=', '||', '&&', '+=', '-=', '*=', '/=', '**')
    sep1 = ('=', '(', ')', ';', ',', ':', '[', ']', '{', '}',
            '+', '-', '*', '/', '<', '>',  '^', '%', '!', '?')
    digits_p = ('.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9')

    tokens = []
    comments = []

    for s in segments:

        prv = ' '
        s_id = 0
        s0 = s[0]
        if (s0[0] == '#'):
            comments.append(s)
            continue

        for i, c in enumerate(s0):
            if s_id > i:
                continue
            elif c in whitespace:
                if (not prv in whitespace) and (s_id < i):
                    tokens.append((s0[s_id:i], s[1], s[2]+s_id))
                s_id = i+1
            elif s0[i:i+2] in sep2:
                if (not prv in whitespace) and (s_id < i):
                    tokens.append((s0[s_id:i], s[1], s[2]+s_id))
                tokens.append((s0[i:i+2], s[1], s[2]+i))
                s_id = i+2
            elif c in sep1:
                # special case: e+ or e- might not be a separator
                is_exp = False
                if c in ('+', '-'):
                    if s0[i-1:i+1] in ('e+', 'e-', 'E+', 'E-'):
                        if s0[i-2:i-1] in digits_p and s0[i+1:i+2] in digits_p:
                            is_exp = True
                if not is_exp:
                    if (not prv in whitespace) and (s_id < i):
                        tokens.append((s0[s_id:i], s[1], s[2]+s_id))
                    tokens.append((s0[i:i+1], s[1], s[2]+i))
                    s_id = i+1
            prv = s0[i]
        r = s0[s_id:]
        if len(r) > 0:
            tokens.append((r, s[1], s[2]+s_id))

    return tokens, comments

#---------------------------------------------------------------------------

def build_expressions(exp_lines, tokens):
    """
    Organize expressions as lists of subexpressions based on levels
    """

    # Now we have a fully tokenized expression, we can buid a list of assignments

    opening_tokens = ('(', '{', '[')
    closing_tokens = (')', '}', ']')
    open_match = {')': '(',
                  '}': '{',
                  ']': '['}
    close_match = {'(': ')',
                  '{': '}',
                  '[': ']'}

    parent = []
    current = []

    rvalues = []
    expression = []

    previous = None
    level_open = []

    # Build levels based on parenthesis and braces (check consistency)

    match_error = None

    for t in tokens:

        if t[0] in opening_tokens:
            level_open.append(t)
            parent.append(current)
            current = []
            current.append(t)
        elif t[0] in closing_tokens:
            match_open = False
            if level_open:
                t_open = level_open.pop()
                if t_open[0] == open_match[t[0]]:
                    match_open = True
                    current.append(t)
                    sub = current
                    current = parent.pop()
                    current.append(sub)
            if not match_open:
                match_error = (t[0], t[1], t[2], open_match[t[0]])
                break
        else:
            current.append(t)

    if level_open:
        t = level_open.pop()
        match_error = (t[0], t[1], t[2], close_match[t[0]])

    if match_error:
        err_msg = []
        n_lines = 7
        j = match_error[1]  # error line
        for i in range(n_lines):
            if i + j + 1 - n_lines > -1:
                err_msg.append(exp_lines[i + j + 1 - n_lines])
        c_line = ''
        for k in range(match_error[2]):  # error column
            c_line += ' '
        c_line += '^'
        err_msg.append(c_line)
        fmt = "Error: '{0}' at line {1} and column {2} does not have a matching '{3}'"
        err_msg.append(fmt.format(match_error[0], match_error[1],
                                  match_error[2], match_error[3]))
        err_msg.append('')
        for i in range(n_lines):
            if j+i < len(exp_lines):
                err_msg.append(exp_lines[j-i])

        """
        msg = ''
        for l in err_msg:
            msg += l + '\n'

        raise Exception(msg)
        """
        for l in err_msg:
            print(l)

        return None

    return current


#---------------------------------------------------------------------------

def split_for_assignment(l):
    """
    Check for assignemnt (separating along = but not >=, <=)
    """
    lf = []
    c_idx = 0
    s_idx = 0
    e_idx = len(l)
    for c_idx in range(e_idx):
        if l[c_idx] == '=':
            if c_idx > 0 and l[c_idx-1] not in ('>', '<'):
                lf.append(l[s_idx:c_idx])
                s_idx = c_idx+1
    if s_idx <= e_idx:
        lf.append(l[s_idx:e_idx])

    return lf

#---------------------------------------------------------------------------

def parse_gui_expression(expression,
                         req,
                         known_symbols,
                         func_type,
                         need_for_loop = False):

    usr_code = ''
    usr_defs = ''
    if_loop = False

    tab = '  '
    ntabs = 3

    if_open = 0

    if func_type == 'vol':
        req_fields = split_req_components(req)
#        dump_req_fields(req_fields)

    # ------------------------------------
    # Parse the Mathematical expression and generate the C block code

    exp_lines = expression.split("\n")

    segments = separate_segments(exp_lines)
    tokens, comments = tokenize(segments)
    expr_list = build_expressions(exp_lines, tokens)

    # TODO: better handle parsing error (message printed in build_expressions)

    if expr_list:
        # Update expressions to new syntax (should be moved to back compatiility)
        expr_list = update_expressions_syntax(expr_list)

        # Translate expressions such as power functions
        expr_list = recurse_expressions_syntax(expr_list)

        # Rebuild source text
        text, comments, s_line, s_col \
            = rebuild_text(expr_list, comments)

        # Return to previous stage
        # (we should use the expressions list for more robust parsing
        # also in the following stages in the future).

        exp_lines = text.split("\n")
        segments = separate_segments(exp_lines)

    s_idx = 0
    s_end = len(segments)
    nreq = len(req)

    for lidx in range(len(exp_lines)):

        # Rebuild line from segments, ignoring comments
        # segments should be used to improve parsing in the future
        l = ""
        while s_idx < s_end:
            s_l_idx = segments[s_idx][1]
            if s_l_idx > lidx:
                break
            elif s_l_idx == lidx:
                s = segments[s_idx][0]
                if s[0] != '#':
                    l += s + ' '
            s_idx += 1
        l = l.rstrip()

        # ------------------------------------
        # Check that the line is not empty
        if len(l) > 0:

            # Check for assignemnt (separating along = but not >=, <=)
            lf = split_for_assignment(l)

            # ------------------------------------
            # If the line contains an assignment
            if len(lf) > 1:
                if lf[0].strip() not in known_symbols:
                    known_symbols.append(lf[0].strip())
                    # Test whether we are inside an if loop or not!
                    if if_open:
                        usr_defs += 2*tab + 'cs_real_t %s = -1.;\n' % (lf[0])
                    else:
                        l = 'cs_real_t '+l

                elif lf[0].strip() in req:
                    if func_type == 'vol':
                        fid, fcomp, fdim = get_req_field_info(req_fields, lf[0].strip())
                        if fid == None:
                            raise Exception("Uknown field: %s" %(lf[0].strip()))

                        if fcomp < 0:
                            new_v = 'f[%d]->val[c_id]' % (fid)
                        else:
                            new_v = 'f[%d]->val[c_id*%d + %d]' % (fid, fdim, fcomp)

                    elif func_type == 'bnd':
                        ir = req.index(lf[0].strip())
                        if need_for_loop:
                            new_v = 'new_vals[%d * zone->n_elts + e_id]' % (ir)
                        else:
                            new_v = 'new_vals[%d]' % (ir)

                    elif func_type == 'src':
                        if nreq > 1:
                            ir = req.index(lf[0].strip())
                            new_v = 'new_vals[%d * e_id + %d]' % (nreq, ir)
                        else:
                            new_v = 'new_vals[e_id]'

                    elif func_type == 'ini':
                        if nreq > 1:
                            ir = req.index(lf[0].strip())
                            new_v = 'new_vals[%d * e_id + %d]' % (nreq, ir)
                        else:
                            new_v = 'new_vals[e_id]'

                    l = new_v + ' = ' + lf[1]

            # ------------------------------------
            # If loop :
            # 1 : Line starting with if
            if l[:2] == 'if':
                if l[-1] != '{' and exp_lines[lidx+1][0] != '{':
                    l = l + ' {'
                usr_code += '\n'
                usr_code += (ntabs+if_open)*tab + l
                if_open += 1

            # 2 : Line with an else
            elif 'else' in l:
                if l[0] != '}' and if_open and exp_lines[lidx-1][-1] != '}':
                    l = '} '+l

                if l[-1] != '{' and exp_lines[lidx+1][0] != '{':
                    l = l + ' {'

                if_open -= 1
                usr_code += '\n'
                usr_code += (ntabs+if_open)*tab + l
                if_open += 1

            # 3 : Other lines
            else:
                if l[0] == '}':
                    if_open -= 1

                usr_code += '\n'
                if l[-1] == '}' and len(l) > 1:
                        usr_code += (ntabs+if_open)*tab + l[:-1]
                        usr_code += '\n' + (ntabs+if_open)*tab + '}'
                else:
                    usr_code += (ntabs+if_open)*tab + l

        else:
            usr_code += '\n'

    if if_open:
        for i in range(if_open):
            if_open -= 1
            usr_code += '\n' + (ntabs+if_open)*tab + '}\n'
    else:
        usr_code += '\n'

    return usr_code, usr_defs

#---------------------------------------------------------------------------


#===============================================================================
# Main class
#===============================================================================

class mei_to_c_interpreter:

    #---------------------------------------------------------------------------

    def __init__(self, case, create_functions=True, module_name=None):

        self.case = case
        if module_name:
            self.module_name = module_name
        else:
            self.module_name = case.module_name()

        self.data_path = os.path.join(case['case_path'], 'DATA')
        self.tmp_path = os.path.join(self.data_path, 'tmp')

        # function name to file name dictionary
        self.funcs = {'vol':{},
                      'bnd':{},
                      'src':{},
                      'ini':{}}

        self.code_to_write = ""

        nb = NotebookModel(self.case)
        self.notebook = {}
        for (nme, val) in nb.getNotebookList():
            self.notebook[nme] = str(val)

        if create_functions and getRunType(self.case) == 'standard':
            # Volume code
            self.generate_volume_code()

            # Boundary code
            self.generate_boundary_code()

            # Source terms code
            self.generate_source_terms_code()

            # Initialization function
            self.generate_initialize_code()

    #---------------------------------------------------------------------------

    def update_block_expression(self, func_type, key, new_exp):

        self.funcs[func_type][key]['exp']   = new_exp
        self.funcs[func_type][key]['lines'] = break_expression(new_exp)

    #---------------------------------------------------------------------------

    def init_block(self,
                   ftype,
                   zone_name,
                   name,
                   expression,
                   required,
                   symbols,
                   known_fields,
                   condition = None,
                   source_type = None):

        # Creating a unique function name based on the zone and variable name
        fkey = '::'.join([zone_name, name])
        if fkey in self.funcs[ftype].keys():
            msg = 'Formula for "%s" in %s %s was already defined:\n %s' \
                    % (name, _func_short_to_long[ftype], zone_name,
                       self.funcs[ftype][fkey]['exp'])
            raise Exception(msg)

        self.funcs[ftype][fkey] = {'exp':expression,
                                   'req':required,
                                   'sym':symbols,
                                   'knf':known_fields,
                                   'cnd':condition,
                                   'tpe':source_type}

        self.funcs[ftype][fkey]['lines'] = break_expression(expression)

    #---------------------------------------------------------------------------

    def write_cell_block(self, func_key):

        # Check if function exists
        if func_key not in self.funcs['vol'].keys():
            return

        func_params = self.funcs['vol'][func_key]

        expression   = func_params['exp']
        symbols      = func_params['sym']
        known_fields = func_params['knf']

        if type(func_params['req'][0]) == tuple:
            required = [r[0] for r in func_params['req']]
        else:
            required = func_params['req']

        zone, name = func_key.split('::')
        exp_lines_comp = func_params['lines']

        # Get user definitions and code
        usr_defs = ''
        usr_code = ''
        usr_blck = ''

        tab   = "  "
        ntabs = 2

        known_symbols = []
        coords = ['x', 'y', 'z']
        need_coords = False

        # Add to definitions useful arrays or scalars
        for line_comp in exp_lines_comp:
            for s in symbols:
                if type(s) == tuple:
                    sn = s[0]
                else:
                    sn = s
                if sn in line_comp and sn not in known_symbols:
                    if sn == 'dt':
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t dt = cs_glob_time_step->dt[0];\n'
                        known_symbols.append(sn)
                    elif sn == 't':
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t t = cs_glob_time_step->t_cur;\n'
                        known_symbols.append(sn)
                    elif sn == 'iter':
                        usr_defs += ntabs*tab
                        usr_defs += 'const int iter = cs_glob_time_step->nt_cur;\n'
                        known_symbols.append(sn)
                    elif sn in coords:
                        ic = coords.index(sn)
                        lxyz = 'const cs_real_t %s = xyz[c_id][%s];\n' % (sn, str(ic))
                        usr_code += (ntabs+1)*tab + lxyz
                        known_symbols.append(sn)
                        need_coords = True
                    elif sn == "volume":
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t volume = zone->measure;\n'
                        known_symbols.append(sn)
                    elif sn in self.notebook.keys():
                        l = 'const cs_real_t %s = cs_notebook_parameter_value_by_name("%s");\n' \
                                % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

                    elif sn in _ref_turb_values:
                        l = 'const cs_real_t %s = cs_glob_turb_ref_values->%s;\n' % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

                    elif sn in _pkg_fluid_prop_dict[self.module_name].keys():
                        if len(name.split("_")) > 1:
                            try:
                                phase_id = int(name.split('_')[-1])-1
                            except:
                                phase_id = -1
                        else:
                            phase_id = -1
                        gs = _pkg_glob_struct[self.module_name].replace('PHASE_ID',
                                                                     str(phase_id))
                        pn = _pkg_fluid_prop_dict[self.module_name][sn]
                        ms = 'const cs_real_t %s = %s->%s;\n' %(sn, gs, pn)
                        usr_defs += ntabs*tab + ms
                        known_symbols.append(sn)

                    elif s not in known_fields:
                        if len(s[1].split('=')) > 1:
                            sval = s[1].split('=')[-1]
                            usr_defs += ntabs*tab + 'const cs_real_t '+sn+' = '+str(sval)+';\n'
                            known_symbols.append(sn)

        if need_coords:
            usr_defs = ntabs*tab \
                     + 'const cs_real_3_t *xyz = (cs_real_3_t *)cs_glob_mesh_quantities->cell_cen;' \
                     + '\n\n' \
                     + usr_defs

        # List of fields where we need to use the label instead of the name:
        label_not_name = ['Additional scalar', 'Thermal scalar', 'Pressure']
        for f in known_fields:
            (fl, fn) = f
            for line in exp_lines_comp:
                if fl in line:
                    for lnn in label_not_name:
                        if lnn in fn:
                            fn = fl
                    l = 'const cs_real_t *%s_vals = cs_field_by_name("%s")->val;\n' \
                            % (fl, fn)
                    usr_defs += ntabs*tab + l
                    known_symbols.append(fl)
                    usr_code += (ntabs+1)*tab + 'const cs_real_t %s = %s_vals[c_id];\n'\
                            % (fl, fl)

                    break

        # Internal names of mathematical functions
        for key in _cs_math_internal_name.keys():
            if key in expression:
                expression = expression.replace(key+'(',
                                                _cs_math_internal_name[key]+'(')

        for line in exp_lines_comp:
            if 'pi' in line and 'pi' not in known_symbols:
                usr_defs += ntabs*tab + 'const cs_real_t pi = cs_math_pi;\n'
                known_symbols.append('pi')

        for s in required:
            known_symbols.append(s);

        known_symbols.append('#')

        ntabs += 1
        if_loop = False

        # Parse the user expresion
        parsed_exp = parse_gui_expression(expression,
                                          required,
                                          known_symbols,
                                          'vol')
        usr_code += parsed_exp[0]
        if parsed_exp[1] != '':
            usr_defs += parsed_exp[1]

        # Write the block
        nsplit = name.split('+')
        usr_blck = tab + 'if (strcmp(f[0]->name, "%s") == 0 &&\n' % (nsplit[0])
        for i in range(1,len(nsplit)):
            usr_blck += tab + '    strcmp(f[%d]->name, "%s") == 0 &&\n' % (i, nsplit[i])

        usr_blck += tab + '    strcmp(zone->name, "%s") == 0) {\n' % (zone)

        usr_blck += usr_defs + '\n'

        usr_blck += 2*tab + 'for (cs_lnum_t e_id = 0; e_id < zone->n_elts; e_id++) {\n'
        usr_blck += 3*tab + 'cs_lnum_t c_id = zone->elt_ids[e_id];\n'

        usr_blck += usr_code
        usr_blck += 2*tab + '}\n'
        usr_blck += tab + '}\n'

        return usr_blck

    #---------------------------------------------------------------------------

    def write_bnd_block(self, func_key):

        if func_key not in self.funcs['bnd'].keys():
            return

        func_params = self.funcs['bnd'][func_key]

        expression   = func_params['exp']
        symbols      = func_params['sym']
        known_fields = func_params['knf']
        cname        = func_params['cnd']

        if type(func_params['req'][0]) == tuple:
            required = [r[0] for r in func_params['req']]
        else:
            required = func_params['req']

        exp_lines_comp = func_params['lines']

        zone, field_name = func_key.split('::')

        # Check if for loop is needed
        need_for_loop = True
        if cname == 'flow1_formula' or cname == 'flow2_formula':
            need_for_loop = False

        # Get user definitions and code
        usr_defs = ''
        usr_code = ''
        usr_blck = ''

        tab   = "  "
        ntabs = 2

        known_symbols = []
        for req in required:
            known_symbols.append(req)
        coords = ['x', 'y', 'z']
        need_coords = False

        # allocate the new array
        if need_for_loop:
            usr_defs += ntabs*tab + 'const int vals_size = zone->n_elts * %d;\n' % (len(required))
        else:
            usr_defs += ntabs*tab + 'const int vals_size = %d;\n' % (len(required))

        usr_defs += ntabs*tab + 'BFT_MALLOC(new_vals, vals_size, cs_real_t);\n'
        usr_defs += '\n'

        # Add to definitions useful arrays or scalars
        for line_comp in exp_lines_comp:
            for s in symbols:
                if type(s) == tuple:
                    sn = s[0]
                else:
                    sn = s
                if sn in line_comp and sn not in known_symbols:
                    if sn == 'dt':
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t dt = cs_glob_time_step->dt[0];\n'
                        known_symbols.append(sn)
                    elif sn == 't':
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t t = cs_glob_time_step->t_cur;\n'
                        known_symbols.append(sn)
                    elif sn == 'iter':
                        usr_defs += ntabs*tab
                        usr_defs += 'const int iter = cs_glob_time_step->nt_cur;\n'
                        known_symbols.append(sn)
                    elif sn in coords:
                        ic = coords.index(sn)
                        lxyz = 'const cs_real_t %s = xyz[f_id][%s];\n' % (sn, str(ic))
                        usr_code += (ntabs+1)*tab + lxyz
                        known_symbols.append(sn)
                        need_coords = True
                    elif sn == "surface":
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t surface = zone->measure;\n'
                        known_symbols.append(sn)
                    elif sn in _ref_turb_values:
                        l = 'const cs_real_t %s = cs_glob_turb_ref_values->%s;\n' % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

            for sn in self.notebook.keys():
                if sn in line_comp and sn not in known_symbols:
                        l = 'const cs_real_t %s = cs_notebook_parameter_value_by_name("%s");\n' \
                                % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

        if need_coords:
            usr_defs = ntabs*tab \
                     + 'const cs_real_3_t *xyz = (cs_real_3_t *)cs_glob_mesh_quantities->b_face_cog;' \
                     + '\n\n' \
                     + usr_defs

        # Fields
        for f in known_fields:
            for line in exp_lines_comp:
                if f[0] in line:
                    l = 'const cs_real_t *%s_vals = cs_field_by_name("%s")->val;\n' \
                            % (f[0], f[1])
                    usr_defs += ntabs*tab + l
                    known_symbols.append(f[0])
                    usr_code += (ntabs+1)*tab + 'const cs_real_t %s = %s_vals[c_id];\n'\
                            % (f[0], f[0])
                    break

        # Internal names of mathematical functions
        for key in _cs_math_internal_name.keys():
            if key in expression:
                expression = expression.replace(key+'(',
                                                _cs_math_internal_name[key]+'(')

        for line in exp_lines_comp:
            if 'pi' in line and 'pi' not in known_symbols:
                usr_defs += ntabs*tab + 'const cs_real_t pi = cs_math_pi;\n'
                known_symbols.append('pi')

        if need_for_loop:
            ntabs += 1

        # Parse the user expresion
        parsed_exp = parse_gui_expression(expression,
                                          required,
                                          known_symbols,
                                          'bnd',
                                          need_for_loop)

        usr_code += parsed_exp[0]
        if parsed_exp[1] != '':
            usr_defs += parsed_exp[1]

        # Write the block
        block_cond  = tab + 'if (strcmp(field_name, "%s") == 0 &&\n' % (field_name)
        block_cond += tab + '    strcmp(condition, "%s") == 0 &&\n' % (cname)
        block_cond += tab + '    strcmp(zone->name, "%s") == 0) {\n' % (zone)
        usr_blck = block_cond + '\n'

        usr_blck += usr_defs + '\n'

        if need_for_loop:
            usr_blck += 2*tab + 'for (cs_lnum_t e_id = 0; e_id < zone->n_elts; e_id++) {\n'
            usr_blck += 3*tab + 'cs_lnum_t f_id = zone->elt_ids[e_id];\n'

        usr_blck += usr_code

        if need_for_loop:
            usr_blck += 2*tab + '}\n'

        usr_blck += tab + '}\n'

        return usr_blck

    #---------------------------------------------------------------------------

    def write_src_block(self, func_key):

        # Check if function exists:
        if func_key not in self.funcs['src'].keys():
            return

        func_params = self.funcs['src'][func_key]

        expression   = func_params['exp']
        symbols      = func_params['sym']
        known_fields = dict( zip([k[0] for k in func_params['knf']],
                                 [k[1] for k in func_params['knf']]))
        if type(func_params['req'][0]) == tuple:
            required = [r[0] for r in func_params['req']]
        else:
            required = func_params['req']

        source_type  = func_params['tpe']

        zone, name = func_key.split('::')
        exp_lines_comp = func_params['lines']

        # Get user definitions and code
        usr_defs = ''
        usr_code = ''
        usr_blck = ''

        tab   = '  '
        ntabs = 2

        known_symbols = []
        coords = ['x', 'y', 'z']
        need_coords = False

        usr_defs += ntabs*tab + 'const int vals_size = zone->n_elts * %d;\n' % (len(required))
        usr_defs += ntabs*tab + 'BFT_MALLOC(new_vals, vals_size, cs_real_t);\n'
        usr_defs += '\n'

        # Add to definitions useful arrays or scalars
        for line_comp in exp_lines_comp:
            for s in symbols:
                if type(s) == tuple:
                    sn = s[0]
                else:
                    sn = s
                if sn in line_comp and sn not in known_symbols:
                    if sn == 'dt':
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t dt = cs_glob_time_step->dt[0];\n'
                        known_symbols.append(sn)
                    elif sn == 't':
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t t = cs_glob_time_step->t_cur;\n'
                        known_symbols.append(sn)
                    elif sn == 'iter':
                        usr_defs += ntabs*tab
                        usr_defs += 'const int iter = cs_glob_time_step->nt_cur;\n'
                        known_symbols.append(sn)
                    elif sn in coords:
                        ic = coords.index(sn)
                        lxyz = 'const cs_real_t %s = xyz[c_id][%s];\n' % (sn, str(ic))
                        usr_code += (ntabs+1)*tab + lxyz
                        known_symbols.append(sn)
                        need_coords = True
                    elif sn == "volume":
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t volume = zone->measure;\n'
                        known_symbols.append(sn)
                    elif sn in self.notebook.keys():
                        l = 'const cs_real_t %s = cs_notebook_parameter_value_by_name("%s");\n' \
                                % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)
                    elif sn in _ref_turb_values:
                        l = 'const cs_real_t %s = cs_glob_turb_ref_values->%s;\n' % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

                    elif sn in known_fields.keys():
                        l = 'const cs_real_t *%s_vals = cs_field_by_name("%s")->val;\n'
                        l = l % (sn, known_fields[sn])
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)
                        usr_code += (ntabs+1)*tab + 'const cs_real_t %s = %s_vals[c_id];\n'\
                                % (sn, sn)

        if need_coords:
            usr_defs = ntabs*tab \
                     + 'const cs_real_3_t *xyz = (cs_real_3_t *)cs_glob_mesh_quantities->cell_cen;' \
                     + '\n\n' \
                     + usr_defs

        # Internal names of mathematical functions
        for key in _cs_math_internal_name.keys():
            if key in expression:
                expression = expression.replace(key+'(',
                                                _cs_math_internal_name[key]+'(')

        for line in exp_lines_comp:
            if 'pi' in line and 'pi' not in known_symbols:
                usr_defs += ntabs*tab + 'const cs_real_t pi = cs_math_pi;\n'
                known_symbols.append('pi')

        for r in required:
            known_symbols.append(r)

        # Parse the user expresion
        parsed_exp = parse_gui_expression(expression,
                                          required,
                                          known_symbols,
                                          'src')

        usr_code += parsed_exp[0]
        if parsed_exp[1] != '':
            usr_defs += parsed_exp[1]

        # Write the block
        block_cond  = tab + 'if (strcmp(zone->name, "%s") == 0 &&\n' % (zone)
        block_cond += tab + '    strcmp(name, "%s") == 0 && \n' % (name)
        block_cond += tab + '    strcmp(source_type, "%s") == 0 ) {\n' % (source_type)
        usr_blck = block_cond + '\n'

        usr_blck += usr_defs + '\n'

        usr_blck += 2*tab + 'for (cs_lnum_t e_id = 0; e_id < zone->n_elts; e_id++) {\n'
        usr_blck += 3*tab + 'cs_lnum_t c_id = zone->elt_ids[e_id];\n'

        usr_blck += usr_code

        usr_blck += 2*tab + '}\n'
        usr_blck += tab + '}\n'

        return usr_blck

    #---------------------------------------------------------------------------

    def write_ini_block(self, func_key):

        # Check if function exists:
        if func_key not in self.funcs['ini'].keys():
            return

        func_params = self.funcs['ini'][func_key]

        expression   = func_params['exp']
        symbols      = func_params['sym']
        known_fields = func_params['knf']
        if type(func_params['req'][0]) == tuple:
            required = [r[0] for r in func_params['req']]
        else:
            required = func_params['req']

        zone, name = func_key.split('::')
        exp_lines_comp = func_params['lines']

        # Get user definitions and code
        usr_defs = ''
        usr_code = ''
        usr_blck = ''

        tab   = '  '
        ntabs = 2

        known_symbols = []
        coords = ['x', 'y', 'z']
        need_coords = False

        usr_defs += ntabs*tab + 'const int vals_size = zone->n_elts * %d;\n' % (len(required))
        usr_defs += ntabs*tab + 'BFT_MALLOC(new_vals, vals_size, cs_real_t);\n'
        usr_defs += '\n'

        # Add to definitions useful arrays or scalars
        for line_comp in exp_lines_comp:
            for s in symbols:
                if type(s) == tuple:
                    sn = s[0]
                else:
                    sn = s
                if sn in line_comp and sn not in known_symbols:
                    if sn in coords:
                        ic = coords.index(sn)
                        lxyz = 'const cs_real_t %s = xyz[c_id][%s];\n' % (sn, str(ic))
                        usr_code += (ntabs+1)*tab + lxyz
                        known_symbols.append(sn)
                        need_coords = True

                    elif sn == "volume":
                        usr_defs += ntabs*tab
                        usr_defs += 'const cs_real_t volume = zone->measure;\n'
                        known_symbols.append(sn)

                    elif sn in self.notebook.keys():
                        l = 'const cs_real_t %s = cs_notebook_parameter_value_by_name("%s");\n' \
                                % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

                    elif sn in _ref_turb_values:
                        l = 'const cs_real_t %s = cs_glob_turb_ref_values->%s;\n' % (sn, sn)
                        usr_defs += ntabs*tab + l
                        known_symbols.append(sn)

                    elif sn in _pkg_fluid_prop_dict[self.module_name].keys():
                        if len(name.split("_")) > 1:
                            try:
                                phase_id = int(name.split('_')[-1])-1
                            except:
                                phase_id = -1
                        else:
                            phase_id = -1

                        gs = _pkg_glob_struct[self.module_name].replace('PHASE_ID',
                                                                     str(phase_id))
                        pn = _pkg_fluid_prop_dict[self.module_name][sn]
                        ms = 'const cs_real_t %s = %s->%s;\n' %(sn, gs, pn)
                        usr_defs += ntabs*tab + ms
                        known_symbols.append(sn)


        if need_coords:
            usr_defs = ntabs*tab \
                     + 'const cs_real_3_t *xyz = (cs_real_3_t *)cs_glob_mesh_quantities->cell_cen;' \
                     + '\n\n' \
                     + usr_defs

        # known fields
        for f in known_fields:
            for line in exp_lines_comp:
                if f[0] in line:
                    l = 'const cs_real_t *%s_vals = cs_field_by_name("%s")->val;\n' \
                            % (f[0], f[1])
                    usr_defs += ntabs*tab + l
                    known_symbols.append(f[0])
                    usr_code += (ntabs+1)*tab + 'const cs_real_t %s = %s_vals[c_id];\n'\
                            % (f[0], f[0])

                    break

        # Internal names of mathematical functions
        for key in _cs_math_internal_name.keys():
            if key in expression:
                expression = expression.replace(key+'(',
                                                _cs_math_internal_name[key]+'(')

        for line in exp_lines_comp:
            if 'pi' in line and 'pi' not in known_symbols:
                usr_defs += ntabs*tab + 'const cs_real_t pi = cs_math_pi;\n'
                known_symbols.append('pi')

        for r in required:
            known_symbols.append(r)

        # Parse the user expresion
        parsed_exp = parse_gui_expression(expression,
                                          required,
                                          known_symbols,
                                          'ini')

        usr_code += parsed_exp[0]
        if parsed_exp[1] != '':
            usr_defs += parsed_exp[1]

        # Write the block
        block_cond  = tab + 'if (strcmp(zone->name, "%s") == 0 &&\n' % (zone)
        block_cond += tab + '    strcmp(field_name, "%s") == 0 ) {\n' % (name)
        usr_blck = block_cond + '\n'

        usr_blck += usr_defs + '\n'

        usr_blck += 2*tab + 'for (cs_lnum_t e_id = 0; e_id < zone->n_elts; e_id++) {\n'
        usr_blck += 3*tab + 'cs_lnum_t c_id = zone->elt_ids[e_id];\n'

        usr_blck += usr_code

        usr_blck += 2*tab + '}\n'
        usr_blck += tab + '}\n'

        return usr_blck

    #---------------------------------------------------------------------------

    def write_block(self, func_type, key):

        if func_type == 'vol':
            return self.write_cell_block(key)
        elif func_type == 'bnd':
            return self.write_bnd_block(key)
        elif func_type == 'src':
            return self.write_src_block(key)
        elif func_type == 'ini':
            return self.write_ini_block(key)
        else:
            return None

    #---------------------------------------------------------------------------

    def generate_volume_code(self):
        # Ground water model enabled ?
        gwm = False

        from code_saturne.model.LocalizationModel import LocalizationModel
        from code_saturne.model.GroundwaterLawModel import GroundwaterLawModel
        if self.module_name == 'code_saturne':
            from code_saturne.model.FluidCharacteristicsModel \
                import FluidCharacteristicsModel

            fcm = FluidCharacteristicsModel(self.case)
            for (fk,sym) in fcm.lst:
                if fcm.getPropertyMode(fk) == 'user_law':
                    exp, req, sca, sym = fcm.getFormulaComponents(fk)
                    self.init_block('vol', 'all_cells', fk,
                                    exp, req, sym, sca)

            slist = fcm.m_sca.getUserScalarNameList()
            for s in fcm.m_sca.getScalarsVarianceList():
                if s in slist: slist.remove(s)
            if slist != []:
                for s in slist:
                    diff_choice = fcm.m_sca.getScalarDiffusivityChoice(s)
                    if diff_choice == 'variable':
                        dname = fcm.m_sca.getScalarDiffusivityName(s)
                        exp, req, sca, sym, = \
                        fcm.getFormulaComponents('scalar_diffusivity', s)
                        self.init_block('vol', 'all_cells', dname,
                                        exp, req, sym, sca)

            # GroundWater Flows Law
            vlm = LocalizationModel('VolumicZone', self.case)
            glm = None

            for zone in vlm.getZones():
                z_id = str(zone.getCodeNumber())
                zone_name = zone.getLabel()
                nature_list = zone.getNatureList()

                if "groundwater_law" in nature_list:
                    if not glm:
                        glm = GroundwaterLawModel(self.case)
                    if zone.getNature()['groundwater_law'] == 'on':
                        if glm.getGroundwaterLawModel(z_id) == 'user':
                            exp, req, sym = glm.getGroundwaterLawFormulaComponents(z_id)
                            self.init_block('vol', zone_name,
                                            'capacity+saturation+permeability',
                                            exp, req, sym, [])

            from code_saturne.model.GroundwaterModel import GroundwaterModel
            # Ground water model enabled ?
            gwm = not (GroundwaterModel(self.case).getGroundwaterModel() == 'off')

        elif self.module_name == 'neptune_cfd':
            from code_saturne.model.ThermodynamicsModel import ThermodynamicsModel
            from code_saturne.model.MainFieldsModel import MainFieldsModel

            tm = ThermodynamicsModel(self.case)
            mfm = MainFieldsModel(self.case)

            authorized_fields = ['density', 'molecular_viscosity',
                                 'specific_heat', 'thermal_conductivity']

            compressible_fields = ['d_rho_d_P', 'd_rho_d_h']

            gas_liq_fields = ['SaturationTemperature',
                              'SaturationEnthalpyLiquid', 'SaturationEnthalpyGas',
                              'LatentHeat', 'd_Tsat_d_P',
                              'd_Hsat_d_P_Liquid', 'd_Hsat_d_P_Gas']

            user_gas_liq_fields = False
            # surface tenstion
            if tm:
                if tm.getPropertyMode('none', 'surface_tension') == 'user_law':
                    name = 'SurfaceTension'
                    exp, req, sca, sym = tm.getFormulaComponents('none',
                                                                 'surface_tension')
                    self.init_block('vol', 'all_cells', name,
                                    exp, req, sym, sca)

                for fieldId in tm.getFieldIdList():
                    if tm.getMaterials(fieldId) == 'user_material':
                        user_gas_liq_fields = True
                        for fk in authorized_fields:
                            if tm.getPropertyMode(fieldId, fk) == 'user_law':
                                name = fk + '_' + str(fieldId)
                                exp, req, sca, sym = tm.getFormulaComponents(fieldId,fk)
                                self.init_block('vol', 'all_cells', name,
                                                exp, req, sym, sca)

                        if mfm.getCompressibleStatus(fieldId) == 'on':
                            for fk in compressible_fields:
                                name = fk + '_' + str(fieldId)
                                exp, req, sca, sym = tm.getFormulaComponents(fieldId,fk)
                                self.init_block('vol', 'all_cells', name,
                                                exp, req, sym, sca)

                        # Temperature as a function of enthalpy
                        if mfm.getEnergyResolution(fieldId) == 'on':
                            name = 'temperature_' + str(fieldId)
                            exp, req, sca, sym = tm.getFormulaComponents(fieldId,
                                                                        'temperature')
                            self.init_block('vol', 'all_cells', name,
                                            exp, req, sym, sca)

            # User properties for Water/Steam kind flows
            if mfm.getPredefinedFlow() != 'None' and \
               mfm.getPredefinedFlow() != "particles_flow":
                if user_gas_liq_fields:
                    for fk in gas_liq_fields:
                        exp, req, sca, sym = tm.getFormulaComponents('none', fk)
                        self.init_block('vol', 'all_cells', fk,
                                        exp, req, sym, sca)


        # Porosity for both solvers
        vlm = LocalizationModel('VolumicZone', self.case)
        from code_saturne.model.PorosityModel import PorosityModel

        if not gwm:
            prm = PorosityModel(self.case)
            for zone in vlm.getZones():
                z_id = zone.getCodeNumber()
                zone_name = zone.getLabel()
                nature_list = zone.getNatureList()
                if 'porosity' in nature_list:
                    if zone.getNature()['porosity'] == 'on':
                        fname = 'porosity'
                        if prm.getPorosityModel(z_id) == 'anisotropic':
                            fname += '+tensorial_porosity'
                        exp, req, known_fields, sym = \
                        prm.getPorosityFormulaComponents(z_id)

                        self.init_block('vol', zone_name, fname,
                                        exp, req, sym, known_fields)


    #---------------------------------------------------------------------------

    def generate_boundary_code(self):

        from code_saturne.model.NotebookModel import NotebookModel

        if self.module_name == 'code_saturne':
            from code_saturne.model.LocalizationModel import LocalizationModel
            from code_saturne.model.Boundary import Boundary
            from code_saturne.model.TurbulenceModel import TurbulenceModel

            blm = LocalizationModel('BoundaryZone', self.case)
            tm = TurbulenceModel(self.case)

            for zone in blm.getZones():
                if zone._nature == "symmetry":
                    continue

                boundary = Boundary(zone._nature, zone._label, self.case)

                # Velocity for inlets
                if 'inlet' in zone._nature and zone._nature != 'free_inlet_outlet':
                    c = boundary.getVelocityChoice()
                    if '_formula' in c:
                        sym = ['t', 'dt', 'iter', 'surface']
                        if c == 'norm_formula':
                            req = ['u_norm']
                            sym += ['x', 'y', 'z']
                        elif c == 'flow1_formula':
                            req = ['q_m']
                        elif c == 'flow2_formula':
                            req = ['q_v']

                        for (name, val) in NotebookModel(self.case).getNotebookList():
                            sym.append((name, 'value (notebook) = ' + str(val)))

                        name = 'velocity'

                        exp = boundary.getVelocity()
                        self.init_block('bnd', zone._label, name,
                                        exp, req, sym, known_fields=[],
                                        condition=c)

                    d = boundary.getDirectionChoice()
                    if d == 'formula':
                        req  = ['dir_x', 'dir_y', 'dir_z']
                        exp  = boundary.getDirection('direction_formula')
                        sym = ['x', 'y', 'z', 't', 'dt', 'iter']

                        for (name, val) in NotebookModel(self.case).getNotebookList():
                            sym.append((name, 'value (notebook) = ' + str(val)))

                        name = 'direction'

                        self.init_block('bnd', zone._label, name,
                                        exp, req, sym,
                                        [], condition=d)

                    # Turbulence
                    tc = boundary.getTurbulenceChoice()
                    if tc == 'formula':
                        turb_model = tm.getTurbulenceModel()
                        sym = ['x', 'y', 'z', 't', 'dt', 'iter', 'surface']

                        for (name, val) in NotebookModel(self.case).getNotebookList():
                            sym.append((name, 'value (notebook) = ' + str(val)))

                        if turb_model in ('k-epsilon', 'k-epsilon-PL'):
                            name = 'turbulence_ke'
                            req  = ['k', 'epsilon']
                        elif turb_model in ('Rij-epsilon', 'Rij-SSG'):
                            name = 'turbulence_rije'
                            # Carefull! The order of rij components must be the same
                            # as in the code (r23 before r13)
                            req  = ['r11', 'r22', 'r33',
                                    'r12', 'r23', 'r13',
                                    'epsilon']
                        elif turb_model == 'Rij-EBRSM':
                            name = 'turbulence_rij_ebrsm'
                            # Carefull! The order of rij components must be the same
                            # as in the code (r23 before r13)
                            req  = ['r11', 'r22', 'r33',
                                    'r12', 'r23', 'r13',
                                    'epsilon', 'alpha']
                        elif turb_model == 'v2f-BL-v2/k':
                            name = 'turbulence_v2f'
                            req  = ['k', 'epsilon', 'phi', 'alpha']
                        elif turb_model == 'k-omega-SST':
                            name = 'turbulence_kw'
                            req  = ['k', 'omega']
                        elif turb_model == 'Spalart-Allmaras':
                            name = 'turbulence_spalart'
                            req  = ['nu_tilda']

                        exp = boundary.getTurbFormula()
                        self.init_block('bnd', zone._label, name,
                                        exp, req, sym,
                                        [], condition=tc)

                # Specific free_inlet_outlet head loss
                if zone._nature == 'free_inlet_outlet':
                    name = "head_loss"
                    req  = ['K']
                    sym  = ['x', 'y', 'z', 't', 'dt', 'iter', 'surface']
                    for (nb_var, val) in NotebookModel(self.case).getNotebookList():
                        sym.append((nb_var, 'value (notebook) = ' + str(val)))

                    exp  = boundary.getHeadLossesFormula()
                    self.init_block('bnd', zone._label, name,
                                    exp, req, sym,
                                    [], condition='formula')

                # Hydraulic head for groundwater flow
                if zone._nature == 'groundwater':
                    c = boundary.getHydraulicHeadChoice()
                    sym  = ['x', 'y', 'z', 't', 'dt', 'iter', 'surface']
                    for (name, val) in NotebookModel(self.case).getNotebookList():
                        sym.append((name, 'value (notebook) = ' + str(val)))

                    if c == 'dirichlet_formula':
                        name = 'hydraulic_head'
                        req  = ['H']
                        exp  = boundary.getHydraulicHeadFormula()
                        self.init_block('bnd', zone._label, name,
                                        exp, req, sym,
                                        [], condition=c)

                # Scalars
                scalar_list = boundary.sca_model.getScalarNameList()
                if boundary.sca_model.getMeteoScalarsNameList() != None:
                    for sca in boundary.sca_model.getMeteoScalarsNameList():
                        scalar_list.append(sca)
                if len(boundary.sca_model.getThermalScalarName()) > 0:
                    scalar_list.append(boundary.sca_model.getThermalScalarName()[0])

                if zone._nature not in ['free_inlet_outlet', 'free_surface', \
                                        'imposed_p_outlet']:
                  for sca in scalar_list:
                      c = boundary.getScalarChoice(sca)
                      sym  = ['x', 'y', 'z', 't', 'dt', 'iter', 'surface']
                      for (name, val) in NotebookModel(self.case).getNotebookList():
                          sym.append((name, 'value (notebook) = ' + str(val)))

                      if '_formula' in c:
                          exp = boundary.getScalarFormula(sca, c)
                          if c == 'dirichlet_formula':
                              req = [sca]
                          elif c == 'neumann_formula':
                              req = ['flux']
                          else:
                              req = [sca, 'hc']
                          self.init_block('bnd', zone._label, sca,
                                          exp, req, sym, [],
                                          condition=c)

        else:
            from code_saturne.model.LocalizationModel import LocalizationModel
            from code_saturne.model.BoundaryNeptune import Boundary
            from code_saturne.model.MainFieldsModel import MainFieldsModel
            from code_saturne.model.TurbulenceNeptuneModel import TurbulenceModel

            blm = LocalizationModel("BoundaryZone", self.case)
            mfm = MainFieldsModel(self.case)
            tm  = TurbulenceModel(self.case)

            for zone in blm.getZones():
                if "inlet" in zone.getNature():
                    for fId in mfm.getFieldIdList():
                        boundary = Boundary(zone.getNature(),
                                            zone.getLabel(),
                                            self.case,
                                            fId)

                        # Velocity
                        c = boundary.getVelocityChoice(fId)
                        if '_formula' in c:
                            if c == 'norm_formula':
                                req = ['u_norm']
                            elif c == 'flow1_formula':
                                req = ['q_m']
                            sym = ['x', 'y', 'z', 't', 'dt', 'iter', 'surface']

                            for (name, val) in NotebookModel(self.case).getNotebookList():
                                sym.append((name, 'value (notebook) = ' + str(val)))

                            exp = boundary.getVelocity(fId)

                            self.init_block('bnd',
                                            zone.getLabel(),
                                            'velocity_'+str(fId),
                                            exp,
                                            req,
                                            sym,
                                            [],
                                            condition=c)

                        # Velocity direction
                        d = boundary.getDirectionChoice(fId)
                        if d == 'formula':
                            exp = boundary.getDirection(fId, 'direction_formula')
                            req = ['dir_x', 'dir_y', 'dir_z']
                            sym = ['x', 'y', 'z', 't', 'dt', 'iter', 'surface']
                            for (name, val) in NotebookModel(self.case).getNotebookList():
                                sym.append((name, 'value (notebook) = ' + str(val)))

                            self.init_block('bnd',
                                            zone.getLabel(),
                                            'direction_'+str(fId),
                                            exp,
                                            req,
                                            sym,
                                            [],
                                            condition = d)

                        # Turbulence
                        tc = boundary.getTurbulenceChoice(fId)
                        turb_model = tm.getTurbulenceModel(fId)
                        if tc == 'formula' and turb_model != 'none':
                            exp, reqo, sym = boundary.getTurbFormulaComponents(fId,
                                                                              turb_model)
                            if turb_model in('k-epsilon', 'k-epsilon_linear_production'):
                                name = 'turbulence_ke_%s' % (fId)
                            elif turb_model in ('rij-epsilon_ssg', 'rij-epsilon_ebrsm'):
                                name = 'turbulence_rije_%s' % (fId)
                            elif turb_model in ('tchen', 'q2-q12'):
                                name = 'turbulence_tchen_%s' % (fId)
                            elif turb_model in ('r2-q12'):
                                name = 'turbulence_r2q12_%s' % (fId)
                            elif turb_model in ('r2-r12-tchen'):
                                name = 'turbulence_r2r12_%s' % (fId)

                            if type(reqo[0]) == tuple:
                                req = [r[0] for r in reqo]
                            else:
                                req = reqo

                            self.init_block('bnd',
                                            zone.getLabel(),
                                            name,
                                            exp,
                                            req,
                                            sym,
                                            [],
                                            condition=tc)

    #---------------------------------------------------------------------------

    def generate_source_terms_code(self):

        if self.module_name == 'code_saturne':
            from code_saturne.model.LocalizationModel import LocalizationModel
            from code_saturne.model.SourceTermsModel import SourceTermsModel
            from code_saturne.model.GroundwaterModel import GroundwaterModel
            from code_saturne.model.DefineUserScalarsModel import DefineUserScalarsModel

            vlm = LocalizationModel('VolumicZone', self.case)
            stm = SourceTermsModel(self.case)
            gwm = GroundwaterModel(self.case)

            for zone in vlm.getZones():
                z_id = str(zone.getCodeNumber())
                zone_name = zone.getLabel()

                nature_list = zone.getNatureList()

                if 'momentum_source_term' in nature_list:
                    if zone.getNature()['momentum_source_term'] == 'on':
                        if gwm.getGroundwaterModel() == 'off':
                            exp, req, sym = stm.getMomentumFormulaComponents(z_id)
                            self.init_block('src', zone_name, "momentum",
                                            exp, req, sym, [],
                                            source_type="momentum_source_term")
                        else:
                            exp, req, sym = stm.getRichardsFormulaComponents(z_id)
                            self.init_block('src', zone_name, 'richards',
                                            exp, req, sym, [],
                                            source_type="momentum_source_term")


                if 'scalar_source_term' in nature_list:
                    if zone.getNature()['scalar_source_term'] == 'on':
                        sca_list = DefineUserScalarsModel(self.case).getUserScalarNameList()
                        if gwm.getGroundwaterModel() == 'off':
                            for sca in sca_list:
                                exp, req, sym = stm.getSpeciesFormulaComponents(z_id, sca)
                                self.init_block('src', zone_name, sca,
                                                exp, req, sym, [],
                                                source_type="scalar_source_term")
                        else:
                            for sca in sca_list:
                                exp, req, sym = \
                                stm.getGroundWaterSpeciesFormulaComponents(z_id, sca)
                                self.init_block('src', zone_name, sca,
                                                exp, req, sym, [],
                                                source_type="scalar_source_term")

                if 'thermal_source_term' in nature_list:
                    if zone.getNature()['thermal_source_term'] == 'on':
                        th_sca_name = stm.therm.getThermalScalarName()
                        exp, req, sym = stm.getThermalFormulaComponents(z_id,
                                                                        th_sca_name)
                        self.init_block('src', zone_name, th_sca_name,
                                        exp, req, sym, [],
                                        source_type="thermal_source_term")
        else:
            from code_saturne.model.LocalizationModel import LocalizationModel
            from code_saturne.model.MainFieldsSourceTermsModel import MainFieldsSourceTermsModel

            vlm = LocalizationModel('VolumicZone', self.case)
            stm = MainFieldsSourceTermsModel(self.case)

            for zone in vlm.getZones():
                z_id = str(zone.getCodeNumber())
                zone_name = zone.getLabel()

                nature_list = zone.getNatureList()
                if 'thermal_source_term' in nature_list:
                    if zone.getNature()['thermal_source_term'] == 'on':
                        for fId in stm.mfm.getFieldIdList():
                            exp, req, sym = stm.getThermalFormulaComponents(z_id,
                                                                            fId,
                                                                            'enthalpy')
                            known_fields = stm.getKnownFields(fId)
                            self.init_block('src', zone_name,
                                            'enthalpy_'+str(fId),
                                            exp, req, sym, known_fields,
                                            source_type='thermal_source_term')


    #---------------------------------------------------------------------------

    def generate_initialize_code(self):

        if self.module_name == 'code_saturne':
            from code_saturne.model.LocalizationModel import LocalizationModel
            from code_saturne.model.InitializationModel import InitializationModel
            from code_saturne.model.CompressibleModel import CompressibleModel
            from code_saturne.model.DefineUserScalarsModel import DefineUserScalarsModel
            im = InitializationModel(self.case)
            cpm = CompressibleModel(self.case)

            vlm = LocalizationModel('VolumicZone', self.case)

            for zone in vlm.getZones():
                if zone.getNature()['initialization'] is 'on':
                    z_id = str(zone.getCodeNumber())
                    zone_name = zone.getLabel()

                    # Velocity
                    exp, req, sym = im.getVelocityFormulaComponents(z_id)
                    self.init_block('ini', zone_name, 'velocity',
                                    exp, req, sym, [])

                    # Turbulence
                    tin = im.node_turb.xmlGetNode('initialization', zone_id=z_id)
                    if tin:
                        if tin['choice'] is 'formula':
                            tmodel = im.node_turb['model']
                            exp, req, sym = im.getTurbFormulaComponents(z_id, tmodel)
                            self.init_block('ini', zone_name, 'turbulence',
                                            exp, req, sym, [])

                    # Thermal
                    node_t = im.node_scalartherm.xmlGetNode('variable')
                    if node_t:
                        exp, req, sym = im.getThermalFormulaComponents(z_id)
                        self.init_block('ini', zone_name, 'thermal',
                                        exp, req, sym, [])

                    # HydraulicHead
                    if im.node_veloce.xmlGetNode('variable', name = 'hydraulic_head'):
                        if im.getHydraulicHeadFormula(z_id):
                            exp, req, sym = im.getHydraulicHeadFormulaComponents(z_id)
                            self.init_block('ini', zone_name, 'hydraulic_head',
                                            exp, req, sym, [])

                    if cpm.getCompressibleModel() != 'off':
                        # Pressure
                        if im.getPressureStatus(z_id) is not 'off':
                            exp, req, sym = im.getPressureFormulaComponents(z_id)
                            self.init_block('ini', zone_name, 'pressure',
                                            exp, req, sym, [])

                        # Density
                        if im.getDensityStatus(z_id) is not 'off':
                            exp, req, sym = im.getDensityFormulaComponents(z_id)
                            self.init_block('ini', zone_name, 'density',
                                            exp, req, sym, [])

                        # Temperature
                        if im.getTemperatureStatus(z_id) is not 'off':
                            exp, req, sym = im.getTemperatureFormulaComponents(z_id)
                            self.init_block('ini', zone_name, 'temperature',
                                            exp, req ,sym, [])

                        # Energy
                        if im.getEnergyStatus(z_id) is not 'off':
                            exp, req, sym = im.getEnergyFormulaComponents(z_id)
                            self.init_block('ini', zone_name, 'energy',
                                            exp, req, sym, [])

                    # Species
                    usm = DefineUserScalarsModel(self.case)
                    for scalar in usm.getUserScalarNameList():
                        if im.getSpeciesFormula(z_id, scalar):
                            exp, req, sym = im.getSpeciesFormulaComponents(z_id, scalar)
                            self.init_block('ini', zone_name, scalar,
                                            exp, req, sym, [])

                    # Meteo
                    node_atmo = im.models.xmlGetNode('atmospheric_flows')
                    if node_atmo:
                        for mscalar in usm.getMeteoScalarsNameList():
                            node = node_atmo.xmlGetNode('variable', name=mscalar)
                            if node.xmlGetString('formula', zone_id=z_id):
                                exp,req,sym = im.getMeteoFormulaComponents(z_id,mscalar)
                                self.init_block('ini', zone_name, mscalar,
                                                exp, req, sym, [])

        else:
            from code_saturne.model.LocalizationModel import LocalizationModel
            from code_saturne.model.MainFieldsModel import MainFieldsModel
            from code_saturne.model.MainFieldsInitializationModel import MainFieldsInitializationModel
            from code_saturne.model.NonCondensableModel import NonCondensableModel
            from code_saturne.model.SpeciesModel import SpeciesModel

            vlm = LocalizationModel('VolumicZone', self.case)
            mfm = MainFieldsModel(self.case)
            mfi = MainFieldsInitializationModel(self.case)
            ncm = NonCondensableModel(self.case)
            spm = SpeciesModel(self.case)

            for zone in vlm.getZones():
                if zone.getNature()['initialization'] is 'on':
                    z_id = str(zone.getCodeNumber())
                    zone_name = zone.getLabel()

                    # Pressure
                    exp, req, sym = mfi.getPressureFormulaComponents(z_id)
                    self.init_block('ini', zone_name,
                                    'pressure',
                                    exp, req, sym, [])

                    for fId in mfm.getFieldIdList():

                        # Velocity
                        exp, req, sym = mfi.getFormulaComponents(z_id,
                                                                 fId,
                                                                 'velocity')
                        self.init_block('ini', zone_name, 'velocity_'+str(fId),
                                        exp, req, sym, [])

                        # Volume fraction
                        exp, req, sym = mfi.getFormulaComponents(z_id,
                                                                 fId,
                                                                 'volume_fraction')
                        self.init_block('ini', zone_name,
                                        'volume_fraction_'+str(fId),
                                        exp, req, sym, [])

                        # Enthalpy (only if energy resolution is activated)
                        if mfm.getEnergyResolution(fId) == 'on':
                            if mfi.getEnergyModel(z_id, fId) != 'hsat_P':
                                exp, req, sym = mfi.getFormulaComponents(z_id,
                                                                         fId,
                                                                         'enthalpy')
                                self.init_block('ini', zone_name,
                                                'enthalpy_'+str(fId),
                                                exp, req, sym, [])

                        # Non condensables
                        for nc in ncm.getNonCondensableByFieldId(fId):
                            exp, req, sym = \
                                mfi.getNonCondensableFormulaComponents(z_id,
                                                                       fId,
                                                                       nc)
                            self.init_block('ini', zone_name,
                                            ncm.getNonCondLabel(nc),
                                            exp, req, sym, [])

                        # Species
                        for s in spm.getScalarByFieldId(fId):
                            exp, req, sym = \
                                mfi.getScalarFormulaComponents(z_id,
                                                               fId,
                                                               s)
                            self.init_block('ini', zone_name,
                                            spm.getScalarLabelByName(s),
                                            exp, req, sym, [])


    #---------------------------------------------------------------------------

    def check_meg_code_syntax(self, function_name):

        if not os.path.exists(self.tmp_path):
            os.makedirs(self.tmp_path)

        if function_name in ['vol', 'bnd', 'src', 'ini']:
            self.save_function(func_type=function_name,
                               hard_path=self.tmp_path)

        from code_saturne import cs_compile

        os.chdir(self.tmp_path)
        out = open('comp.out', 'w')
        err = open('comp.err', 'w')
        compilation_test = cs_compile.compile_and_link(self.case['package'],
                                                       self.case['package'].solver,
                                                       self.tmp_path,
                                                       opt_cflags='-w',
                                                       stdout=out,
                                                       stderr=err)
        out.close()
        err.close()

        n_errors = 0
        msg = ''
        if compilation_test != 0:
            errors = open('comp.err', 'r').readlines()
            for i in range(len(errors)):
                if 'error:' in errors[i]:
                    msg += errors[i].split('error:')[-1].strip()+'\n'
                    msg += errors[i+1].strip() + '\n'

                    n_errors += 1

        os.chdir(self.data_path)

        return compilation_test, msg, n_errors

    #---------------------------------------------------------------------------

    def clean_tmp_dir(self):

        if os.path.exists(self.tmp_path):
            fl = os.listdir(self.tmp_path)
            for f in fl:
                self.delete_file(f, self.tmp_path)
            os.rmdir(self.tmp_path)

    #---------------------------------------------------------------------------

    def has_meg_code(self):

        retcode = False

        if getRunType(self.case) == 'standard':
            for func_type in self.funcs.keys():
                if len(self.funcs[func_type].keys()) > 0:
                    retcode = True
                    break

        return retcode

    #---------------------------------------------------------------------------

    def delete_file(self, c_file_name, hard_path=None):

        # Copy function file if needed
        if hard_path:
            fpath = os.path.join(hard_path, c_file_name)
        else:
            fpath = os.path.join(self.case['case_path'], 'SRC', c_file_name);

        if os.path.isfile(fpath):
            os.remove(fpath)

    #---------------------------------------------------------------------------

    def save_file(self, c_file_name, code_to_write, hard_path=None):

        if code_to_write != '':
            # Try and write the function in the src if in RESU folder
            # For debugging purposes
            try:
                if hard_path != None:
                    fpath = os.path.join(hard_path, c_file_name)
                else:
                    fpath = os.path.join(self.case['case_path'],
                                         'src',
                                         c_file_name)

                new_file = open(fpath, 'w')
                new_file.write(code_to_write)
                new_file.close()
                return 1

            except:
                # Cant save the function. xml file will still be saved
                return 2

        # Return 0 if nothing is written for robustness
        else:
            return 0

    #---------------------------------------------------------------------------

    def save_function(self, func_type, hard_path = None):

        # Delete previous existing file
        file2write = _function_names[func_type]
        self.delete_file(file2write)

        # Check if it is a standard computation
        if getRunType(self.case) != 'standard':
            return 0

        # Generate the functions code if needed
        code_to_write = ''
        if len(self.funcs[func_type].keys()) > 0:
            code_to_write = _file_header
#            if self.module_name != "code_saturne":
#                code_to_write += _file_header2
            code_to_write += _file_header3
            code_to_write += _function_header[func_type]
            for key in self.funcs[func_type].keys():
                zone_name, var_name = key.split('::')
                var_name = var_name.replace("+", ", ")
                m1 = _block_comments[func_type] % (var_name, zone_name)
                m2 = '/*-' + '-'*len(m1) + '-*/\n'
                m1 = '/* ' + m1 + ' */\n'

                code_to_write += '  ' + m2
                code_to_write += '  ' + m1
                code_to_write += self.write_block(func_type, key)
                code_to_write += '  ' + m2 + '\n'

            if func_type in ['bnd', 'src', 'ini']:
                code_to_write += '  return new_vals;\n'

            code_to_write += _file_footer

        # Write the C file if necessary
        save_status = self.save_file(file2write,
                                     code_to_write,
                                     hard_path = hard_path)

        return save_status

    #---------------------------------------------------------------------------

    def save_all_functions(self):

        save_status = 0

        is_empty    = 0
        empty_exps  = []
        for func_type in self.funcs.keys():
            state = self.save_function(func_type)
            if state != 0:
                save_status = state

            for ek in self.funcs[func_type].keys():
                if self.funcs[func_type][ek]['exp'] in [None, ""]:
                    is_empty = 1

                    empty_exps.append({})
                    empty_exps[-1]['zone'] = ek.split('::')[0]
                    empty_exps[-1]['var']  = ek.split('::')[1]
                    empty_exps[-1]['func'] = _func_short_to_long[func_type] + ' formula'

        if is_empty == 1:
            state = -1

        ret = {'state':state,
               'exps':empty_exps,
               'nexps':len(empty_exps)}

        return ret

#-------------------------------------------------------------------------------
# End
#-------------------------------------------------------------------------------
