"""
@package animation.nviztask

@brief Conversion from workspace file to m.nviz.image command

Classes:
 - nviztask::NvizTask

(C) 2013 by the GRASS Development Team

This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.

@author Anna Petrasova <kratochanna gmail.com>
"""

import os
try:
    import xml.etree.ElementTree as etree
except ImportError:
    import elementtree.ElementTree as etree  # Python <= 2.4

from core.workspace import ProcessWorkspaceFile
from core.gcmd import RunCommand, GException
from core.utils import GetLayerNameFromCmd, _
from grass.script import task as gtask
from core.settings import UserSettings


class NvizTask:

    def __init__(self):
        self.task = None
        self.filename = None
        self.region = {}

    def Load(self, filename):
        self.task = gtask.grassTask("m.nviz.image")
        self.filename = filename
        try:
            gxwXml = ProcessWorkspaceFile(etree.parse(self.filename))
        except Exception:
            raise GException(_("Reading workspace file <%s> failed.\n"
                               "Invalid file, unable to parse XML document.") % filename)
        # for display in gxwXml.displays:
            # pprint(display)
        # for layer in gxwXml.layers:
            # pprint(layer)
        # pprint(gxwXml.nviz_state)

        if not gxwXml.nviz_state:
            raise GException(
                _("No 3d view information in workspace file <%s>.") %
                self.filename)

        self._getExtent(gxwXml)
        self._processState(gxwXml.nviz_state)
        self._processLayers(gxwXml.layers)

    def _getExtent(self, root):
        for display in root.displays:
            if display['viewMode'] == '3d':
                self.region['w'], self.region['s'],\
                    self.region['e'], self.region['n'],\
                    self.region['b'], self.region['t'] = display['extent']
                self.region['tbres'] = display['tbres']

    def _processLayers(self, layers):
        for layer in layers:
            if not layer['checked']:
                continue

            if not layer['nviz']:
                continue
            layerName, found = GetLayerNameFromCmd(
                layer['cmd'], fullyQualified=False, param='map')
            if not found:
                continue

            if 'surface' in layer['nviz']:
                self._processSurface(
                    layer['nviz']['surface'],
                    mapName=layerName)
            if 'volume' in layer['nviz']:
                self._processVolume(layer['nviz']['volume'], mapName=layerName)
            if 'vector' in layer['nviz']:
                if 'points' in layer['nviz']['vector']:
                    self._processPoints(
                        layer['nviz']['vector']['points'],
                        mapName=layerName)

    def _processSurface(self, surface, mapName):
        self._setMultiTaskParam('elevation_map', mapName)

        # attributes like color, shine, transparency
        attributes = ('color', 'shine', 'transp')  # mask missing
        parameters = (('color_map', 'color'),
                      ('shininess_map', 'shininess_value'),
                      ('transparency_map', 'transparency_value'))
        for attr, params in zip(attributes, parameters):
            mapname = None
            const = None
            if attr in surface['attribute']:
                if surface['attribute'][attr]['map']:
                    mapname = surface['attribute'][attr]['value']
                else:
                    const = surface['attribute'][attr]['value']
            else:
                if attr == 'transp':
                    const = 0
                elif attr == 'color':
                    mapname = mapName

            if mapname:
                self._setMultiTaskParam(params[0], mapname)
            else:
                self._setMultiTaskParam(params[1], const)

        # draw mode
        for mode in ('mode', 'shading', 'style'):
            value = surface['draw']['mode']['desc'][mode]
            self._setMultiTaskParam(mode, value)
        # wire color
        value = surface['draw']['wire-color']['value']
        self._setMultiTaskParam('wire_color', value)
        # resolution
        for mode1, mode2 in zip(
                ('coarse', 'fine'),
                ('resolution_coarse', 'resolution_fine')):
            value = surface['draw']['resolution'][mode1]
            self._setMultiTaskParam(mode2, value)

        # position
        pos = []
        for coor in ('x', 'y', 'z'):
            pos.append(str(surface['position'][coor]))
        value = ','.join(pos)
        self._setMultiTaskParam('surface_position', value)

    def _processPoints(self, points, mapName):
        for attrib in ('color', 'size', 'width'):
            if attrib in points:
                val = points[attrib]['value']
                self._setMultiTaskParam('vpoint_' + attrib, str(val))
        if 'height' in points:
            height = points['height']['value']
            self._setMultiTaskParam(
                'vpoint_position',
                '0,0,{h}'.format(
                    h=height))
        if 'marker' in points:
            marker = list(
                UserSettings.Get(
                    group='nviz', key='vector', subkey=[
                        'points', 'marker'], settings_type='internal'))[
                points['marker']['value']]
            self._setMultiTaskParam('vpoint_marker', marker)
        if 'mode' in points:
            if points['mode']['type'] == '3d':
                self._setMultiTaskParam('vpoint_mode', '3D')
            else:
                self._setMultiTaskParam('vpoint_mode', 'surface')

    def _processVolume(self, volume, mapName):
        self._setMultiTaskParam('volume', mapName)

        if volume['draw']['box']['enabled']:
            self.task.set_flag('b', True)

        # isosurfaces
        isosurfaces = volume['isosurface']
        if isosurfaces:
            res_value = volume['draw']['resolution']['isosurface']['value']
            self._setMultiTaskParam('volume_resolution', res_value)
            for isosurface in isosurfaces:
                attributes = ('topo', 'color', 'shine', 'transp')
                parameters = (
                    (None, 'isosurf_level'),
                    ('isosurf_color_map', 'isosurf_color_value'),
                    ('isosurf_shininess_map', 'isosurf_shininess_value'),
                    ('isosurf_transparency_map', 'isosurf_transparency_value'))
                for attr, params in zip(attributes, parameters):
                    mapname = None
                    const = None
                    if attr in isosurface:
                        if isosurface[attr]['map']:
                            mapname = isosurface[attr]['value']
                        else:
                            const = float(isosurface[attr]['value'])
                    else:
                        if attr == 'transp':
                            const = 0
                        elif attr == 'color':
                            mapname = mapName

                    if mapname:
                        self._setMultiTaskParam(params[0], mapname)
                    else:
                        if attr == 'topo':
                            # TODO: we just assume it's the first volume, what
                            # to do else?
                            self._setMultiTaskParam(
                                params[1], '1:' + str(const))
                        else:
                            self._setMultiTaskParam(params[1], const)
                if isosurface['inout']['value']:
                    self.task.set_flag('n', True)
        # slices
        slices = volume['slice']
        if slices:
            res_value = volume['draw']['resolution']['slice']['value']
            self._setMultiTaskParam('volume_resolution', res_value)
            for slice_ in slices:
                self._setMultiTaskParam(
                    'slice_transparency', slice_['transp']['value'])
                axis = slice_['position']['axis']
                self._setMultiTaskParam('slice', '1:' + 'xyz'[axis])
                pos = slice_['position']
                coords = pos['x1'], pos['x2'], pos[
                    'y1'], pos['y2'], pos['z1'], pos['z2']
                self._setMultiTaskParam('slice_position', ','.join(
                    [str(c) for c in coords]))

        # position
        pos = []
        for coor in ('x', 'y', 'z'):
            pos.append(str(volume['position'][coor]))
            value = ','.join(pos)

        self._setMultiTaskParam('volume_position', value)

    def _processState(self, state):
        color = state['view']['background']['color']
        self.task.set_param('bgcolor', self._join(color, delim=':'))
        self.task.set_param(
            'position', self._join(
                (state['view']['position']['x'],
                 state['view']['position']['y'])))
        self.task.set_param('height', state['iview']['height']['value'])
        self.task.set_param('perspective', state['view']['persp']['value'])
        self.task.set_param('twist', state['view']['twist']['value'])
        # TODO: fix zexag
        self.task.set_param('zexag', state['view']['z-exag']['value'])
        self.task.set_param(
            'focus', self._join(
                (state['iview']['focus']['x'],
                 state['iview']['focus']['y'],
                 state['iview']['focus']['z'])))
        self.task.set_param(
            'light_position', self._join(
                (state['light']['position']['x'],
                 state['light']['position']['y'],
                 state['light']['position']['z'] / 100.)))
        color = state['light']['color'][:3]
        self.task.set_param('light_color', self._join(color, delim=':'))
        self.task.set_param('light_brightness', int(state['light']['bright']))
        self.task.set_param('light_ambient', state['light']['ambient'])

    def _setMultiTaskParam(self, param, value):
        last = self.task.get_param(param)['value']
        self.task.set_param(param, self._join((last, value)))

    def _join(self, toJoin, delim=','):
        toJoin = filter(self._ignore, toJoin)
        return delim.join(map(str, toJoin))

    def _ignore(self, value):
        if value == '' or value is None:
            return False
        else:
            return True

    def ListMapParameters(self):
        # params = self.task.get_list_params()
        # parameter with 'map' name
        # params = filter(lambda x: 'map' in x, params)
        return ('elevation_map', 'color_map', 'vline', 'vpoint', 'volume')

    def GetCommandSeries(self, layerList, paramName):
        commands = []
        if not self.task:
            return commands

        if len(layerList) > 1:
            raise GException(_("Please add only one layer in the list."))
            return
        layer = layerList[0]
        if hasattr(layer, 'maps'):
            series = layer.maps
        else:
            raise GException(
                _("No map series nor space-time dataset is added."))

        for value in series:
            self.task.set_param(paramName, value)
            # FIXME: we assume we want always default color map
            if paramName == 'elevation_map':
                self.task.set_param('color_map', '')
            if paramName == 'volume':
                self.task.set_param('isosurf_color_map', '')
            self.task.set_flag('overwrite', True)
            self.task.set_param('output', 'tobechanged')
            cmd = self.task.get_cmd(
                ignoreErrors=False,
                ignoreRequired=False,
                ignoreDefault=True)
            commands.append(cmd)

        return commands

    def GetCommand(self):
        if not self.task:
            return None
        self.task.set_flag('overwrite', True)
        self.task.set_param('output', 'tobechanged')
        cmd = self.task.get_cmd(
            ignoreErrors=False,
            ignoreRequired=False,
            ignoreDefault=True)
        return gtask.cmdlist_to_tuple(cmd)

    def GetRegion(self):
        return self.region


def test():
    nviz = NvizTask('/home/anna/testy/nviz/t12.gxw')
    # nviz = NvizState('/home/anna/testy/nviz/t3.gxw')

    # cmd = nviz.GetCommand()
    cmds = nviz.GetCommandSeries(['aspect', 'elevation'], 'color_map')
    for cmd in cmds:
        print cmd
        returncode, message = RunCommand(
            getErrorMsg=True, prog=cmd[0], **cmd[1])
        print returncode, message


if __name__ == '__main__':

    test()
