#!/usr/bin/env python
"""Determine the filename for a JSON model.

This is a utility script that will parse a service's metadata
file and determine where this file should be placed.  It
hides the details of botocore's internal file layout for JSON
models.

It its simplest usage you give it a path to a file and it
prints the location within botocore::

  $ scripts/get-model-filename /tmp/myfile.json
  /Users/foo/botocore/botocore/data/aws/cloudwatch/2010-08-01.normal.json

If you want, you can use the ``-c`` option to actually copy the file
to this location.  When this option is specified, the parent directory
will be created if it does not exist.

  $ scripts/get-model-filename -c /tmp/myfile.json
  /Users/foo/botocore/data/aws/cloudwatch/2010-08-01.normal.json
  Copied: /tmp/myfile.json -> /Users/foo/botocore/data/aws/cloudwatch/2010-08-01.normal.json

"""
# Note we're using optparse for 2.6 compat.
# We don't have particularly complicated command line
# parsing requirements so we'll deal with it.
import optparse
import unittest
import sys
import json
import shutil
import os
try:
    from StringIO import StringIO
except ImportError:
    # Python3 we need to import from the io module.
    from io import StringIO


def determine_json_data_path(fileobj):
    """Determine the filepath for a JSON model.

    This is a utility function that will introspect a JSON file and determine
    where it should be placed in botocore/data/aws/.

    """
    parsed = json.load(fileobj)
    if 'metadata' in parsed:
        return _determine_path_from_metadata(parsed)
    else:
        raise ValueError("Could not determine file path for JSON model, model "
                         "is missing required 'metadata' key.")


def _determine_path_from_metadata(parsed):
    metadata = parsed['metadata']
    if 'serviceAbbreviation' in metadata:
        service_name = metadata['serviceAbbreviation'].lower().replace(' ', '')
        if service_name.startswith('amazon'):
            service_name = service_name[6:]
        elif service_name.startswith('aws'):
            service_name = service_name[3:]
    else:
        service_name = metadata['endpointPrefix']
        # There's one single special case where we can't automatically
        # figure out, and that's elasticloadbalancing -> elb so we have
        # to hardcode this here.
        if service_name == 'elasticloadbalancing':
            service_name = 'elb'
    api_version = metadata['apiVersion']
    return os.path.join(
        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
        'botocore', 'data', 'aws', service_name, api_version + '.normal.json')


class TestDeterminePath(unittest.TestCase):

    def given_metadata(self, metadata):
        self.metadata = {'metadata': metadata}

    def assert_filename_is(self, filename):
        source_file = StringIO(json.dumps(self.metadata))
        actual = determine_json_data_path(source_file)
        self.assertTrue(actual.endswith(filename))

    def test_can_determine_path_from_metadata(self):
        self.given_metadata(
            {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo'})
        self.assert_filename_is('botocore/data/aws/foo/2015-01-01.normal.json')

        self.given_metadata(
            {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo',
             'serviceAbbreviation': 'Amazon Bar'})
        self.assert_filename_is('botocore/data/aws/bar/2015-01-01.normal.json')

        self.given_metadata(
            {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo',
             'serviceAbbreviation': 'AWS Baz'})
        self.assert_filename_is('botocore/data/aws/baz/2015-01-01.normal.json')

        self.given_metadata(
            {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo',
             'serviceAbbreviation': 'something else'})
        self.assert_filename_is(
            'botocore/data/aws/somethingelse/2015-01-01.normal.json')

        # The special casing of elasticloadbalancing -> elb.
        self.given_metadata(
            {'apiVersion': '2015-01-01',
             'endpointPrefix': 'elasticloadbalancing'}),
        self.assert_filename_is(
            'botocore/data/aws/elb/2015-01-01.normal.json')


def main():
    parser = optparse.OptionParser(usage=__doc__)
    parser.add_option(
        '-c', '--copy', action='store_true',
        default=False, help='Copy the file to the appropriate location. ')
    parser.add_option(
        '-t', '--test', action='store_true',
        default=False, help='Dev use only, run the unit tests.')
    opts, args = parser.parse_args()
    if opts.test:
        sys.argv[:] = [sys.argv[0]]
        unittest.main()
        return 0
    if len(args) != 1:
        sys.stderr.write("Must provide the filename of the JSON model "
                         "as an argument.\n")
        return 1
    source_filename = args[0]
    if not source_filename.endswith('.normal.json'):
        sys.stderr.write("Only the *.normal.json files are supported.\n")
        return 1
    f = open(source_filename)
    try:
        filename = determine_json_data_path(f)
        sys.stdout.write(filename)
        sys.stdout.write("\n")
    finally:
        f.close()
    if opts.copy:
        if not os.path.isdir(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        shutil.copy(source_filename, filename)
        sys.stdout.write("Copied: %s -> %s\n" % (source_filename, filename))
    return 0


if __name__ == '__main__':
    sys.exit(main())
