# The Craftr build system
# Copyright (C) 2016  Niklas Rosenstein
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
"""
Provides a high-level interface for compiling C# projects.
"""

# Default to the Microsoft C# compiler on Windows, Mono on all other platforms.
if platform.name == 'win':
  CSC = 'csc'
  RUNPREFIX = ''
else:
  CSC = 'mcs'
  RUNPREFIX = 'mono'


class CsCompiler(object):
  """
  A compiler interface for C# programs.
  """

  def __init__(self, program=None, runprefix=None):
    self.program = program or options.csc or CSC
    self.runprefix = shell.split(runprefix or options.runprefix or RUNPREFIX)

  def compile(self, output, sources, target='exe', suffix=Default, defines=(),
      optimize=True, warn=None, warnaserror=False, appconfig=None, baseaddress=None,
      checked=False, debug=False, main=None, platform=None, unsafe=False,
      win32icon=None, win32manifest=None, win32res=None, additional_flags=(),
      name=None):

    builder = TargetBuilder(gtn(name, 'csharp'), inputs=sources)
    output = buildlocal(output)

    if target in ('appcontainerexe', 'exe', 'winexe'):
      targetsuffix = '.exe'
    elif target == 'library':
      targetsuffix = '.dll'
    elif target == 'winmdobj':
      targetsuffix = '.winmdobj'
    elif target == 'module':
      targetsuffix = '.netmodule'
    else:
      raise ValueError('invalid target: {0!r}'.format(target))

    if suffix is Default:
      suffix = targetsuffix
    if suffix is not None:
      output = path.addsuffix(output, suffix)

    if warn not in (None, 0, 1, 2, 3, 4):
      raise ValueError('invalid warn: {0!r}'.format(warn))
    if platform not in (None, 'anycpu', 'anycpu32bitpreferred', 'ARM', 'x64', 'x86', 'Itanium'):
      raise ValueError('invalid platform: {0!r}'.format(platform))

    command = [self.program, '/nologo', '/out:$out']
    command += ['/warn:{0}'.format(warn)] if warn is not None else []
    command += ['/warnaserror'] if warnaserror else []
    command += ['/target:{0}'.format(target)]
    command += ['/define:{0}'.format(x) for x in defines]
    command += ['/appconfig:{0}'.format(appconfig)] if appconfig else []
    command += ['/baseaddress:{0}'.format(baseaddress)] if baseaddress else []
    command += ['/checked'] if checked else []
    command += ['/main:{0}'.format(main)] if main else []
    command += ['/platform:{0}'.format(platform)] if platform else []
    command += ['/unsafe'] if unsafe else []
    if debug:
      command += ['/debug']
    elif optimize:
      command += ['/optimize']
    command += ['/win32icon:{0}'.format(win32icon)] if win32icon else []
    command += ['/win32manifest:{0}'.format(win32manifest)] if win32manifest else []
    command += ['/win32res:{0}'.format(win32res)] if win32res else []
    command += additional_flags
    command += ['$in']

    return builder.build([command], None, [output], runprefix=self.runprefix)


csc = CsCompiler()


def compile(*args, name=None, **kwargs):
  """
  Compile one or more C# source files. Uses the :class:`CsCompiler` that is
  created globally under them name ``csc``.

  :param output: The name of the output file. The suffix will be determined
    automatically based on the *target* parameter.
  :param sources: A list of input files to compile.
  :param target: The output target type. Defaults to 'exe'. Valid values for
    this parameter are: 'appcontainer', 'exe', 'library', 'module', 'winexe'
    and 'winmdobj'.
  :param suffix: Defaults to ``Default``. If that value is specified, the
    suffix will be automatically derived from the *target* parameter. Otherwise,
    a suffix string or function can be passed.
  :param defines:
  :param optimize: Optimize generated code. Default is True.
  :param warn: Warning level. Defaults to None. Valid values for this parmaeter
    are None, 0, 1, 2, 3 and 4.
  :param warnaserror: Treat warnings as errors. Defaults to False.
  :param appconfig: Path to an assembly's application configuration file.
  :param baseaddress:
  :param checked:
  :param debug:
  :param main:
  :param platform:
  :param unsafe:
  :param win32icon:
  :param win32manifest:
  :param win32res:
  :param additional_flags: Additional flags to be appended to the command.
  :param name: Alternative target name.
  """

  return csc.compile(*args, name=gtn(name, None), **kwargs)


def csharp_compile(*args, name=None, **kwargs):
  logger.warn('csharp_compile() is deprecated, use csharp.compile() instead')
  return compile(*args, name=gtn(name), **kwargs)


__all__ = ['csharp_compile']
