"""
* Copyright (c) 2009 Stanford University, School of Medicine All rights reserved. 
* 
* STANFORD ACADEMIC SOFTWARE LICENSE AGREEMENT 
* FOR AngioLocalize SOURCE CODE: 
* 
* 1.  This is a legal agreement between you, RECIPIENT, and STANFORD UNIVERSITY.  
*       By accepting, receiving, and using AngioLocalize.including any accompanying information, materials or 
*       manuals (Program), you are agreeing to be bound by the terms of this Agreement.  
*       If you do not agree to the terms of this Agreement, promptly return the Program to the STANFORD.

* 2.  STANFORD grants to RECIPIENT a royalty-free, nonexclusive, and nontransferable license to use the Program 
*       and accompanying source code furnished hereunder, upon the terms and conditions set out below. 
*       documentation and/or other materials provided with the distribution. 
* 3.  RECIPIENT acknowledges that the Program is a research tool still in the development stage and that 
*       it is being supplied as is, without any accompanying services, support or improvements from STANFORD.
* 4.  RECIPIENT agrees to use the Program solely for internal non-commercial purposes. 
* 5.  RECIPIENT may create derivatives and further transfer the Program under the following terms and conditions: 
*        A) Recipient agrees to not commercially exploit the Program and agrees all uses will be for non-commercial purposes; and,
*        B) Recipient may not mark any derivatives with the title of Program and may not to use iPad mark for any derivatives; and,
*        C) Recipient agrees to mark and acknowledge all derivatives as follows: "This is a derivative work based on AngioLocalize:
*           Frameless AVM localization with 2D Digital Subtraction Angiography (DSA) and 3D Rotational Angiography (DRA) Images developed
*           by Dimitre Hristov, Lina Liu.
*        D) Recipient agrees to make all improvements, enhancements and derivatives available under 
*           similar terms and conditions to the research community. 
* 6.   RECIPIENT warrants that RECIPIENT will not remove or export any part of the software or Program from the United 
*       States except in full compliance with all United States and other applicable laws and regulations.
* 7.  RECIPIENT will use the Program in compliance with all applicable laws, policies and regulations including, but not 
*       limited to, any approvals, informed consent and patient confidentiality principles.
* 8.   RECIPIENT will indemnify, hold harmless, and defend Stanford against any claim of any kind arising out of or
*       related to the exercise of any rights granted under this Agreement or the breach of this Agreement by RECIPIENT.
* 9.  Title and copyright to the Program and any associated documentation shall at all times remain with STANFORD, 
*       and RECIPIENT agrees to preserve same.
""" 
#----------------------------------------------------------------------------------
#! Macro module LocalizationWrapper
#/*!
# \file    LocalizationWrapper.py
# \author  Lina Liu and Dimitre Hristov
# \date    2009-12-10
#
# 
#*/
#----------------------------------------------------------------------------------

from mevis import *
import os;

## Global varibles
src3D = ""
target3D = ""
Img2D1 = ""
Img2D2 = ""
winHandle = None 
win3D = None 

## initialize some varibles when window pops up
def initWindow():
  ctx.field("PatientID").setValue(ctx.parent().field("PatientID").value);
  ctx.field("PatientsName").setValue(ctx.parent().field("PatientsName").value);
  ctx.field("PatientsBirthDate").setValue(ctx.parent().field("PatientsBirthDate").value);
  ctx.field("PatientsSex").setValue(ctx.parent().field("PatientsSex").value); 
  ctx.field("ImgLoad2D1.autoLoad").touch();
  ctx.field("ImgLoad2D2.autoLoad").touch();
  ctx.field("ImgLoad3DTarget.autoLoad").touch();
  ctx.field("ImgLoad3DSource.autoLoad").touch();
  ctx.field("Localization.View2DImage1.ext.annotation.annotationMode").setValue("ANNO_MODE_USER");
  ctx.field("Localization.View2DImage1.ext.annotation.annotationUserBottomLeft").setValue("Timepoint: $(slice) \n$(sizex),$(sizey),$(sizec) \n$(voxelx),$(voxely) \n$(SeriesDescription)");
  ctx.field("Localization.View2DImage1.ext.annotation.showOrientation").setValue("ANNO_ORIENTATION_OFF");
  ctx.field("Localization.View2DImage2.ext.annotation.annotationMode").setValue("ANNO_MODE_USER");
  ctx.field("Localization.View2DImage2.ext.annotation.annotationUserBottomLeft").setValue("Timepoint: $(slice) \n$(sizex),$(sizey),$(sizec) \n$(voxelx),$(voxely) \n$(SeriesDescription)");
  ctx.field("Localization.View2DImage2.ext.annotation.showOrientation").setValue("ANNO_ORIENTATION_OFF");
  patientID = getPatientRecordID();
  if (patientID != "0"):
    loadGUILists(patientID);
  
