import json, sys
from six import string_types

header_template = """
[

]
"""

# Each entry in the list is a chunk (probably a line) in the to-be-generated code
high_level_interface = [
    "",
    {
        "type": "print",
        "arguments": [
            "'**************** INFORMATION ***************'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'This example was auto-generated by the language-agnostic dev/scripts/example_generator.py script written by Ian Bell'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'CoolProp version:'",
            {
                "type": "function",
                "function": "get_global_param_string",
                "arguments": [
                    "'version'"
                ]
            }
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'CoolProp gitrevision:'",
            {
                "type": "function",
                "function": "get_global_param_string",
                "arguments": [
                    "'gitrevision'"
                ]
            }
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'CoolProp Fluids:'",
            {
                "type": "function",
                "function": "get_global_param_string",
                "arguments": [
                    "'FluidsList'"
                ]
            }
        ],
        "EOL":True
    },
    "",
    {
        "type": "comment",
        "comment": "See http://www.coolprop.org/coolprop/HighLevelAPI.html#table-of-string-inputs-to-propssi-function for a list of inputs to high-level interface",
        "EOL": True
    },
    {
        "type": "print",
        "arguments": [
            "'*********** HIGH LEVEL INTERFACE *****************'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Critical temperature of water:'",
            {
                "type": "function",
                "function": "Props1SI",
                "arguments": [
                    "'Water'",
                    "'Tcrit'"
                ]
            },
            "'K'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Boiling temperature of water at 101325 Pa:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'T'", "'P'", "101325", "'Q'", "0", "'Water'"
                ]
            },
            "'K'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Phase of water at 101325 Pa and 300 K:'",
            {
                "type": "function",
                "function": "PhaseSI",
                "arguments": [
                    "'P'", "101325", "'T'", "300", "'Water'"
                ]
            }
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'c_p of water at 101325 Pa and 300 K:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'C'", "'P'", "101325", "'T'", "300", "'Water'"
                ]
            },
            "'J/kg/K'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'c_p of water (using derivatives) at 101325 Pa and 300 K:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'d(H)/d(T)|P'", "'P'", "101325", "'T'", "300", "'Water'"
                ]
            },
            "'J/kg/K'"
        ],
        "EOL":True
    },
    "",
    {
        "type": "print",
        "arguments": ["'*********** HUMID AIR PROPERTIES *****************'"],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Humidity ratio of 50% rel. hum. air at 300 K, 101325 Pa:'",
            {
                "type": "function",
                "function": "HAPropsSI",
                "arguments": [
                    "'W'", "'T'", "300", "'P'", "101325", "'R'", "0.5"
                ]
            },
            "'kg_w/kg_da'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Relative humidity from last calculation:'",
            {
                "type": "function",
                "function": "HAPropsSI",
                "arguments": [
                    "'R'", "'T'", "300", "'P'", "101325", "'W'",
                    {
                        "type": "function",
                        "function": "HAPropsSI",
                        "arguments": [
                            "'W'", "'T'", "300", "'P'", "101325", "'R'", "0.5"
                        ]
                    }
                ]
            },
            "'(fractional)'"
        ],
        "EOL":True
    },
    "",
    {
        "type": "print",
        "arguments": ["'*********** INCOMPRESSIBLE FLUID AND BRINES *****************'"],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Density of 50% (mass) ethylene glycol/water at 300 K, 101325 Pa:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'D'", "'T'", "300", "'P'", "101325", "'INCOMP::MEG-50%'"
                ]
            },
            "'kg/m^3'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Viscosity of Therminol D12 at 350 K, 101325 Pa:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'V'", "'T'", "350", "'P'", "101325", "'INCOMP::TD12'"
                ]
            },
            "'Pa-s'"
        ],
        "EOL":True
    },
    "",
    {
        "type": "comment",
        "comment": "If you don't have REFPROP installed, disable the following lines",
        "EOL": True
    },
    {
        "type": "print",
        "arguments": ["'*********** REFPROP *****************'"],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'REFPROP version:'",
            {
                "type": "function",
                "function": "get_global_param_string",
                "arguments": [
                    "'REFPROP_version'"
                ]
            }
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Critical temperature of water:'",
            {
                "type": "function",
                "function": "Props1SI",
                "arguments": [
                    "'REFPROP::WATER'",
                    "'Tcrit'"
                ]
            },
            "'K'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'Boiling temperature of water at 101325 Pa:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'T'", "'P'", "101325", "'Q'", "0", "'REFPROP::WATER'"
                ]
            },
            "'K'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": [
            "'c_p of water at 101325 Pa and 300 K:'",
            {
                "type": "function",
                "function": "PropsSI",
                "arguments": [
                    "'C'", "'P'", "101325", "'T'", "300", "'REFPROP::WATER'"
                ]
            },
            "'J/kg/K'"
        ],
        "EOL":True
    }
]
low_level_interface_with_mixtures = [
    "",
    {
        "type": "print",
        "arguments": ["'*********** LOW-LEVEL INTERFACE *****************'"],
        "EOL":True
    },
    {
        "type": "custom_assignment",
        "variable_type": "AbstractState",
        "variable_name": "AS",
        "RHS": {
                "type": "function",
                "function": "factory",
                "arguments": [
                    "'HEOS'", "'Water&Ethanol'"
                ]
            },
        "EOL":True
    },
    {
        "type": "custom_assignment",
        "variable_name": "z",
        "variable_type": "vector",
        "RHS": {
            "type": "vector",
            "arguments": [
                "0.5",
                "0.5"
            ]
        },
        "EOL":True
    },
    {
        "type": "class_dereference",
        "name": "AS",
        "RHS": {
                "type": "function",
                "function": "set_mole_fractions",
                "arguments": [
                    "z"
                ]
            },
        "EOL":True
    },
    {
        "type": "class_dereference",
        "name": "AS",
        "RHS": {
                "type": "function",
                "function": "update",
                "arguments": [
                    {
                        "type": "enum",
                        "enum": "input_pairs",
                        "key": "PQ_INPUTS"
                    },
                    "101325", "1"
                ]
            },
        "EOL": True
    },
    {
        "type": "print",
        "arguments": [
            "'Normal boiling point temperature of water and ethanol:'",
            {

                "type": "class_dereference",
                "name": "AS",
                "RHS": {
                    "type": "function",
                    "function": "T",
                    "arguments": []
                }
            },
            "'K'"
        ],
        "EOL":True
    },
    "",
    {
        "type": "comment",
        "comment": "If you don't have REFPROP installed, disable the following block",
        "EOL": True
    },
    {
        "type": "print",
        "arguments": ["'*********** LOW-LEVEL INTERFACE (REFPROP) *****************'"],
        "EOL":True
    },
    {
        "type": "custom_assignment",
        "variable_type": "AbstractState",
        "variable_name": "AS2",
        "RHS": {
                "type": "function",
                "function": "factory",
                "arguments": [
                    "'REFPROP'", "'METHANE&ETHANE'"
                ]
            },
        "EOL":True
    },
    {
        "type": "custom_assignment",
        "variable_name": "z2",
        "variable_type": "vector",
        "RHS": {
            "type": "vector",
            "arguments": [
                "0.2",
                "0.8"
            ]
        },
        "EOL":True
    },
    {
        "type": "class_dereference",
        "name": "AS2",
        "RHS": {
                "type": "function",
                "function": "set_mole_fractions",
                "arguments": [
                    "z2"
                ]
            },
        "EOL":True
    },
    {
        "type": "class_dereference",
        "name": "AS2",
        "RHS": {
                "type": "function",
                "function": "update",
                "arguments": [
                    {
                        "type": "enum",
                        "enum": "input_pairs",
                        "key": "QT_INPUTS"
                    },
                    "1", "120"
                ]
            },
        "EOL": True
    },
    {
        "type": "print",
        "arguments": [
            "'Vapor molar density:'",
            {

                "type": "class_dereference",
                "name": "AS2",
                "RHS": {
                    "type": "function",
                    "function": "keyed_output",
                    "arguments": [
                        {
                            "type": "enum",
                            "enum": "parameters",
                            "key": "iDmolar"
                        }
                    ]
                }
            },
            "'mol/m^3'"
        ],
        "EOL": True
    }
]

