import os
import unittest
import vtk, qt, ctk, slicer
from slicer.ScriptedLoadableModule import *
from slicer.util import TESTING_DATA_URL
#
# RSNAQuantTutorial
#

class RSNAQuantTutorial(ScriptedLoadableModule):
  """Uses ScriptedLoadableModule base class, available at:
  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  """
  def __init__(self, parent):
    ScriptedLoadableModule.__init__(self, parent)
    parent.title = "RSNAQuantTutorial" # TODO make this more human readable by adding spaces
    parent.categories = ["Testing.TestCases"]
    parent.dependencies = []
    parent.contributors = ["Steve Pieper (Isomics)"] # replace with "Firstname Lastname (Org)"
    parent.helpText = """
    This module was developed as a self test to perform the operations needed for the RSNA 2012 Quantitative Imaging Tutorial
    """
    parent.acknowledgementText = """
    This file was originally developed by Steve Pieper, Isomics, Inc.  and was partially funded by NIH grant 3P41RR013218-12S1.
""" # replace with organization, grant and thanks.

#
# qRSNAQuantTutorialWidget
#

class RSNAQuantTutorialWidget(ScriptedLoadableModuleWidget):
  """Uses ScriptedLoadableModuleWidget base class, available at:
  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  """

  def setup(self):
    ScriptedLoadableModuleWidget.setup(self)
    # Instantiate and connect widgets ...

    # Collapsible button
    testsCollapsibleButton = ctk.ctkCollapsibleButton()
    testsCollapsibleButton.text = "Tests"
    self.layout.addWidget(testsCollapsibleButton)

    # Layout within the collapsible button
    formLayout = qt.QFormLayout(testsCollapsibleButton)

    # test buttons
    tests = ( ("Part 1 : Ruler", self.onPart1Ruler),("Part 2: ChangeTracker", self.onPart2ChangeTracker),("Part 3 : PETCT", self.onPart3PETCT) )
    for text,slot in tests:
      testButton = qt.QPushButton(text)
      testButton.toolTip = "Run the test."
      formLayout.addWidget(testButton)
      testButton.connect('clicked(bool)', slot)

    # A collapsible button to hide screen shot options
    screenShotsCollapsibleButton = ctk.ctkCollapsibleButton()
    screenShotsCollapsibleButton.text = "Screen shot options"
    self.layout.addWidget(screenShotsCollapsibleButton)

    # layout within the collapsible button
    screenShotsFormLayout = qt.QFormLayout(screenShotsCollapsibleButton)

    #
    # check box to trigger taking screen shots for later use in tutorials
    #
    self.enableScreenshotsFlagCheckBox = qt.QCheckBox()
    self.enableScreenshotsFlagCheckBox.checked = 0
    self.enableScreenshotsFlagCheckBox.setToolTip("If checked, take screen shots for tutorials. Use Save Data to write them to disk.")
    screenShotsFormLayout.addRow("Enable Screenshots", self.enableScreenshotsFlagCheckBox)

    #
    # scale factor for screen shots
    #
    self.screenshotScaleFactorSliderWidget = ctk.ctkSliderWidget()
    self.screenshotScaleFactorSliderWidget.singleStep = 1.0
    self.screenshotScaleFactorSliderWidget.minimum = 1.0
    self.screenshotScaleFactorSliderWidget.maximum = 50.0
    self.screenshotScaleFactorSliderWidget.value = 1.0
    self.screenshotScaleFactorSliderWidget.setToolTip("Set scale factor for the screen shots.")
    screenShotsFormLayout.addRow("Screenshot scale factor", self.screenshotScaleFactorSliderWidget)

    # Add vertical spacer
    self.layout.addStretch(1)

  def onPart1Ruler(self):
    enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked
    screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value)

    tester = RSNAQuantTutorialTest()
    tester.setUp()
    tester.test_Part1Ruler(enableScreenshotsFlag,screenshotScaleFactor)

  def onPart2ChangeTracker(self):
    enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked
    screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value)

    tester = RSNAQuantTutorialTest()
    tester.setUp()
    tester.test_Part2ChangeTracker(enableScreenshotsFlag,screenshotScaleFactor)

  def onPart3PETCT(self):
    enableScreenshotsFlag = self.enableScreenshotsFlagCheckBox.checked
    screenshotScaleFactor = int(self.screenshotScaleFactorSliderWidget.value)

    tester = RSNAQuantTutorialTest()
    tester.setUp()
    tester.test_Part3PETCT(enableScreenshotsFlag,screenshotScaleFactor)