## clean Up the module. Disconnect all the image inputs and other connections that may cause performance slow down
def cleanUp():
  disconnectFields();
  ctx.field("ImgLoad2D1.close").touch();
  ctx.field("ImgLoad2D2.close").touch();
  ctx.field("ImgLoad3DSource.close").touch();
  ctx.field("ImgLoad3DTarget.close").touch();

## reset and disconnect fields
def disconnectFields():
  ctx.disconnectField("Localization.image3Dtarget");
  ctx.disconnectField("Localization.image3Dsource");
  ctx.disconnectField("Localization.image2D1");
  ctx.disconnectField("Localization.Image2D2");
  ctx.disconnectField("Localization.TargetView3D.inInvPreLUT");
  ctx.disconnectField("Localization.CSOPerspectiveTransformation1.apply");
  ctx.disconnectField("Localization.CSOPerspectiveTransformation2.apply");
  
## pop up the 3D selection context menu
def selectChanged3D():
  ctx.control("popup3D").popup();
  
## pop up the 2D select context menu  
def selectChanged2D():
  ctx.control("popup2D").popup();

## send a query to the Database and return the result set
def sendQuery(query):
  ctx.field("DBConn.Statement").setValue(query);
  dbModule = ctx.module("DBConn");
  result = dbModule.call("sendQuery");
  if (result == -1):
    MLAB.log("The following query to database failed: \n" + query + "\n.");
  dbModule.call("close");
  return result;

## get the Image File Name
def getImageFileName(imgID):
  queryString = "select imageName, imageDir from images where imageID = " + str(imgID);
  result = sendQuery(queryString);
  imgName = None;
  for item in result:
    imgName = item["imagename"];
  return imgName; 
  
## The function specifies actions taken when user selects 3D image as a source  
def select3DAsSource():
  global src3D;
  list3D = ctx.control("list3D");
  srcItem = list3D.selectedItem();
  src3D = srcItem.text(0);
  loadImage(src3D,1);
  
## load image given a fileName and src or Target type
def loadImage(imageID, srcOrTgt):
  srcF = getImageFileName(imageID);
  src = os.path.normpath(srcF + ".dcm");
  fileName = os.path.normpath(src);
  if (srcOrTgt == 1):
    if fileName!= None:
      ctx.field("ImgLoad3DSource.filename").setValue(fileName);
    ctx.control("src1Label").setTitle("3D Source Image file:" + fileName);
    ctx.field("ImgLoad3DSource.load").touch();
    MLAB.log("source selected:" + fileName);
  else:
    if fileName!= None:
      ctx.field("ImgLoad3DTarget.filename").setValue(fileName);
      ctx.field("TargetImgID").setValue(imageID);
    ctx.control("target1Label").setTitle("3D Target Image file:" + fileName);
    ctx.field("ImgLoad3DTarget.load").touch();
    MLAB.log("target selected:" + fileName);
    
## The function specifies actions taken when user selects 3D image as a target 
def select3DAsTarget():
  global target3D;
  list3D = ctx.control("list3D");
  targetItem = list3D.selectedItem();
  target3D = targetItem.text(0);
  loadImage(target3D,2);

## The function specifies the actions taken when the user selects approve and finish registration   
def regExistAndApproved(target, src):
  queryString = "SELECT matrixID as Num FROM REGMATRICES WHERE srcImage = " + str(src) + " AND targetImage = " + str(target) + ";";
  result = sendQuery(queryString);
  number = 0;
  if (str(result) == "()"):
    return number;
  
  number = result[0]["num"];
  approvalID = 0;
  if (number != 0):
     queryString = "select userID from approvals where approvalStatus = 1 and objType = 0 and objID = " + str(number);
     result = sendQuery(queryString);
     if (str(result) == "()"):
       return 0;
     approvalID = result[0]["userid"];
  if (approvalID == 0):
    return 0;
  else:
    return number; 
  