low_level_interface = [
    "",
    {
        "type": "print",
        "arguments": ["'*********** TABULAR BACKENDS *****************'"],
        "EOL":True
    },
    {
        "type": "custom_assignment",
        "variable_type": "AbstractState",
        "variable_name": "TAB",
        "RHS": {
                "type": "function",
                "function": "factory",
                "arguments": [
                    "'BICUBIC&HEOS'", "'R245fa'"
                ]
            },
        "EOL":True
    },
    {
        "type": "class_dereference",
        "name": "TAB",
        "RHS": {
                "type": "function",
                "function": "update",
                "arguments": [
                    {
                        "type": "enum",
                        "enum": "input_pairs",
                        "key": "PT_INPUTS"
                    },
                    "101325", "300"
                ]
            },
        "EOL": True
    },
    {
        "type": "print",
        "arguments": [
            "'Mass density of refrigerant R245fa at 300 K, 101325 Pa:'",
            {

                "type": "class_dereference",
                "name": "TAB",
                "RHS": {
                    "type": "function",
                    "function": "rhomass",
                    "arguments": []
                }
            },
            "'kg/m^3'"
        ],
        "EOL":True
    },
    {
        "type": "print",
        "arguments": ["'*********** SATURATION DERIVATIVES (LOW-LEVEL INTERFACE) ***************'"],
        "EOL":True
    },
    {
        "type": "custom_assignment",
        "variable_type": "AbstractState",
        "variable_name": "AS_SAT",
        "RHS": {
                "type": "function",
                "function": "factory",
                "arguments": [
                    "'HEOS'", "'R245fa'"
                ]
            },
        "EOL":True
    },
    {
        "type": "class_dereference",
        "name": "AS_SAT",
        "RHS": {
                "type": "function",
                "function": "update",
                "arguments": [
                    {
                        "type": "enum",
                        "enum": "input_pairs",
                        "key": "PQ_INPUTS"
                    },
                    "101325", "0"
                ]
            },
        "EOL": True
    },
    {
        "type": "print",
        "arguments": [
            "'First saturation derivative:'",
            {

                "type": "class_dereference",
                "name": "AS_SAT",
                "RHS": {
                    "type": "function",
                    "function": "first_saturation_deriv",
                    "arguments": [
                        {
                            "type": "enum",
                            "enum": "parameters",
                            "key": "iP"
                        },
                        {
                            "type": "enum",
                            "enum": "parameters",
                            "key": "iT"
                        }
                    ]
                }
            },
            "'Pa/K'"
        ],
        "EOL": True
    }
]


