#
# 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_cmake.py -- generator for CMake build system
#

import os
import sys
import ezt
import gen_base

class _eztdata(object):
  def __init__(self, **kw):
    vars(self).update(kw)

def get_target_type(target):
  if isinstance(target, gen_base.TargetExe):
    if target.install == "test" and target.testing != "skip":
      return "test"
    else:
      return "exe"
  if isinstance(target, gen_base.TargetSWIG):
    return "swig"
  if isinstance(target, gen_base.TargetSWIGProject):
    return "swig-project"
  if isinstance(target, gen_base.TargetLib):
    return "lib"
  else:
    return str(type(target))

def get_module_name(name):
  """
  Returns the name of the library as a module name. Module name
  is a library name without `libsvn_` prefix and in upper case.
  For example, for `libsvn_fs_fs`, the function would return
  just `FS_FS`.
  """

  return name[7:].upper()

def get_output_name(target):
  if target.name.startswith("lib"):
    return target.name[3:] + "-1"
  elif isinstance(target, gen_base.TargetSWIG):
    module_name = target.name[len(target.lang + "_"):]
    if target.lang == "python":
      return module_name
    else:
      return target.name
  else:
    return target.name

def get_target_conditions(target):
  enable_condition = []

  if isinstance(target, gen_base.TargetRaModule) or \
     isinstance(target, gen_base.TargetFsModule):
    enable_condition.append("SVN_ENABLE_" + get_module_name(target.name))

  if isinstance(target, gen_base.TargetLib):
    if target.name == "libsvnxx":
      enable_condition.append("SVN_ENABLE_SVNXX")
    elif target.name == "libsvn_auth_kwallet":
      enable_condition.append("SVN_ENABLE_AUTH_KWALLET")
    elif target.name == "libsvn_auth_gnome_keyring":
      enable_condition.append("SVN_ENABLE_AUTH_GNOME_KEYRING")

  if isinstance(target, gen_base.TargetExe):
    if target.install == "test" or target.install == "sub-test":
      enable_condition.append("SVN_ENABLE_TESTS")
    elif target.install == "tools":
      enable_condition.append("SVN_ENABLE_TOOLS")
    else:
      enable_condition.append("SVN_ENABLE_PROGRAMS")

  if isinstance(target, gen_base.TargetSWIG) or \
     isinstance(target, gen_base.TargetSWIGLib):
    enable_condition.append("SVN_ENABLE_SWIG_" + target.lang.upper())

  if isinstance(target, gen_base.TargetApacheMod):
    enable_condition.append("SVN_ENABLE_APACHE_MODULES")

  return enable_condition