## The function  is called when user selects cancel button  
def cancel():
  global src3D;
  global target3D;
  global Img2D1;
  global Img2D2;
  src3D = ""
  target3D = ""
  Img2D1 = ""
  Img2D2 = ""
  ctx.controlDebug("target1Label").setTitle("3D Target Image:");
  ctx.controlDebug("src1Label").setTitle("3D Source Image:" );
  ctx.controlDebug("src2Label").setTitle("2D Image 1:");
  ctx.controlDebug("target2Label").setTitle("2D Image 2:");
  cleanUp();
  
## It specifies the actions that were taken when user clicks go button  
def go():
  global winHandle;
  global win3D;
  global src3D;
  global target3D;
  global Img2D1;
  global Img2D2;
  disconnectFields();
  winHandle = ctx.window();
  title = "Patient: " + ctx.field("PatientsName").value + "   with MRO: " + ctx.field("PatientID").value + " DOB: " + ctx.field("PatientsBirthDate").value + " " + ctx.field("PatientsSex").value;
  if (src3D == "" and target3D != "" and Img2D1 == "" and Img2D2 == ""): 
    ctx.connectField("Localization.image3Dtarget", "ImgLoad3DTarget.output0");
    manager = ctx.module("Localization");
    setWindows(False, True, False, False, False);
    win3D = manager.showWindow("Two2DTwo3D");
    win3D.setTitle(title);
    win3D.showMaximized(); 
    reloadCSO(target3D);
  elif (target3D != "" and Img2D1 != "" and src3D == "" and Img2D2 ==""):
    if (regExistAndApproved(target3D, Img2D1) == 0):
      info = "The image " + str(target3D) + " and " + str(Img2D1) +" is not registered. You have to register them first.";
      MLAB.showInformation(info);
      return;
    ctx.connectField("Localization.image3Dtarget", "ImgLoad3DTarget.output0");
    ctx.connectField("Localization.image2D1", "ImgLoad2D1.output0");
    manager = ctx.module("Localization");
    fillInMatrices(target3D, Img2D1, 0);
    ctx.field("Localization.StructureManager.useRepaintNotification").setValue(True);
    ctx.connectField("Localization.CSOPerspectiveTransformation1.apply", "Localization.StructureManager.notifyOnRepaint");
    setWindows(False, True, True, False, False);
    win3D = manager.showWindow("Two2DTwo3D");
    win3D.setTitle(title);
    win3D.showMaximized();
    reloadCSO(target3D);
  elif (target3D != "" and Img2D1 != "" and Img2D2 !=""):
    if (regExistAndApproved(target3D, Img2D1) == 0): 
      info = "The image " + str(target3D) + " and " + str(Img2D1) +" is not registered. You have to register them first.";
      MLAB.showInformation(info);
      return;
    if (regExistAndApproved(target3D, Img2D2) == 0):
      info = "The image " + str(target3D) + " and " + str(Img2D2) +" is not registered. You have to register them first.";
      MLAB.showInformation(info);
      return;
    ctx.connectField("Localization.image3Dtarget", "ImgLoad3DTarget.output0");
    ctx.connectField("Localization.image2D1", "ImgLoad2D1.output0");
    ctx.connectField("Localization.Image2D2", "ImgLoad2D2.output0");
    fillInMatrices(target3D, Img2D1, 0);
    fillInMatrices(target3D, Img2D2, 1);
    manager = ctx.module("Localization");
    reloadCSO(target3D);
    ctx.field("Localization.StructureManager.useRepaintNotification").setValue(True);
    ctx.connectField("Localization.CSOPerspectiveTransformation1.apply", "Localization.StructureManager.notifyOnRepaint");
    ctx.connectField("Localization.CSOPerspectiveTransformation2.apply", "Localization.StructureManager.notifyOnRepaint");
    if (src3D == ""):
      setWindows(False, True, True, True, False);
      win3D = manager.showWindow("Two2DTwo3D");
      win3D.setTitle(title);
      win3D.showMaximized();
    else:
      if (regExistAndApproved(target3D, src3D) == 0):
        info = "The image " + str(target3D) + " and " + str(src3D) +" is not registered. You have to register them first.";
        MLAB.showInformation(info);
        return;
      ctx.connectField("Localization.image3Dsource", "ImgLoad3DSource.output0");
      ctx.field("Localization.SourceView3D.worldPosition").connectFrom(ctx.field("Localization.TargetView3D.worldPosition"));
      ctx.field("Localization.TargetView3D.worldPosition").connectFrom(ctx.field("Localization.SourceView3D.worldPosition"));
      fillInAffineMatrix(src3D, target3D);
      setWindows(True, True, True, True, True);
      win3D = manager.showWindow("Two2DTwo3D");
      win3D.setTitle(title);
      win3D.showMaximized();
  else:
    MLAB.showInformation("Please choose one of the following combinations: \n1) one 3D source, one 3D target and two 2D images \n2) one 3D target \n3) one 3D target and two 2D images \n4)one 3D target and one 2D image as Image1.");