class BaseParser(object):

    def __init__(self):
        self.pieces = high_level_interface + low_level_interface + low_level_interface_with_mixtures

    def footer(self):
        return ''

    def parse(self):
        lines = []
        for line in self.pieces:
            if isinstance(line, string_types):
                lines.append(line)
            elif isinstance(line, dict):
                lines.append(self.dict2string(line))
            else:
                raise ValueError()

        return ''.join(self.header()) + '\n'.join([self.indentation + line for line in lines if line]) + ''.join(self.footer())

    def write(self, fname, code):
        with open(fname, 'w') as fp:
            fp.write(code)

    def map_function(self, name):
        if name in self.function_name_mapping:
            return self.function_name_mapping[name]
        else:
            return name


class Python(BaseParser):

    function_name_mapping = dict(Props1SI='PropsSI',
                                 factory='AbstractState')
    type_name_mapping = {'vector': None,
                         'AbstractState': None}
    enum_name_mapping = {'input_pairs': "CoolProp", 'parameters': "CoolProp"}
    indentation = ''

    def parse_arguments(self, arguments):
        out = []
        for arg in arguments:
            if isinstance(arg, string_types) and arg[0] == "'" and arg[-1] == "'":
                out.append('\"' + arg[1:-1] + '\"')
            elif isinstance(arg, dict):
                out.append(self.dict2string(arg))
            else:
                out.append(arg)
        return out

    def dict2string(self, d):
        if d['type'] == 'comment':
            return '# ' + d['comment']
        elif d['type'] == 'function':
            return self.map_function(d['function']) + '(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'print':
            return 'print(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'vector':
            return '[' + ', '.join(self.parse_arguments(d['arguments'])) + ']'
        elif d['type'] == 'enum':
            return self.enum_name_mapping[d['enum']] + '.' + d['key']
        elif d['type'] == 'class_dereference':
            return d['name'] + '.' + self.dict2string(d['RHS'])
        elif d['type'] == 'custom_assignment':
            type_name = self.type_name_mapping[d['variable_type']]
            if type_name:
                LHS = type_name + ' ' + d['variable_name']
            else:
                LHS = d['variable_name']
            return ' '.join([LHS, '=', self.dict2string(d['RHS'])])
        else:
            return '??????????????????????????????'

    def header(self):
        return '\n'.join(['from __future__ import print_function',
                          'from CoolProp import AbstractState',
                          'from CoolProp.CoolProp import PhaseSI, PropsSI, get_global_param_string',
                          'import CoolProp.CoolProp as CoolProp',
                          'from CoolProp.HumidAirProp import HAPropsSI',
                          'from math import sin\n'])