class Generator(gen_base.GeneratorBase):
  _extension_map = {
    ('exe', 'target'): '.exe',
    ('exe', 'object'): '.obj',
    ('lib', 'target'): '.dll',
    ('lib', 'object'): '.obj',
    ('pyd', 'target'): '.pyd',
    ('pyd', 'object'): '.obj',
    ('so', 'target'): '.so',
    ('so', 'object'): '.obj',
  }

  def __init__(self, fname, verfname, options=None):
    gen_base.GeneratorBase.__init__(self, fname, verfname, options)

  def write(self):
    targets = []

    for target in self.get_install_sources():
      group = None
      enable_condition = []
      enable_condition += get_target_conditions(target)
      build_type = None
      swig_lang = None
      is_apache_mod = None

      if isinstance(target, gen_base.TargetScript):
        # there is nothing to build
        continue
      elif isinstance(target, gen_base.TargetRaModule):
        group = "ra-libs"
        build_type = "${SVN_RA_BUILD_TYPE}"
      elif isinstance(target, gen_base.TargetFsModule):
        group = "fs-libs"
        build_type = "${SVN_FS_BUILD_TYPE}"
      elif isinstance(target, gen_base.TargetApacheMod):
        is_apache_mod = True
      elif isinstance(target, gen_base.TargetSWIG):
        swig_lang = target.lang
      elif isinstance(target, gen_base.TargetLib):
        if target.msvc_static:
          build_type = "STATIC"

      msvc_export = []
      if isinstance(target, gen_base.TargetLib):
        for export in target.msvc_export:
          msvc_export.append("subversion/include/" + export)

      sources = []
      private_libs = []
      public_libs = []

      for dep in self.get_dependencies(target.name):
        enable_condition += get_target_conditions(dep)

        if isinstance(dep, gen_base.TargetSWIG):
          # Just ignore them
          pass
        elif isinstance(dep, gen_base.TargetLinked):
          if dep.external_lib:
            if dep.name == "ra-libs":
              private_libs.append("ra-libs")
            elif dep.name == "fs-libs":
              private_libs.append("fs-libs")
            elif dep.name in ["apriconv",
                              "apr_memcache",
                              "magic",
                              "macos-plist",
                              "macos-keychain",
                              "sasl"]:
              # These dependencies are currently ignored
              # TODO:
              pass
            else:
              dep_name = "external-" + dep.name

              # APR and APR-Util are part of our public interface and should be
              # declared PUBLIC in library target dependencies.
              if (dep_name in ["external-apr", "external-aprutil"]
                  and not isinstance(target, gen_base.TargetExe)):
                public_libs.append(dep_name)
              else:
                private_libs.append(dep_name)
          else:
            private_libs.append(dep.name)
        elif isinstance(dep, gen_base.ObjectFile):
          for source in self.graph.get_sources(gen_base.DT_OBJECT, dep,
                                               gen_base.SourceFile):
            sources.append(source.filename)

          for obj in self.graph.get_sources(gen_base.DT_OBJECT, dep,
                                            gen_base.ObjectFile):
            for source in self.graph.get_sources(gen_base.DT_SWIG_C, obj,
                                                 gen_base.SWIGSource):
              sources.append(source.filename)

      target_type = get_target_type(target)

      if target_type in ["exe", "lib", "test", "swig"]:
        msvc_libs = []
        msvc_objects = []

        for lib in target.msvc_libs:
          if lib.endswith(".obj"):
            msvc_objects.append(lib)
          else:
            msvc_libs.append(lib)

        if isinstance(target, gen_base.TargetLib) or target.install == "bin":
          install_target = True
        else:
          install_target = False

        enable_condition = list(set(enable_condition))
        if len(enable_condition) > 0:
          enable_condition_str = " AND ".join(enable_condition)
        else:
          enable_condition_str = "TRUE"

        new_target = _eztdata(
          name = target.name,
          output_name = get_output_name(target),
          type = target_type,
          sources = sources,
          libs = public_libs + private_libs,
          public_libs = public_libs,
          private_libs = private_libs,
          msvc_libs = msvc_libs,
          msvc_objects = msvc_objects,
          msvc_export = msvc_export,
          enable_condition = enable_condition_str,
          group = group,
          build_type = build_type,
          description = target.desc,
          srcdir = target.path,
          install_target = ezt.boolean(install_target),
          swig_lang = swig_lang,
          is_apache_mod = is_apache_mod,
          namespace = os.path.basename(target.path),
        )

        targets.append(new_target)

    data = _eztdata(
      targets = targets,
    )
    output_file = open(os.path.join('build', 'cmake', 'targets.cmake'), 'w')

    template = ezt.Template(compress_whitespace=False)
    template.parse_file(os.path.join('build', 'generator', 'templates',
                                     'targets.cmake.ezt'))
    template.generate(output_file, data)

  def get_install_sources(self):
    install_sources = self.graph.get_all_sources(gen_base.DT_INSTALL)
    result = []

    for target in install_sources:
      if not self.check_ignore_target(target):
        result.append(target)

    result.sort(key = lambda s: s.name)

    return result

  def get_dependencies(self, target_name):
    deps = []

    deps += self.graph.get_sources(gen_base.DT_LINK, target_name)
    deps += self.graph.get_sources(gen_base.DT_NONLIB, target_name)

    return deps

  def check_ignore_target(self, target):
    ignore_names = [
      "svnxx-tests",

      "libsvnjavahl",
      "__JAVAHL__",
      "__JAVAHL_TESTS__",
    ]

    for name in ignore_names:
      if target.name == name:
        return True

  if sys.platform == 'win32':
    def errno_filter(self, codes):
      """From errno_filter() in gen_win.py"""
      return [code for code in codes if not (10000 <= code <= 10100)]
