#!/bin/env python3
#
# Copyright 2014 Peter Griess
#
# 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.
#
# Work with GeoJSON files.

import argparse
import functools
import json
import logging
import os.path
import shapely.geometry
import shapely.geometry.collection
import subprocess
import sys
import tempfile

# Add src/ directory to PYTHONPATH so that this can be run without the operator
# having to configure that manually
sys.path += [os.path.join(os.path.dirname(sys.argv[0]), '..', 'src')]

import crimedb.cli
import crimedb.debug

def view_cmd(args):
    gjos = []
    for fp in args.files:
        if fp == '-':
            gjos += [json.load(sys.stdin)]
        else:
            with open(fp, 'rt', encoding='utf-8') as gjf:
                gjos += [json.load(gjf)]

    crimedb.debug.view_geojson_objects(*gjos)


def transform_cmd(args):
    def diff_transform(path, so):
        with open(path, 'rt', encoding='utf-8') as f:
            return so.difference(shapely.geometry.shape(json.load(f)))

    def append_transform(path, so):
        with open(path, 'rt', encoding='utf-8') as f:
            return shapely.geometry.collection.GeometryCollection(geoms=[
                so,
                shapely.geometry.shape(json.load(f))])

    with open(args.gj_path, 'rt', encoding='utf-8') as gjf:
        so = shapely.geometry.shape(json.load(gjf))

    i = 0
    transformations = []
    while i < len(args.commands):
        if args.commands[i] == '-diff':
            i += 1
            transformations += [functools.partial(diff_transform, args.commands[i])]
        elif args.commands[i] == '-append':
            i += 1
            transformations += [functools.partial(append_transform, args.commands[i])]
        else:
            raise Exception('unknown command: {}'.format(args.commands[i]))

        i += 1

    so = functools.reduce(lambda gjo, f: f(gjo), transformations, so)
    json.dump(
            shapely.geometry.mapping(so),
            sys.stdout,
            ensure_ascii=False,
            indent=4,
            sort_keys=True)


def create_point_cmd(args):
    so = shapely.geometry.Point(args.lon, args.lat)
    json.dump(
            shapely.geometry.mapping(so),
            sys.stdout,
            ensure_ascii=False,
            indent=4,
            sort_keys=True)


ap = argparse.ArgumentParser(
        description='Work with CrimeDB region files.',
        parents=[crimedb.cli.logging_argument_parser])
sp = ap.add_subparsers()

view_parser = sp.add_parser(
        'view',
        help='view GeoJSON objects',
        description='View GeoJSON objects in a webpage')
view_parser.add_argument(
        'files', metavar='<file>', default=['-'], nargs='*',
        help='path to the GeoJSON file to view; use "-" for stdin (default: '
             '%(default)s)')
view_parser.set_defaults(func=view_cmd)

transform_parser = sp.add_parser(
    'transform',
    help='transform GeoJSON objects',
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description='''
Transform GeoJSON objects. Transformations are applied in the order specified.
Due to the way argparse handles arguments, you will have to provide a '--'
positional argument immediately after the <path> argument in order to get the
behavior desired.

Possible transformations and their arguments are:

  -diff <path>          Subtract the GeoJSON object from the given path from the
                        current object
  -append <path>        Append the GeoJSON oject from the given path to the current
                        objet, producing a GeometryCollection (e.g. to plot multiple
                        points, polygons, etc)
''')
transform_parser.add_argument(
    'gj_path', metavar='<path>',
    help='path to base GeoJSON file')
transform_parser.add_argument(
    'commands', nargs='*',
    help='set of transformations to apply')
transform_parser.set_defaults(func=transform_cmd)

create_parser = sp.add_parser(
    'create',
    help='create a GeoJSON object',
    description='Create a GeoJSON object')
csp = create_parser.add_subparsers()

create_point_parser = csp.add_parser(
    'point',
    help='create a Point',
    description='create a Point')
create_point_parser.add_argument(
    'lon', type=float,
    metavar='<longitude>')
create_point_parser.add_argument(
    'lat', type=float,
    metavar='<latitude>')
create_point_parser.set_defaults(func=create_point_cmd)

args = ap.parse_args()
crimedb.cli.process_logging_args(args)

if 'func' not in args:
    ap.error('command name required')

args.func(args)