class Octave(BaseParser):

    function_name_mapping = dict(Props1SI='CoolProp.Props1SI',
                                 PropsSI='CoolProp.PropsSI',
                                 PhaseSI='CoolProp.PhaseSI',
                                 HAPropsSI='CoolProp.HAPropsSI',
                                 get_global_param_string='CoolProp.get_global_param_string',
                                 factory='AbstractState.factory')
    type_name_mapping = {'vector': None,
                         'AbstractState': None}
    enum_name_mapping = {'input_pairs': "CoolProp", 'parameters': "CoolProp"}
    indentation = ' ' * 0

    def parse_arguments(self, arguments):
        out = []
        for arg in arguments:
            if isinstance(arg, string_types) and arg[0] == "'" and arg[-1] == "'":
                out.append('\'' + arg[1:-1] + '\'')
            elif isinstance(arg, dict):
                out.append(self.dict2string(arg))
            else:
                out.append(arg)
        return out

    def dict2string(self, d):
        if d['type'] == 'comment':
            l = '% ' + d['comment']
        elif d['type'] == 'function':
            l = self.map_function(d['function']) + '(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'print':
            args = self.parse_arguments(d['arguments'])
            args = ['num2str(' + arg + ')' for arg in args]
            l = 'disp([' + ', \' \', '.join(args) + '])'
        elif d['type'] == 'vector':
            args = self.parse_arguments(d['arguments'])
            l = '[' + ', '.join(args) + ']'
        elif d['type'] == 'class_dereference':
            l = d['name'] + '.' + self.dict2string(d['RHS'])
        elif d['type'] == 'enum':
            l = self.enum_name_mapping[d['enum']] + '.' + d['key']
        elif d['type'] == 'custom_assignment':
            type_name = self.type_name_mapping[d['variable_type']]
            if type_name:
                LHS = type_name + ' ' + d['variable_name']
            else:
                LHS = d['variable_name']

            if d['RHS']['type'] != 'vector':
                RHS = self.dict2string(d['RHS'])
            else:  # Custom processing for vector assignment
                pushes = [d['variable_name'] + '.push_back(' + arg + ');' for arg in d['RHS']['arguments']]
                RHS = 'DoubleVector(); ' + ' '.join(pushes)

            l = ' '.join([LHS, '=', RHS])
        else:
            l = '??????????????????????????????'

        if 'EOL' in d and d['EOL']:
            l += ';'
        return l

    def header(self):
        return '\n'.join(['CoolProp\n'])


class R(BaseParser):

    function_name_mapping = dict(factory='AbstractState_factory')
    type_name_mapping = {'vector': None,
                         'AbstractState': None}
    enum_name_mapping = {'input_pairs': "CoolProp", 'parameters': "CoolProp"}
    indentation = ' ' * 0

    def parse_arguments(self, arguments):
        out = []
        for arg in arguments:
            if isinstance(arg, string_types) and arg[0] == "'" and arg[-1] == "'":
                out.append('\'' + arg[1:-1] + '\'')
            elif isinstance(arg, dict):
                out.append(self.dict2string(arg))
            else:
                out.append(arg)
        return out

    def dict2string(self, d):
        if d['type'] == 'comment':
            l = '# ' + d['comment']
        elif d['type'] == 'function':
            l = self.map_function(d['function']) + '(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'print':
            args = self.parse_arguments(d['arguments'])
            l = 'writeLines(paste(' + ', \' \', '.join(args) + '))'
        elif d['type'] == 'vector':
            args = self.parse_arguments(d['arguments'])
            l = '[' + ', '.join(args) + ']'
        elif d['type'] == 'class_dereference':
            l = d['name'] + '$' + self.dict2string(d['RHS'])
        elif d['type'] == 'enum':
            l = '"' + d['key'] + '"'
        elif d['type'] == 'custom_assignment':
            type_name = self.type_name_mapping[d['variable_type']]
            if type_name:
                LHS = type_name + ' ' + d['variable_name']
            else:
                LHS = d['variable_name']

            if d['RHS']['type'] != 'vector':
                RHS = self.dict2string(d['RHS'])
            else:  # Custom processing for vector assignment
                pushes = [d['variable_name'] + ' <- ' + arg + ';' for arg in d['RHS']['arguments']]
                RHS = 'c(' + ', '.join([arg for arg in d['RHS']['arguments']]) + ')'

            l = ' '.join([LHS, '=', RHS])
        else:
            l = '??????????????????????????????'

        if 'EOL' in d and d['EOL']:
            l += ''
        return l

    def header(self):
        return 'dyn.load(paste("CoolProp", .Platform$dynlib.ext, sep=""))\nlibrary(methods) # See http://stackoverflow.com/a/19468533\nsource("CoolProp.R")\ncacheMetaData(1)\n'


