#!/usr/bin/python
#
# Copyright 2017 The Cobalt Authors. All Rights Reserved.
#
# Licensed 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.
#
"""Extracts python files and writes platforms information.

This script extracts the app launcher scripts (and all their dependencies) from
the Cobalt source tree, and then packages them into a user-specified location so
that the app launcher can be run independent of the Cobalt source tree.
"""

import argparse
import logging
import os
import shutil
import sys

import _env  # pylint: disable=unused-import
from paths import REPOSITORY_ROOT
from paths import THIRD_PARTY_ROOT
sys.path.append(THIRD_PARTY_ROOT)
import jinja2
import starboard.tools.platform


def _MakeDir(d):
  """Make the specified directory and any parents in the path.

  Args:
    d: Directory name to create.
  """
  if d and not os.path.isdir(d):
    root = os.path.dirname(d)
    _MakeDir(root)
    os.mkdir(d)


def _IsOutDir(source_root, d):
  """Check if d is under source_root/out directory.

  Args:
    source_root: Absolute path to the root of the files to be copied.
    d: Directory to be checked.
  """
  out_dir = os.path.join(source_root, 'out')
  return out_dir in d


def CopyPythonFiles(source_root, dest_root):
  """Copy all python files to the destination folder.

  Copy from source to destination while maintaining the directory structure.

  Args:
    source_root: Absolute path to the root of files to be copied.
    dest_root: Destination into which files will be copied.
  """
  logging.info('Searching in ' + source_root + ' for python files.')
  file_list = []
  for root, _, files in os.walk(source_root):
    # Eliminate any locally built files under the out directory.
    if _IsOutDir(source_root, root):
      continue
    for f in files:
      if f.endswith('.py'):
        source_file = os.path.join(root, f)
        dest_file = source_file.replace(source_root, dest_root)
        file_list.append((source_file, dest_file))

  logging.info('Starting copy of ' + str(len(file_list)) + ' python files.')
  for (source, dest) in file_list:
    _MakeDir(os.path.dirname(dest))
    shutil.copy2(source, dest)
  logging.info('Copy of python files finished.')


def WritePlatformsInfo(repo_root, dest_root):
  """Get platforms' information and write the platform.py based on a template.

  Platform.py is responsible for enumerating all supported platforms in the
  Cobalt source tree.  Since we are extracting the app launcher script from the
  Cobalt source tree, this function records which platforms are supported while
  the Cobalt source tree is still available and bakes them in to a newly created
  platform.py file that does not depend on the Cobalt source tree.

  Args:
    repo_root: Absolute path to the root of the repository where platforms'
      information is retrieved.
    dest_root: Absolute path to the root of the new repository into which
      platforms' information to be written.
  """
  logging.info('Baking platform info files.')
  current_file = os.path.abspath(__file__)
  current_dir = os.path.dirname(current_file)
  dest_dir = current_dir.replace(repo_root, dest_root)

  platforms_map = {}
  for p in starboard.tools.platform.GetAll():
    platform_path = os.path.relpath(
        starboard.tools.platform.Get(p).path, repo_root)
    platforms_map[p] = platform_path

  template = jinja2.Template(
      open(os.path.join(current_dir, 'platform.py.template')).read())
  with open(os.path.join(dest_dir, 'platform.py'), 'w+') as f:
    template.stream(platforms_map=platforms_map).dump(f, encoding='utf-8')
  logging.info('Finished baking in platform info files.')


def CopyAppLauncherTools(repo_root, dest_root):
  if os.path.isdir(dest_root):
    shutil.rmtree(dest_root)

  CopyPythonFiles(repo_root, dest_root)
  WritePlatformsInfo(repo_root, dest_root)

  # Create an extra zip file of the app launcher package so that users have
  # the option of downloading a single file which is much faster, especially
  # on x20.
  logging.info('Creating a zip file of the app launcher package.')

  # Make a zip that has the same name as the dest_root. Then the zip file
  # and dest_root are guaranteed to be on the same file system under the
  # same parent, so that moving the zip file to dest_root is optimized.
  app_launcher_zip_file = shutil.make_archive(dest_root, 'zip', dest_root)
  dest_zip = os.path.join(dest_root, 'app_launcher.zip')
  if os.path.isfile(dest_zip):
    os.unlink(dest_zip)
  shutil.move(app_launcher_zip_file, dest_zip)


def main(command_args):
  logging.basicConfig(level=logging.INFO)
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '-d',
      '--destination_root',
      required=True,
      help='The path to the root of the destination folder into which the '
      'python scripts are packaged.')
  args = parser.parse_args(command_args)
  CopyAppLauncherTools(REPOSITORY_ROOT, args.destination_root)
  return 0


if __name__ == '__main__':
  sys.exit(main(sys.argv[1:]))