## get the AffineMatrix for 3D src and 3D target and fill In the Localization panel    
def fillInAffineMatrix(src3D, target3D):
  queryString = "select affineM from regmatrices where type = 0 and srcImage = " + str(src3D) + " and targetImage = " + str(target3D) + ";";
  result = sendQuery(queryString);
  if (str(result) == "()"): 
      return;
  affineM = None;
  for item in result:
    affineM = item["affinem"];
  ctx.field("Localization.set3DRegistration.matrix").setStringValue(affineM);
  
## fill In the projection matrixes fields  
def fillInMatrices(target3D, Img2D1, pairNumber):
  ##pairNumber: 0 is the first set of 2D img, 1 is the second set of 2D img
  queryString = "select b.affineM as affineM, b.perspectM as perspectM, b.postAffineM as postAffineM from images a, regmatrices b, images c, approvals d where b.srcImage = " + str(Img2D1) + " and a.ImageID = " + str(Img2D1) + " and d.objID = b.matrixID and d.approvalStatus = 1 and b.targetImage = " + str(target3D) + " and c.ImageID = " + str(target3D) + " and a.patientID = " + getPatientRecordID() + " and c.patientID = " + getPatientRecordID() + ";";
  result = sendQuery(queryString);
  if (str(result) == "()"): 
      return;
  for item in result:
    postAffineM = item["postaffinem"];
    perspectM = item["perspectm"];
    affineM = item["affinem"];
    if (pairNumber == 0):
      ctx.field("Localization.AffinePostProjMatrix1").setStringValue(postAffineM);  
      ctx.field("Localization.AffinePostProjMatrix1").touch();
      ctx.field("MatrixArithmetic.matrixA").setStringValue(perspectM);
      ctx.field("MatrixArithmetic.matrixB").setStringValue(affineM);
      ctx.field("MatrixArithmetic.operation").setStringValue("Multiply");
      MLAB.log("projectiong matrix1:" + ctx.field("MatrixArithmetic.outputMatrixC").stringValue());
      MLAB.log("postAffine Matrix1:" + ctx.field("Localization.AffinePostProjMatrix1").stringValue());
      ctx.field("Localization.projectionMatrix1").setStringValue(ctx.field("MatrixArithmetic.outputMatrixC").stringValue());
      ctx.field("Localization.projectionMatrix1").touch();
      ctx.field("Localization.CSOPerspectiveTransformation1.apply").touch();
    else:    
      ctx.field("Localization.AffinePostProjMatrix2").setStringValue(postAffineM);  
      ctx.field("Localization.AffinePostProjMatrix2").touch();
      ctx.field("MatrixArithmetic.matrixA").setStringValue(perspectM);
      ctx.field("MatrixArithmetic.matrixB").setStringValue(affineM);
      ctx.field("MatrixArithmetic.operation").setStringValue("Multiply");
      MLAB.log("projectiong matrix2:" + ctx.field("MatrixArithmetic.outputMatrixC").stringValue());
      MLAB.log("postAffine Matrix2:" + ctx.field("Localization.AffinePostProjMatrix2").stringValue());
      ctx.field("Localization.projectionMatrix2").setStringValue(ctx.field("MatrixArithmetic.outputMatrixC").stringValue());
      ctx.field("Localization.projectionMatrix2").touch();
      ctx.field("Localization.CSOPerspectiveTransformation2.apply").touch();
  