class MATLAB(BaseParser):

    function_name_mapping = dict(Props1SI='CoolProp.Props1SI',
                                 PropsSI='CoolProp.PropsSI',
                                 PhaseSI='CoolProp.PhaseSI',
                                 HAPropsSI='CoolProp.HAPropsSI',
                                 get_global_param_string='CoolProp.get_global_param_string',
                                 factory='CoolProp.AbstractState.factory')
    enum_name_mapping = {'input_pairs': "CoolProp", 'parameters': "CoolProp"}
    type_name_mapping = {'vector': None,
                         'AbstractState': None}
    indentation = ''

    def parse_arguments(self, arguments):
        out = []
        for arg in arguments:
            if isinstance(arg, string_types) and arg[0] == "'" and arg[-1] == "'":
                out.append('\'' + arg[1:-1] + '\'')
            elif isinstance(arg, dict):
                out.append(self.dict2string(arg))
            else:
                out.append(arg)
        return out

    def dict2string(self, d):
        if d['type'] == 'comment':
            l = '% ' + d['comment']
        elif d['type'] == 'function':
            l = self.map_function(d['function']) + '(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'print':
            args = self.parse_arguments(d['arguments'])
            args = ['num2str(' + arg + ')' for arg in args]
            l = 'disp([' + ', \' \', '.join(args) + '])'
        elif d['type'] == 'vector':
            args = self.parse_arguments(d['arguments'])
            l = '[' + ', '.join(args) + ']'
        elif d['type'] == 'enum':
            l = self.enum_name_mapping[d['enum']] + '.' + d['key']
        elif d['type'] == 'class_dereference':
            l = d['name'] + '.' + self.dict2string(d['RHS'])
        elif d['type'] == 'custom_assignment':
            type_name = self.type_name_mapping[d['variable_type']]
            if type_name:
                LHS = type_name + ' ' + d['variable_name']
            else:
                LHS = d['variable_name']
            # Custom processing for vector assignment
            if d['RHS']['type'] != 'vector':
                RHS = self.dict2string(d['RHS'])
            else:  # vector
                pushes = [d['variable_name'] + '.push_back(' + arg + ');' for arg in d['RHS']['arguments']]
                RHS = 'CoolProp.DoubleVector(); ' + ' '.join(pushes)

            l = ' '.join([LHS, '=', RHS])
        else:
            l = '??????????????????????????????'

        if 'EOL' in d and d['EOL']:
            l += ';'
        return l

    def header(self):
        return ''


