#
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
#
#
# gen_vcnet.py -- generate Microsoft Visual C++.NET projects
#

import os
import gen_base
import gen_win
import ezt


class Generator(gen_win.WinGeneratorBase):
  "Generate a Visual C++.NET project"

  def __init__(self, fname, verfname, options):
    gen_win.WinGeneratorBase.__init__(self, fname, verfname, options,
                                      'vcnet-vcproj')

  def quote(self, str):
    return '"%s"' % str

  def gen_proj_names(self, install_targets):
    "Generate project file names for the targets"

    if float(self.vcproj_version) < 11.0:
      gen_win.WinGeneratorBase.gen_proj_names(self, install_targets)
      return

    # With VS2012 we can assume that even the light versions
    # support proper project nesting in the UI

    for target in install_targets:
      if target.msvc_name:
        target.proj_name = target.msvc_name
        continue

      target.proj_name = target.name

  def write_project(self, target, fname, depends):
    "Write a Project (.vcproj/.vcxproj)"

    if isinstance(target, gen_base.TargetProject):
      config_type='Utility'
      target_type=10
    elif isinstance(target, gen_base.TargetExe):
      config_type='Application'
      target_type=1
    elif isinstance(target, gen_base.TargetJava):
      config_type='Utility'
      target_type=10
    elif isinstance(target, gen_base.TargetLib):
      if target.msvc_static:
        config_type='StaticLibrary'
        target_type=4
      else:
        config_type='DynamicLibrary'
        target_type=2
    elif isinstance(target, gen_base.TargetI18N):
      config_type='Makefile'
      target_type=4
    else:
      raise gen_base.GenError("Cannot create project for %s" % target.name)

    target.output_name = self.get_output_name(target)
    target.output_pdb = self.get_output_pdb(target)
    target.output_dir = self.get_output_dir(target)
    target.intermediate_dir = self.get_intermediate_dir(target)
    basename = os.path.basename(target.output_name)
    target.output_ext = basename[basename.rfind('.'):]
    target.output_name_without_ext = basename[:basename.rfind('.')]

    configs = self.get_configs(target)

    sources = self.get_proj_sources(False, target)

    if self.vcproj_extension == '.vcxproj':
      for src in sources:
        if src.custom_build is not None:
          src.custom_build = src.custom_build.replace('$(InputPath)', '%(FullPath)')

    data = {
      'target' : target,
      'target_type' : target_type,
      'project_guid' : target.project_guid,
      'rootpath' : self.rootpath,
      'platforms' : self.platforms,
      'config_type' : config_type,
      'configs' : configs,
      'sources' : sources,
      'default_platform' : self.platforms[0],
      'default_config' : configs[0].name,
      'def_file' : self.get_def_file(target),
      'depends' : depends,
      'is_exe' : ezt.boolean(isinstance(target, gen_base.TargetExe)),
      'is_external' : ezt.boolean((isinstance(target, gen_base.TargetProject)
                                   or isinstance(target, gen_base.TargetI18N))
                                  and target.cmd),
      'is_utility' : ezt.boolean(isinstance(target,
                                            gen_base.TargetProject)),
      'instrument_apr_pools' : self.instrument_apr_pools,
      'instrument_purify_quantify' : self.instrument_purify_quantify,
      'version' : self.vcproj_version,
      'toolset_version' : 'v' + self.vcproj_version.replace('.',''),
      'user_macros': self.user_macros,
      }

    if self.vcproj_extension == '.vcproj':
      self.write_with_template(fname, 'templates/vcnet_vcproj.ezt', data)
      self.write_with_template(os.path.splitext(fname)[0] + '.vsprops',
                               'templates/vcnet_vsprops.ezt', data)
    else:
      self.write_with_template(fname, 'templates/vcnet_vcxproj.ezt', data)
      self.write_with_template(fname + '.filters', 'templates/vcnet_vcxproj_filters.ezt', data)

  def write(self):
    "Write a Solution (.sln)"

    # Gather sql targets for inclusion in svn_config project.
    class _eztdata(object):
      def __init__(self, **kw):
        vars(self).update(kw)

    import sys
    sql=[]
    for hdrfile, sqlfile in sorted(self.graph.get_deps(gen_base.DT_SQLHDR),
                                   key=lambda t: t[0]):
      sql.append(_eztdata(header=hdrfile.replace('/', '\\'),
                          source=sqlfile[0].replace('/', '\\'),
                          dependencies=[x.replace('/', '\\') for x in sqlfile[1:]]))

    # apr doesn't supply vcproj files, the user must convert them
    # manually before loading the generated solution
    self.move_proj_file(self.projfilesdir,
                        'svn_config' + self.vcproj_extension,
                          (
                            ('svn_python', sys.executable),
                            ('sql', sql),
                            ('project_guid', self.makeguid('__CONFIG__')),
                          )
                        )
    self.move_proj_file(self.projfilesdir,
                        'svn_locale' + self.vcproj_extension,
                        (
                          ('project_guid', self.makeguid('svn_locale')),
                        ))

    install_targets = self.get_install_targets()

    targets = [ ]

    guids = { }

    # Visual Studio uses GUIDs to refer to projects. Get them up front
    # because we need them already assigned on the dependencies for
    # each target we work with.
    for target in install_targets:
      # If there is a GUID in an external project, then use it
      # rather than generating our own that won't match and will
      # cause dependency failures.
      proj_path = self.get_external_project(target, self.vcproj_extension[1:])
      if proj_path is not None:
        target.project_guid = self.makeguid(target.name)
      guids[target.name] = target.project_guid

    self.gen_proj_names(install_targets)

    for target in install_targets:
      fname = self.get_external_project(target, self.vcproj_extension[1:])
      if fname is None:
        fname = os.path.join(self.projfilesdir, "%s%s" %
                             (target.proj_name, self.vcproj_extension))
      target.fname = fname

    # Traverse the targets and generate the project files
    for target in install_targets:
      name = target.name

      depends = [ ]
      if not isinstance(target, gen_base.TargetI18N):
        depends = self.adjust_win_depends(target, name)

      deplist = [ ]
      for i in range(len(depends)):
        dp = depends[i]
        if dp.fname.startswith(self.projfilesdir):
          path = dp.fname[len(self.projfilesdir) + 1:]
        else:
          path = os.path.join(os.path.relpath('.', self.projfilesdir),
                              dp.fname)

        if isinstance(dp, gen_base.TargetLib) and dp.msvc_delayload \
           and isinstance(target, gen_base.TargetLinked) \
           and not self.disable_shared:
          delayload = self.get_output_name(dp)
        else:
          delayload = None
        deplist.append(gen_win.ProjectItem(guid=guids[depends[i].name],
                                           index=i,
                                           path=path,
                                           delayload=delayload
                                           ))

      fname = self.get_external_project(target, self.vcproj_extension[1:])
      if fname is None:
        fname = target.fname
        self.write_project(target, fname, deplist)

      groupname = ''

      if target.name.startswith('__'):
        groupname = 'root'
      elif isinstance(target, gen_base.TargetLib):
        if isinstance(target, gen_base.TargetSWIGLib) \
           or isinstance(target, gen_base.TargetSWIG):
          groupname = 'swiglib'
        elif target.msvc_fake:
          groupname = 'fake'
        elif target.msvc_export and not self.disable_shared:
          groupname = 'dll'
        else:
          groupname = 'lib'
      elif isinstance(target, gen_base.TargetSWIGProject):
        groupname = 'swiglib'
      elif isinstance(target, gen_base.TargetJava):
        # Keep the buildbot happy
        groupname = 'root'
        # groupname = 'java'
      elif isinstance(target, gen_base.TargetExe):
        if target.name.endswith('-test') \
           or target.name.endswith('-tests'):
          groupname = 'test'
        else:
          groupname = 'exe'

      targets.append(
        gen_win.ProjectItem(name=target.name,
                            path=fname.replace(os.sep, '\\'),
                            guid=guids[target.name],
                            depends=deplist,
                            group=groupname,
                            ))

    targets.sort(key = lambda x: x.name)

    configs = [ ]
    for i in range(len(self.configs)):
      ### this is different from write_project
      configs.append(gen_win.ProjectItem(name=self.configs[i], index=i))

    # sort the values for output stability.
    guidvals = sorted(guids.values())

    # Before VS2010 dependencies are managed at the solution level
    if self.vcproj_extension == '.vcproj':
      dependency_location = 'solution'
    else:
      dependency_location = 'project'

    data = {
      'version': self.sln_version,
      'vs_version' : self.vs_version,
      'dependency_location' : dependency_location,
      'targets' : targets,
      'configs' : configs,
      'platforms' : self.platforms,
      'guids' : guidvals,
      }

    self.write_with_template('subversion_vcnet.sln', 'templates/vcnet_sln.ezt', data)

  def quote_define(self, value):
    "Properly quote special characters in a define (if needed)"

    if self.vcproj_extension == '.vcproj':
      return value.replace('"', '""')
    else:
      return value
