import os
import logging

from edalize.edatool import Edatool

logger = logging.getLogger(__name__)

MAKE_HEADER ="""#Generated by Edalize
ifndef MODEL_TECH
$(error Environment variable MODEL_TECH was not found. It should be set to <modelsim install path>/bin)
endif

CC ?= gcc
CFLAGS := -c -std=c99 -fPIC -fno-stack-protector -g -m32

LD ?= ld
LDFLAGS := -shared -E -melf_i386

RM ?= rm
INCS := -I$(MODEL_TECH)/../include

VSIM ?= $(MODEL_TECH)/vsim

TOPLEVEL      := {toplevel}
VPI_MODULES   := {modules}
PARAMETERS    ?= {parameters}
PLUSARGS      ?= {plusargs}
VSIM_OPTIONS  ?= {vsim_options}
EXTRA_OPTIONS ?= $(VSIM_OPTIONS) $(addprefix -g,$(PARAMETERS)) $(addprefix +,$(PLUSARGS))

all: work $(VPI_MODULES)

run: work $(VPI_MODULES)
	$(VSIM) -do "run -all" -c $(addprefix -pli ,$(VPI_MODULES)) $(TOPLEVEL) $(EXTRA_OPTIONS)

run-gui: work $(VPI_MODULES)
	$(VSIM) -gui $(addprefix -pli ,$(VPI_MODULES)) $(TOPLEVEL) $(EXTRA_OPTIONS)

work:
	$(VSIM) -c -do "do edalize_main.tcl; exit"

clean: {clean_targets}
"""

VPI_MAKE_SECTION = """
{name}_OBJS := {objs}
{name}_LIBS := {libs}
{name}_INCS := $(INCS) {incs}

$({name}_OBJS): %.o : %.c
	$(CC) $(CFLAGS) $({name}_INCS) -o $@ $<

{name}: $({name}_OBJS)
	$(LD) $(LDFLAGS) -o $@ $? $({name}_LIBS)

clean_{name}:
	$(RM) $({name}_OBJS) {name}
"""

class Modelsim(Edatool):

    tool_options = {'lists' : {'vlog_options' : 'String',
                               'vsim_options' : 'String'}}

    argtypes = ['plusarg', 'vlogdefine', 'vlogparam']

    def _write_build_rtl_tcl_file(self, tcl_main):
        tcl_build_rtl  = open(os.path.join(self.work_root, "edalize_build_rtl.tcl"), 'w')

        (src_files, incdirs) = self._get_fileset_files()
        vlog_include_dirs = ['+incdir+'+d.replace('\\','/') for d in incdirs]

        libs = []
        for f in src_files:
            if not f.logical_name:
                f.logical_name = 'work'
            if not f.logical_name in libs:
                tcl_build_rtl.write("vlib {}\n".format(f.logical_name))
                libs.append(f.logical_name)
            if f.file_type.startswith("verilogSource") or \
               f.file_type.startswith("systemVerilogSource"):
                cmd = 'vlog'
                args = []

                args += self.tool_options.get('vlog_options', [])

                for k, v in self.vlogdefine.items():
                    args += ['+define+{}={}'.format(k,self._param_value_str(v))]

                if f.file_type.startswith("systemVerilogSource"):
                    args += ['-sv']
                args += vlog_include_dirs
            elif f.file_type.startswith("vhdlSource"):
                cmd = 'vcom'
                if f.file_type.endswith("-87"):
                    args = ['-87']
                if f.file_type.endswith("-93"):
                    args = ['-93']
                if f.file_type.endswith("-2008"):
                    args = ['-2008']
                else:
                    args = []
            elif f.file_type == 'tclSource':
                cmd = None
                tcl_main.write("do {}\n".format(f.name))
            elif f.file_type == 'user':
                cmd = None
            else:
                _s = "{} has unknown file type '{}'"
                logger.warning(_s.format(f.name, f.file_type))
                cmd = None
            if cmd:
                args += ['-quiet']
                args += ['-work', f.logical_name]
                args += [f.name.replace('\\','/')]
                tcl_build_rtl.write("{} {}\n".format(cmd, ' '.join(args)))

    def _write_makefile(self):
        vpi_make = open(os.path.join(self.work_root, "Makefile"), 'w')
        _parameters = []
        for key, value in self.vlogparam.items():
            _parameters += ['{}={}'.format(key, self._param_value_str(value))]
        _plusargs = []
        for key, value in self.plusarg.items():
            _plusargs += ['{}={}'.format(key, self._param_value_str(value))]

        _vsim_options = self.tool_options.get('vsim_options', [])

        _modules = [m['name'] for m in self.vpi_modules]
        _clean_targets = ' '.join(["clean_"+m for m in _modules])
        _s = MAKE_HEADER.format(toplevel = self.toplevel,
                                parameters = ' '.join(_parameters),
                                plusargs = ' '.join(_plusargs),
                                vsim_options = ' '.join(_vsim_options),
                                modules = ' '.join(_modules),
                                clean_targets = _clean_targets)
        vpi_make.write(_s)

        for vpi_module in self.vpi_modules:
            _name = vpi_module['name']
            _objs = [os.path.splitext(s)[0]+'.o' for s in vpi_module['src_files']]
            _libs = ['-l'+l for l in vpi_module['libs']]
            _incs = ['-I'+d for d in vpi_module['include_dirs']]
            _s = VPI_MAKE_SECTION.format(name=_name,
                                         objs=' '.join(_objs),
                                         libs=' '.join(_libs),
                                         incs=' '.join(_incs))
            vpi_make.write(_s)

        vpi_make.close()

    def configure_main(self):
        tcl_main = open(os.path.join(self.work_root, "edalize_main.tcl"), 'w')
        tcl_main.write("do edalize_build_rtl.tcl\n")

        self._write_build_rtl_tcl_file(tcl_main)
        self._write_makefile()
        tcl_main.close()

    def run_main(self):
        args = ['run']

        # Set plusargs
        if self.plusarg:
            plusargs = []
            for key, value in self.plusarg.items():
                plusargs += ['{}={}'.format(key, self._param_value_str(value))]
            args.append('PLUSARGS='+' '.join(plusargs))

        self._run_tool('make', args)