class Java(BaseParser):

    function_name_mapping = dict(Props1SI='CoolProp.Props1SI',
                                 PropsSI='CoolProp.PropsSI',
                                 PhaseSI='CoolProp.PhaseSI',
                                 HAPropsSI='CoolProp.HAPropsSI',
                                 get_global_param_string='CoolProp.get_global_param_string',
                                 factory='AbstractState.factory')
    type_name_mapping = {'vector': 'DoubleVector',
                         'AbstractState': 'AbstractState'}
    enum_name_mapping = {'input_pairs': "input_pairs", 'parameters': "parameters"}
    indentation = ' ' * 8

    def parse_arguments(self, arguments):
        out = []
        for arg in arguments:
            if isinstance(arg, string_types) and arg[0] == "'" and arg[-1] == "'":
                out.append('\"' + arg[1:-1] + '\"')
            elif isinstance(arg, dict):
                out.append(self.dict2string(arg))
            else:
                out.append(arg)
        return out

    def dict2string(self, d):
        if d['type'] == 'comment':
            l = '// ' + d['comment']
        elif d['type'] == 'print':
            l = 'System.out.println(' + ' + " " + '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'function':
            l = self.map_function(d['function']) + '(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'vector':
            l = '{' + ', '.join(self.parse_arguments(d['arguments'])) + '}'
        elif d['type'] == 'enum':
            l = self.enum_name_mapping[d['enum']] + '.' + d['key']
        elif d['type'] == 'class_dereference':
            l = d['name'] + '.' + self.dict2string(d['RHS'])
        elif d['type'] == 'custom_assignment':
            type_name = self.type_name_mapping[d['variable_type']]
            if type_name:
                LHS = type_name + ' ' + d['variable_name']
            else:
                LHS = d['variable_name']

            if d['RHS']['type'] != 'vector':
                RHS = self.dict2string(d['RHS'])
            else:  # Custom processing for vector assignment
                pushes = [d['variable_name'] + '.add(' + arg + ');' for arg in d['RHS']['arguments']]
                RHS = 'new DoubleVector(); ' + ' '.join(pushes)

            l = ' '.join([LHS, '=', RHS])
        else:
            raise ValueError

        if 'EOL' in d and d['EOL']:
            l += ';'

        return l

    def header(self):
        return 'public class Example {\n    static {\n        System.loadLibrary("CoolProp");\n    }\n\n    public static void main(String argv[]){\n'

    def footer(self):
        return '\n    }\n}'


class Csharp(BaseParser):

    function_name_mapping = dict(Props1SI='CoolProp.Props1SI',
                                 PropsSI='CoolProp.PropsSI',
                                 PhaseSI='CoolProp.PhaseSI',
                                 HAPropsSI='CoolProp.HAPropsSI',
                                 get_global_param_string='CoolProp.get_global_param_string',
                                 factory='AbstractState.factory')
    type_name_mapping = {'vector': "DoubleVector",
                         'AbstractState': 'AbstractState'}
    enum_name_mapping = {'input_pairs': "input_pairs", 'parameters': "parameters"}
    indentation = ' ' * 12

    def parse_arguments(self, arguments):
        out = []
        for arg in arguments:
            if isinstance(arg, string_types) and arg[0] == "'" and arg[-1] == "'":
                out.append('\"' + arg[1:-1] + '\"')
            elif isinstance(arg, dict):
                out.append(self.dict2string(arg))
            else:
                out.append(arg)
        return out

    def dict2string(self, d):
        if d['type'] == 'comment':
            l = '// ' + d['comment']
        elif d['type'] == 'print':
            l = 'Console.Write(' + ' + " " + '.join(self.parse_arguments(d['arguments'])) + ' + "\\n")'
        elif d['type'] == 'function':
            l = self.map_function(d['function']) + '(' + ', '.join(self.parse_arguments(d['arguments'])) + ')'
        elif d['type'] == 'vector':
            l = 'new DoubleVector(new double[]{' + ', '.join(self.parse_arguments(d['arguments'])) + '})'
        elif d['type'] == 'enum':
            l = self.enum_name_mapping[d['enum']] + '.' + d['key']
        elif d['type'] == 'class_dereference':
            l = d['name'] + '.' + self.dict2string(d['RHS'])
        elif d['type'] == 'custom_assignment':
            type_name = self.type_name_mapping[d['variable_type']]
            if type_name:
                LHS = type_name + ' ' + d['variable_name']
            else:
                LHS = d['variable_name']
            l = ' '.join([LHS, '=', self.dict2string(d['RHS'])])
        else:
            raise ValueError

        if 'EOL' in d and d['EOL']:
            l += ';'

        return l

    def header(self):
        return 'using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace ConsoleApplication1\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n'

    def footer(self):
        return '\n        }\n    }\n}'


if __name__ == '__main__':

    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("language", help="The target language (Python, Java, Csharp, etc.)")
    parser.add_argument("destination", help="The path to the output file")
    args = parser.parse_args()
    writer = locals()[args.language]()
    writer.write(args.destination, writer.parse())
    print("written to %s" % args.destination)
