"""Generates horizontal cylinder for wind tunnel example."""

import argparse
import math
import sys
from typing import Tuple

try:
    import vtkmodules
except ImportError:
    print('Generates cylinder aligned with z axis.')
    print('Requires vtk module to run. ("pip install vtk")')
    sys.exit(-1)

import vtkmodules.vtkInteractionStyle
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersModeling import vtkOutlineFilter
from vtkmodules.vtkFiltersSources import (
    vtkCylinderSource,
    vtkCubeSource
)
from vtkmodules.vtkIOGeometry import (
    vtkOBJWriter,
    vtkSTLWriter
)
from vtkmodules.vtkIOXML import vtkXMLPolyDataWriter
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkInteractionWidgets import (
    vtkCameraOrientationWidget,
    vtkOrientationMarkerWidget
)
from vtkmodules.vtkRenderingAnnotation import vtkAxesActor
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkCamera,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)


def display_polydata(polydata, title='PolyData'):
    """"""
    colors = vtkNamedColors()

    mapper = vtkPolyDataMapper()
    mapper.SetInputData(polydata)

    actor = vtkActor()
    actor.SetMapper(mapper)
    actor.GetProperty().SetColor(0.8, 0.8, 0.8)

    # Add cube outline
    cube_source = vtkCubeSource()
    cube_source.SetBounds(zmin, zmax, zmin, zmax, zmin, zmax)
    outline = vtkOutlineFilter()
    outline.SetInputConnection(cube_source.GetOutputPort())
    outline_mapper = vtkPolyDataMapper()
    outline_mapper.SetInputConnection(outline.GetOutputPort())
    outline_actor = vtkActor()
    outline_actor.SetMapper(outline_mapper)
    outline_actor.GetProperty().SetColor(colors.GetColor3d('Gold'))

    ren = vtkRenderer()
    ren.SetBackground(0.2, 0.2, 0.2)
    ren.AddActor(actor)
    ren.AddActor(outline_actor)

    renWin = vtkRenderWindow()
    renWin.SetWindowName(title)
    renWin.AddRenderer(ren)
    renWin.SetSize(800, 800)

    iren = vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)

    style = vtkInteractorStyleTrackballCamera()
    iren.SetInteractorStyle(style)

    try:
        # vtk 9.1 has whizzy axes widget
        cam_orient_manipulator = vtkCameraOrientationWidget()
        cam_orient_manipulator.SetParentRenderer(ren)
        cam_orient_manipulator.On()
    except AttributeError:
        print('vtkCameraOrientationWidget not available')

    axes = vtkAxesActor()
    ren.AddActor(axes)

    camera = vtkCamera()
    camera.SetPosition(0.5, 0, 10)
    camera.SetFocalPoint(0.5, 0, 0)
    camera.SetViewUp(0, 1, 0)

    ren.SetActiveCamera(camera)
    ren.ResetCamera()
    ren.ResetCameraClippingRange()

    renWin.Render()
    iren.Start()


if __name__ == '__main__':
    description = 'Generate cylinder geometry (obj/stl/vtk).'
    parser = argparse.ArgumentParser(description=description)

    # Required arguments
    parser.add_argument('r', type=float, help='radius')
    parser.add_argument('y', type=float, help='y coordinate (height) of center')

    # Optional arguments
    parser.add_argument('-d', '--display', action='store_true', default=True,
                        help='Display geometry')
    parser.add_argument('-n', '--number-facets', type=int, default=40,
                        help='number of facets')
    parser.add_argument('-p', '--output_file_prefix', default='cylinder')
    parser.add_argument('-x', '--no_output', action='store_true',
                        help='do NOT write OBJ/STL/VTP files')
    parser.add_argument('-z', '--zmin_zmax', type=int, nargs=2, default=[-1, 1],
                        help="min and max z values for extruding 2D profile to 3D surface")

    args = parser.parse_args()
    print(args)

    zmin, zmax = args.zmin_zmax

    # Create source
    source = vtkCylinderSource()
    source.SetRadius(args.r)
    source.SetCenter(0.0, args.y, 0.0)
    source.SetHeight(zmax - zmin)
    source.SetResolution(args.number_facets)

    # Transform
    xform = vtkTransform()
    xform.Translate(0.0, 0.0, zmin)
    xform.RotateX(90.0)
    # print(xform)

    filter = vtkTransformPolyDataFilter()
    filter.SetInputConnection(source.GetOutputPort())
    filter.SetTransform(xform)
    filter.Update()
    vtk_data = filter.GetOutput()

    # Write files
    if not args.no_output:
        # Write STL file
        filename = '{}.stl'.format(args.output_file_prefix)
        stl_writer = vtkSTLWriter()
        stl_writer.SetFileName(filename)
        stl_writer.SetInputData(vtk_data)
        stl_writer.Write()
        print('Wrote {}'.format(filename))

        # Write OBJ file
        filename = '{}.obj'.format(args.output_file_prefix)
        obj_writer = vtkOBJWriter()
        obj_writer.SetFileName(filename)
        obj_writer.SetInputData(vtk_data)
        obj_writer.Write()
        print('Wrote {}'.format(filename))

        # Write VTK (.vtp) file
        filename = '{}.vtp'.format(args.output_file_prefix)
        vtp_writer = vtkXMLPolyDataWriter()
        vtp_writer.SetFileName(filename)
        vtp_writer.SetInputData(vtk_data)
        vtp_writer.SetDataModeToAscii()
        vtp_writer.Write()
        print('Wrote {}'.format(filename))

    if args.display:
        # print(vtk_data))
        display_polydata(vtk_data, 'Wire')