## when the user select "selection", the background Image and registration list will be popped up in the front
def raiseW():
  global winHandle;
  if (winHandle != None):
    winHandle.raiseWindow();
    
## reload the CSO for Image with ID "targetID"
def reloadCSO(targetID):
  ctx.field("Localization.StructureManager.removeAllCSOsAndGroups").touch();
  query = "select filename from CSOList where srcImage = " + str(targetID);
  result = sendQuery(query);
  if (str(result) == "()"): 
      return;
  CSOFileName = "";
  for item in result:
    CSOFileName = item["filename"];
  if (CSOFileName != ""):
    ctx.field("Localization.StructureLoad.fileName").setValue(os.path.normpath(CSOFileName));
    ctx.field("Localization.StructureLoad.apply").touch();
    ctx.connectField("Localization.StructureManager.inCSOList", "Localization.StructureLoad.outCSOList");
    
## update CSOList in the list panel
def updateCSOList():
   loadCSOList(); 
   
## The function specifies the action when user selects Image2D1
def Image2D1():
  load2DImage(1);

## The function specifies the action when user selects Image2D2
def Image2D2():
  load2DImage(2);
 
## load 2D image data from file 
def load2DImage(num):
  list2D = ctx.control("list2D");
  Item = list2D.selectedItem();
  image2D1F = getImageFileName(Item.text(0));
  file2D = os.path.normpath(image2D1F + ".dcm");
  image2D = os.path.normpath(file2D);
  if (num == 1):
    global Img2D1;
    Img2D1 = Item.text(0);
    if Img2D1!= None:
      ctx.field("ImgLoad2D1.filename").setValue(image2D);
    ctx.field("ImgLoad2D1.load").touch();
    ctx.control("src2Label").setTitle("2D image 1:" + image2D);
    MLAB.log("2D image 1:" + image2D);
  else:
    global Img2D2;
    Img2D2 = Item.text(0);
    if Img2D2!= None:
      ctx.field("ImgLoad2D2.filename").setValue(image2D);
    ctx.field("ImgLoad2D2.load").touch();
    ctx.control("target2Label").setTitle("2D image 2:" + image2D);
    MLAB.log("2D image 2:" + image2D);

## get the patient record ID
def getPatientRecordID():
  patientID = 0;
  if (ctx.field("PatientID").value == ""):
    return str(patientID);
  queryString = "SELECT PatientID FROM Patients WHERE MRO = " + "'" + ctx.field("PatientID").value + "'";
  result = sendQuery(queryString);
  
  for item in result:
    patientID = item["patientid"];
  return str(patientID);

## init DB and load the 2D images, 3D images and registrations existed in DB 
def loadGUILists(patientID):
    load3DImageList(patientID);
    load2DImageList(patientID);   
    loadRegItem();
    loadCSOList();
    MLAB.log("Query has been made in reg tab.");
    
## load the CSOList existed in DB
def loadCSOList():
    CSOList = ctx.controlDebug("CSOList");
    CSOList.clearItems();
    patientID = getPatientRecordID();
    if (patientID == "0"):
      return;
    queryString = "SELECT CSOId, srcImage, approvalStatus from CSOList, approvals where CSOList.patientID = " + patientID + " and approvals.objType = 1 and approvals.objID = csoList.CSOId;";
    result = sendQuery(queryString);
    if (str(result) == "()"): 
      return;
    for item in result:
      csoID = item["csoid"];
      srcImage = item["srcimage"];
      status = item["approvalstatus"];
      approvalS = "false";
      if (status == 1):
        approvalS = "true";
      CSOList.insertItem([str(csoID), srcImage, approvalS]);
    
    
## load the 3D image List    
def load3DImageList(patientID):
    imgList = ctx.controlDebug("list3D");
    imgList.clearItems();
    #init DB
    queryString = "select imageID, modality, seriesDescr from images where imagetype = 2 and patientID = " + patientID + ";";
    result = sendQuery(queryString);
    if (str(result) == "()"): 
      return;
    for item in result:
      imgName = item["imageid"];
      imgM = item["modality"];
      series = item["seriesdescr"];
      imgList.insertItem([str(imgName), imgM, series]);

