#!/usr/bin/python
#
# 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.
#
"""
Converts a project's Cordova.plist file into a config.xml one. This conversion is required for Cordova 2.3.

Usage:
  plist2xml.py path/to/project
"""
from __future__ import print_function

import fileinput
import io
import os
import plistlib
import re
import sys
from xml.dom import minidom
from xml.etree import ElementTree


def Usage():
  sys.stderr.write(__doc__)
  sys.exit(1)


def ValueToElement(node_name, key, value):
  if isinstance(value, bool):
    value = str(value).lower()
  return ElementTree.Element(node_name, attrib={'name':key, 'value':str(value)})


def AppendDict(d, node, name, ignore=()):
  for key in sorted(d):
    if key not in ignore:
      node.append(ValueToElement(name, key, d[key]))


def FindProjectFile(path):
  # Do an extra abspath here to strip off trailing / if present.
  path = os.path.abspath(path)
  if path.endswith('.pbxproj'):
    return path
  elif path.endswith('.xcodeproj'):
    return os.path.join(path, 'project.pbxproj')
  for f in os.listdir(path):
    if f.endswith('.xcodeproj'):
      return os.path.join(path, f, 'project.pbxproj')
  raise Exception('Invalid project path. Please provide the path to your .xcodeproj directory')


def FindPlistFile(search_path):
  for root, unused_dirnames, file_names in os.walk(search_path):
    for file_name in file_names:
      if file_name == 'Cordova.plist':
        return os.path.join(root, file_name)
  raise Exception('Could not find a file named "Cordova.plist" within ' + search_path)


def ConvertPlist(src_path, dst_path):
  # Run it through plutil to ensure it's not a binary plist.
  os.system("plutil -convert xml1 '%s'" % src_path)
  plist = plistlib.readPlist(src_path)
  root = ElementTree.Element('cordova')

  AppendDict(plist, root, 'preference', ignore=('Plugins', 'ExternalHosts'))

  plugins = ElementTree.Element('plugins')
  root.append(plugins)
  AppendDict(plist['Plugins'], plugins, 'plugin')

  for value in sorted(plist['ExternalHosts']):
    root.append(ElementTree.Element('access', attrib={'origin':value}))

  tree = ElementTree.ElementTree(root)
  with io.BytesIO() as s:
    tree.write(s, encoding='UTF-8')
    mini_dom = minidom.parseString(s.getvalue())
  with open(dst_path, 'wb') as out:
    out.write(mini_dom.toprettyxml(encoding='UTF-8'))


def UpdateProjectFile(path):
  file_handle = fileinput.input(path, inplace=1)
  for line in file_handle:
    if 'Cordova.plist' in line:
      line = line.replace('Cordova.plist', 'config.xml')
      line = line.replace('lastKnownFileType = text.plist.xml', 'lastKnownFileType = text.xml')
    print(line, end=' ')
  file_handle.close()


def main(argv):
  if len(argv) != 1:
    Usage();

  project_file = FindProjectFile(argv[0])
  plist_file = FindPlistFile(os.path.dirname(os.path.dirname(project_file)))

  config_file = os.path.join(os.path.dirname(plist_file), 'config.xml')
  sys.stdout.write('Converting %s to %s.\n' % (plist_file, config_file))
  ConvertPlist(plist_file, config_file)

  sys.stdout.write('Updating references in %s\n' % project_file)
  UpdateProjectFile(project_file)

  os.unlink(plist_file)
  sys.stdout.write('Updates Complete.\n')


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