#
# RSNAQuantTutorialLogic
#

class RSNAQuantTutorialLogic(ScriptedLoadableModuleLogic):
  """This class should implement all the actual
  computation done by your module.  The interface
  should be such that other python code can import
  this class and make use of the functionality without
  requiring an instance of the Widget
  """
  pass


class RSNAQuantTutorialTest(ScriptedLoadableModuleTest):
  """
  This is the test case for your scripted module.
  Uses ScriptedLoadableModuleTest base class, available at:
  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  """

  def setUp(self):
    """ Do whatever is needed to reset the state - typically a scene clear will be enough.
    """
    self.delayDisplay("Closing the scene")
    layoutManager = slicer.app.layoutManager()
    layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
    slicer.mrmlScene.Clear(0)

  def runTest(self):
    """Run as few or as many tests as needed here.
    """
    self.setUp()
    self.test_Part1Ruler()
    self.setUp()
    self.test_Part2ChangeTracker()
    self.setUp()
    self.test_Part3PETCT()

  def test_Part1Ruler(self,enableScreenshotsFlag=0,screenshotScaleFactor=1):
    """ Test using rulers
    """
    logic = RSNAQuantTutorialLogic()
    logic.enableScreenshots = enableScreenshotsFlag
    logic.screenshotScaleFactor = screenshotScaleFactor

    self.delayDisplay("Starting the test")

    #
    # first, get some data
    #
    import SampleData
    tumor = SampleData.downloadSample('MRBrainTumor1')

    try:
      # four up view
      layoutManager = slicer.app.layoutManager()
      layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView)

      # annotations module
      m = slicer.util.mainWindow()
      m.moduleSelector().selectModule('Annotations')

      # add ruler 1
      rulerNode1 = slicer.vtkMRMLAnnotationRulerNode()
      rulerNode1.SetName("d1")
      rulerNode1.SetPosition1(-7.59519,43.544,28.6)
      rulerNode1.SetPosition2(-5.56987,14.177,28.6)
      rulerNode1.Initialize(slicer.mrmlScene)
      self.delayDisplay("Ruler 1")

      # add ruler 2
      rulerNode2 = slicer.vtkMRMLAnnotationRulerNode()
      rulerNode2.SetName("d2")
      rulerNode2.SetPosition1(-3.54455,27.656,13.1646)
      rulerNode2.SetPosition2(-2.5319,27.656,47.5949)
      rulerNode2.Initialize(slicer.mrmlScene)
      self.delayDisplay("Ruler 2")

      # scroll
      annotLogic = slicer.modules.annotations.logic()
      annotLogic.JumpSlicesToAnnotationCoordinate(rulerNode1.GetID())

      # show slices
      redWidget = layoutManager.sliceWidget('Red')
      redWidget.sliceController().setSliceLink(True)
      redWidget.sliceController().setSliceVisible(True);


      logic.takeScreenshot('Ruler','Ruler used to measure tumor diameter',-1)

      self.delayDisplay('Test passed!')
    except Exception as e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e))


  def test_Part3PETCT(self,enableScreenshotsFlag=0,screenshotScaleFactor=1):
    """ Test using the PETCT module
    """
    logic = RSNAQuantTutorialLogic()
    logic.enableScreenshots = enableScreenshotsFlag
    logic.screenshotScaleFactor = screenshotScaleFactor

    self.delayDisplay("Starting the test")

    #
    # first, get some data
    #
    import SampleData
    extractPath = SampleData.downloadFromURL(
      fileNames='dataset3_PETCT.zip',
      uris=TESTING_DATA_URL + 'SHA256/11e81af3462076f4ca371b632e03ed435240042915c2daf07f80059b3f78f88d',
      checksums='SHA256:11e81af3462076f4ca371b632e03ed435240042915c2daf07f80059b3f78f88d')[0]

    self.delayDisplay("Loading PET_CT_pre-treatment.mrb")
    preTreatmentPath = extractPath + '/PET_CT_pre-treatment.mrb'
    slicer.util.loadScene(preTreatmentPath)
    logic.takeScreenshot('PETCT-LoadedPre','Loaded pre-treatement scene',-1)

    try:
      mainWindow = slicer.util.mainWindow()
      layoutManager = slicer.app.layoutManager()
      threeDView = layoutManager.threeDWidget(0).threeDView()
      redWidget = layoutManager.sliceWidget('Red')
      redController = redWidget.sliceController()
      greenWidget = layoutManager.sliceWidget('Green')
      greenController = greenWidget.sliceController()
      yellowWidget = layoutManager.sliceWidget('Yellow')
      yellowController = yellowWidget.sliceController()
      viewNode = threeDView.mrmlViewNode()
      cameras = slicer.util.getNodes('vtkMRMLCameraNode*')
      for cameraNode in cameras.values():
        if cameraNode.GetActiveTag() == viewNode.GetID():
          break

      threeDView.resetFocalPoint()
      slicer.util.clickAndDrag(threeDView,button='Right')
      redWidget.sliceController().setSliceVisible(True)
      yellowWidget.sliceController().setSliceVisible(True)
      logic.takeScreenshot('PETCT-ConfigureView','Configure View',-1)

      mainWindow.moduleSelector().selectModule('Volumes')
      compositNode = redWidget.mrmlSliceCompositeNode()
      compositNode.SetForegroundOpacity(0.2)
      logic.takeScreenshot('PETCT-ShowVolumes','Show Volumes with lesion',-1)

      compositNode.SetForegroundOpacity(0.5)
      logic.takeScreenshot('PETCT-CTOpacity','CT1 volume opacity to 0.5',-1)

      yellowWidget.sliceController().setSliceVisible(False)
      greenWidget.sliceController().setSliceVisible(True)
      logic.takeScreenshot('PETCT-ShowSlices','Show axial and sagittal slices',-1)

      self.delayDisplay('SUV Computation')
      if not hasattr(slicer.modules, 'petstandarduptakevaluecomputation'):
        self.delayDisplay("PET SUV Computation not available, skipping the test.")
        return

      slicer.util.selectModule('PETStandardUptakeValueComputation')

      parameters = {
          "PETDICOMPath": extractPath + '/' + 'PET1',
          "PETVolume": slicer.util.getNode('PET1'),
          "VOIVolume": slicer.util.getNode('PET1-label'),
          }

      suvComputation = slicer.modules.petstandarduptakevaluecomputation
      self.CLINode1 = None
      self.CLINode1 = slicer.cli.runSync(suvComputation, self.CLINode1, parameters, delete_temporary_files=False)

      # close the scene
      slicer.mrmlScene.Clear(0)

      self.delayDisplay("Loading PET_CT_post-treatment.mrb")
      postTreatmentPath = extractPath + '/PET_CT_post-treatment.mrb'
      slicer.util.loadScene(postTreatmentPath)
      logic.takeScreenshot('PETCT-LoadedPost','Loaded post-treatement scene',-1)

      compositNode.SetForegroundOpacity(0.5)
      logic.takeScreenshot('PETCT-CT2Opacity','CT2 volume opacity to 0.5',-1)

      redController.setSliceOffsetValue(-165.01)
      logic.takeScreenshot('PETCT-LarynxUptake','Mild uptake in the larynx and pharynx',-1)

      redController.setSliceOffsetValue(-106.15)
      logic.takeScreenshot('PETCT-TumorUptake','No uptake in the area of the primary tumor',-1)

      self.delayDisplay('Test passed!')
    except Exception as e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e))


  def test_Part2ChangeTracker(self,enableScreenshotsFlag=0,screenshotScaleFactor=1):
    """ Test the ChangeTracker module
    """

    logic = RSNAQuantTutorialLogic()
    logic.enableScreenshots = enableScreenshotsFlag
    logic.screenshotScaleFactor = screenshotScaleFactor

    self.delayDisplay("Starting the test")

    if not hasattr(slicer.modules, 'changetracker'):
      self.delayDisplay("ChangeTracker not available, skipping the test.")
      return

    #
    # first, get some data
    #
    import SampleData
    SampleData.downloadFromURL(
      fileNames='ChangeTrackerScene.mrb',
      loadFiles=True,
      uris=TESTING_DATA_URL + 'SHA256/64734cbbf8ebafe4a52f551d1510a8f6f3d0625eb5b6c1e328be117c48e2c653',
      checksums='SHA256:64734cbbf8ebafe4a52f551d1510a8f6f3d0625eb5b6c1e328be117c48e2c653')
    logic.takeScreenshot('ChangeTracker-Loaded','Finished with download and loading',-1)

    try:
      mainWindow = slicer.util.mainWindow()
      layoutManager = slicer.app.layoutManager()
      threeDView = layoutManager.threeDWidget(0).threeDView()
      redWidget = layoutManager.sliceWidget('Red')
      redController = redWidget.sliceController()
      viewNode = threeDView.mrmlViewNode()
      cameras = slicer.util.getNodes('vtkMRMLCameraNode*')
      for cameraNode in cameras.values():
        if cameraNode.GetActiveTag() == viewNode.GetID():
          break

      self.delayDisplay('Configure Module')
      mainWindow.moduleSelector().selectModule('ChangeTracker')
      logic.takeScreenshot('ChangeTracker-ModuleGUI','Select the ChangeTracker module',-1)

      changeTracker = slicer.modules.changetracker.widgetRepresentation().self()

      baselineNode = slicer.util.getNode('2006-spgr1')
      followupNode = slicer.util.getNode('2007-spgr1')
      changeTracker.selectScansStep._ChangeTrackerSelectScansStep__baselineVolumeSelector.setCurrentNode(baselineNode)
      changeTracker.selectScansStep._ChangeTrackerSelectScansStep__followupVolumeSelector.setCurrentNode(followupNode)
      logic.takeScreenshot('ChangeTracker-SetInputs','Select input scans',-1)

      changeTracker.workflow.goForward()
      logic.takeScreenshot('ChangeTracker-GoForward','Go Forward',-1)

      slicer.util.clickAndDrag(redWidget,button='Right')
      logic.takeScreenshot('ChangeTracker-Zoom','Inspect - zoom',-1)

      slicer.util.clickAndDrag(redWidget,button='Middle')
      logic.takeScreenshot('ChangeTracker-Pan','Inspect - pan',-1)

      for offset in range(-20,20,2):
        redController.setSliceOffsetValue(offset)
      logic.takeScreenshot('ChangeTracker-Scroll','Inspect - scroll',-1)

      self.delayDisplay('Set ROI')
      roi = changeTracker.defineROIStep._ChangeTrackerDefineROIStep__roi
      roi.SetXYZ(-2.81037, 28.7629, 28.4536)
      logic.takeScreenshot('ChangeTracker-SetROICenter','Center VOI',-1)
      roi.SetRadiusXYZ(22.6467, 22.6804, 22.9897)
      logic.takeScreenshot('ChangeTracker-SetROIExtent','Resize the VOI',-1)

      layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalWidescreenView)
      logic.takeScreenshot('ChangeTracker-ConventionalWidescreen','Select the viewing mode Conventional Widescreen',-1)

      slicer.util.clickAndDrag(redWidget,button='Right')
      logic.takeScreenshot('ChangeTracker-ZoomVOI','Zoom',-1)

      layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView)
      logic.takeScreenshot('ChangeTracker-FourUpLayout','Go back to Four-Up layout',-1)

      changeTracker.workflow.goForward()
      logic.takeScreenshot('ChangeTracker-GoForward','Go Forward',-1)

      changeTracker.segmentROIStep._ChangeTrackerSegmentROIStep__threshRange.minimumValue = 120
      logic.takeScreenshot('ChangeTracker-Threshold','Set threshold',-1)

      changeTracker.workflow.goForward()
      logic.takeScreenshot('ChangeTracker-GoForward','Go Forward',-1)

      checkList = changeTracker.analyzeROIStep._ChangeTrackerAnalyzeROIStep__metricCheckboxList
      index = list(checkList.values()).index('IntensityDifferenceMetric')
      list(checkList.keys())[index].checked = True
      logic.takeScreenshot('ChangeTracker-PickMetric','Select the ROI analysis method',-1)

      changeTracker.workflow.goForward()
      logic.takeScreenshot('ChangeTracker-GoForward','Go Forward',-1)

      self.delayDisplay('Look!')
      redWidget.sliceController().setSliceVisible(True);


      self.delayDisplay('Crosshairs')
      compareWidget = layoutManager.sliceWidget('Compare1')
      style = compareWidget.interactorStyle()
      interactor = style.GetInteractor()
      for step in range(100):
        interactor.SetEventPosition(10,step)
        style.OnMouseMove()

      self.delayDisplay('Zoom')
      slicer.util.clickAndDrag(compareWidget,button='Right')

      self.delayDisplay('Pan')
      slicer.util.clickAndDrag(compareWidget,button='Middle')

      self.delayDisplay('Inspect - scroll')
      compareController = redWidget.sliceController()
      for offset in range(10,30,2):
        compareController.setSliceOffsetValue(offset)

      logic.takeScreenshot('ChangeTracker-InspectResults','Inspected results',-1)

      self.delayDisplay('Test passed!')
    except Exception as e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e))