## close the Window
def closePWin():
  global win3D;
  if (win3D != None):
    win3D.close();
    
## loaded 2D image list
def load2DImageList(patientID):
    imgList = ctx.controlDebug("list2D");
    imgList.clearItems();
    #init DB
    queryString = "select imageID, modality, seriesDescr from images where imagetype = 1 and patientID = " + patientID + ";";
    result = sendQuery(queryString);
    if (str(result) == "()"): 
      return;
    for item in result:
      imgName = item["imageid"];
      imgM = item["modality"];
      series = item["seriesdescr"];
      imgList.insertItem([str(imgName), imgM, series]);
    
    
## load the registrations existed in DB
def loadRegItem():
    regImage = ctx.controlDebug("registeredImg");
    regImage.clearItems();
    patientID = getPatientRecordID();
    if (patientID == "0"):
      return;
    queryString = "select b.affineM as matrixA, b.perspectM as matrixB, b.postAffineM as matrixC, b.srcImage as src, a.modality as srcM, a.seriesDescr as srcDesc, c.modality as trgM, c.seriesDescr as tgtDesc, b.targetImage as target , d.approvalStatus as status, to_char(d.approvaldate,'YYYY-MM-DD HH12:MI:SS') as savedTime, b.status as genre from images a, regmatrices b, images c, approvals d where b.srcImage = a.imageID and b.targetImage = c.imageID and d.approvalStatus =1 and d.objType = 0 and d.objID = b.matrixID and a.patientID = " + patientID + ";"
    result = sendQuery(queryString);
    if (str(result) == "()"): 
      return;
    for item in result:
      matrixReg = item["matrixa"];
      perspectiveM = item["matrixb"];
      postAffineM = item["matrixc"];
      srcImg = item["src"];
      tgtImg = item["target"];
      srcMod = item["srcm"];
      srcDesc = item["srcdesc"];
      targetMod = item["trgm"];
      targetDesc = item["tgtdesc"];
      statusNum = item["status"];
      approvalDate = item["savedtime"];
      genreNum = item["genre"];
      status = None;
      genre = None;
      if (statusNum == 0):
        status = "false";
      else:
        status = "true";
      if (genreNum == 0):
        genre = "direct";
      else:
        genre = "inferred";
      regImage.insertItem([str(srcImg), srcMod, srcDesc, str(tgtImg), targetMod, targetDesc, status, genre, approvalDate, matrixReg, perspectiveM, postAffineM]);
    
## update changes from CSO manager   
def CSOManagerUpdate():
  ctx.field("Localization.CSOPerspectiveTransformation1.apply").touch();
  ctx.field("Localization.CSOPerspectiveTransformation2.apply").touch();

## set up whether source, target 2D image viewers will be shown
def setWindows(sourceV, targetV, image1V, image2V, two2Dtwo3D):
  ctx.field("Localization.showSource").setBoolValue(sourceV);
  ctx.field("Localization.showTarget").setBoolValue(targetV);
  ctx.field("Localization.showImage1").setBoolValue(image1V);
  ctx.field("Localization.showImage2").setBoolValue(image2V);
  ctx.field("Localization.two2Dtwo3D").setBoolValue(two2Dtwo3D);

## help function
def help():
   ctx.showHtmlHelp();
#//# MeVis signature v1
#//# key: MFowDQYJKoZIhvcNAQEBBQADSQAwRgJBANEfsmYse2e1dRhkQ9AQbreCq9uxwzWLoGom13MNYmyfwoJqQOEXljLFAgw2eEjaT12G4CdqKWhRxh9ANP6n7GMCARE=:VI/mB8bT4u+mRtf/ru8yUQi8BzpaS3UeL2x62YxsUYnVqCWuLrVNLiukIIjnJMKQXlc8ezmgOIcVAV7pgvgKpQ==
#//# owner: MeVis
#//# date: 2009-06-10T04:12:25
#//# hash: vHSfj+Qt8xrIkDVP4W0Rep0YH6a3+MySa0+Cs+AoqEK/IbB+f5szwmyVTENOjzz/JEgm2t31FtHmUUMFcQg5VQ==
#//# MeVis end

