#!/usr/bin/env python
#
# Copyright (c) 2015,2017,2020 - Adjacent Link LLC, Bridgewater,
# New Jersey
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in
#   the documentation and/or other materials provided with the
#   distribution.
# * Neither the name of Adjacent Link LLC nor the names of its
#   contributors may be used to endorse or promote products derived
#   from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from __future__ import absolute_import, division, print_function

from lxml import etree
from argparse import ArgumentParser
from mayavi import mlab
import numpy as np
import matplotlib.pyplot as plt
import os

NUM_ELEVATION=181
NUM_BEARING=360

def loadProfile(filename,fill):
    profile = np.empty((NUM_ELEVATION,NUM_BEARING))
    profile[:] = fill
    gain_max = fill
    abs_gain_max = None
    doc = etree.parse(filename)

    root = doc.getroot()

    for elevation in root.xpath('/antennaprofile//elevation'):
        elevation_min = int(elevation.get('min'))
        elevation_max = int(elevation.get('max'))

        for bearing in elevation.xpath('bearing'):
            bearing_min = int(bearing.get('min'))
            bearing_max = int(bearing.get('max'))

            for gain in bearing.xpath('gain'):
                antenna_gain = int(round(float(gain.get('value'))))
                gain_max = max(antenna_gain,gain_max)
                if abs_gain_max == None:
                    abs_gain_max = np.abs(antenna_gain)
                else:
                    abs_gain_max = max(np.abs(antenna_gain),abs_gain_max)

                for elevation_degrees in range(elevation_min,elevation_max+1):
                    for bearing_degrees in range(bearing_min,bearing_max+1):
                        # convert elevation to index: [0,180]
                        profile[elevation_degrees + 90, bearing_degrees] = antenna_gain

    return (profile,gain_max,abs_gain_max)


def render(profile,gain_max,abs_gain_max,use_line):
    normalized_gain = abs_gain_max - gain_max
    x = []
    y = []
    z = []
    s = []
    for elevation in range(0,NUM_ELEVATION):
        for bearing in range(0,NUM_BEARING):
            rho =  profile[elevation,bearing] + normalized_gain

            if rho >= 0:
                theta =  np.radians(bearing)
                phi = np.radians(elevation)

                x.append(rho * np.sin(phi) * np.cos(theta))
                y.append(rho * np.sin(phi) * np.sin(theta))
                z.append(rho * np.cos(phi))
                s.append(profile[elevation,bearing])

    if len(x):
        if use_line:
            mlab.plot3d(x,y,z,color=(0,1,0),tube_radius=.5)
        else:
            pts = mlab.points3d(x,y,z,s,mode='point')
            pts.glyph.glyph.color_mode = 'color_by_scalar'
            pts.glyph.color_mode = 'color_by_scalar'
            pts.glyph.glyph.scale_mode = 'data_scaling_off'
            lut = pts.module_manager.scalar_lut_manager.lut.table.to_array()
            cmap = plt.cm.coolwarm
            pts.module_manager.scalar_lut_manager.lut.table = lut
            pts.module_manager.scalar_lut_manager.show_scalar_bar = True
            pts.module_manager.scalar_lut_manager.show_legend = True
            pts.module_manager.scalar_lut_manager.data_range = np.array([min(s), max(s)])
            pts.module_manager.scalar_lut_manager.data_name=''


description='Simple rendering utility for EMANE antenna patterns.'

argument_parser = ArgumentParser(description=description)

argument_parser.add_argument('antenna-profile-file',
                             type=str,
                             default='',
                             help='antenna profile XML file')

argument_parser.add_argument('--null',
                             type=float,
                             metavar='DECIBELS',
                             default=-200,
                             help='null value representing full loss in dB [default: %(default)s]')

argument_parser.add_argument("-l",
                             "--line",
                             action='store_true',
                             default=False,
                             help="render as a line [default: %(default)s]")

argument_parser.add_argument("-t",
                             "--show-title",
                             action='store_true',
                             default=False,
                             help="show title [default: %(default)s]")

argument_parser.add_argument("-o",
                             "--show-origin",
                             action='store_true',
                             default=False,
                             help="show origin [default: %(default)s]")

ns = argument_parser.parse_args()

args = vars(ns)

mlab.figure('EMANE Antenna Simple Viewer')

if args['show_origin']:
    mlab.points3d([0],[0],[0],color=(0,0,1),mode='sphere')

# load the antenna profile
profile,gain_max,abs_gain_max = loadProfile(args['antenna-profile-file'],
                                            args['null'])

render(profile,gain_max,abs_gain_max,args['line'])

if args['show_title']:
    mlab.title(os.path.basename(args['antenna-profile-file']),size=.5)

mlab.show()
