"""
* 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 DSA3D
#/*!
# \file    DSA3D.py
# \author  Lina Liu and Dimitre Hristov
# \date    2009-12-10
#
# 
#*/
#----------------------------------------------------------------------------------

from mevis import *
from PythonQt import *
import time;


regContent = "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1";
## initiate the main window
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("OpenImage.dicomBrowser.reload").touch();
  ctx.field("OpenImage.previewLoad.autoLoad").touch();
  ctx.field("OpenImage.imgLoad.autoLoad").touch();
  
## 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.logError("The following query to database failed. \n" + query + "\n.");
  dbModule.call("close");
  return result;

## when the selected image has been changed 
def newImageName():
   ctx.field("DicomTagModify.tagName0").value = "PatientsName";
   ctx.field("DicomTagModify.tagName1").value = "PatientsBirthDate";
   ctx.field("DicomTagModify.tagName2").value = "PatientID";
   ctx.field("DicomTagModify.tagName3").value = "PatientsSex";
   ctx.field("DicomTagModify.tagName4").setValue("SeriesDescription");
   ctx.field("DicomTagModify.tagName5").setValue("Modality");
   ctx.field("DicomTagModify.getValues").touch();
   PatientN = ctx.field("DicomTagModify.tagValue0").value;
   PatientDOB = ctx.field("DicomTagModify.tagValue1").value;
   ID = ctx.field("DicomTagModify.tagValue2").value;
   Sex = ctx.field("DicomTagModify.tagValue3").value;
   if PatientN != ctx.field("PatientsName").value or PatientDOB != ctx.field("PatientsBirthDate").value or ID != ctx.field("PatientID").value or Sex != ctx.field("PatientsSex").value:  
     details = "";
     if PatientN != ctx.field("PatientsName").value:
       details += "<br>PatientName in Dicom:<b>" + PatientN + "</b> versus <b>" +  ctx.field("PatientsName").value + "</b> in records.\n";
     if PatientDOB != ctx.field("PatientsBirthDate").value:
       details +="<br>PatientBirthDate in Dicom:<b>" + PatientDOB + "</b> versus <b>" +  ctx.field("PatientsBirthDate").value + "</b> in records.\n"
     if ID != ctx.field("PatientID").value:
       details += "<br>PatientID in Dicom:<b>" + ID + "</b> versus <b>" +  ctx.field("PatientID").value + "</b> in records.\n";
     if Sex != ctx.field("PatientsSex").value:
       details += "<br>PatientSex in Dicom:<b>" + Sex + "</b> versus <b>" +  ctx.field("PatientsSex").value + "</b> in records.\n";
     answer = MLAB.showQuestion("The DICOM image info does not match patient's record. Do you want to replace the DICOM tags with patient's info in record?", details, "MeVisLab", ["OK", "Cancel"], 0);
     if answer == 1:
       ##if the user wants to cancel in case that the records do not match
       msg = "The user canceled the operation since the patient record did not match that in database. The patient information in DICOM remains as follows:";
       msg =+ "Patient Name:" + PatientN + "  DOB: " + PatientDOB + "  ID:" + ID + "  Sex:" + Sex; 
       MLAB.log(msg);
       return;
     else:
     ## save the Dicom Tag data into the image
       ctx.field("DicomTagModify.tagValue0").value = ctx.field("PatientsName").value;
       ctx.field("DicomTagModify.tagValue1").value = ctx.field("PatientsBirthDate").value;
       ctx.field("DicomTagModify.tagValue2").value = ctx.field("PatientID").value;
       ctx.field("DicomTagModify.tagValue3").value = ctx.field("PatientsSex").value;
       ctx.field("DicomTagModify.apply").touch();
       ctx.field("DicomTagModify.tagValue0").value = ctx.field("PatientsName").value;
       ctx.field("DicomTagModify.tagValue1").value = ctx.field("PatientsBirthDate").value;
       ctx.field("DicomTagModify.tagValue2").value = ctx.field("PatientID").value;
       ctx.field("DicomTagModify.tagValue3").value = ctx.field("PatientsSex").value;
       ctx.field("DicomTagModify.apply").touch();
       msg = "The user replaced the patient information in DICOM image with that in database\n";
       msg += "Patient information saved into DICOM are as follows:\n";
       msg += "Patient Name:" +  ctx.field("PatientsName").value;
       msg += "\nPatient Birth Date:" + ctx.field("PatientsBirthDate").value;
       msg += "\nPatient ID:" + ctx.field("PatientID").value;
       msg += "\nPatient Sex:" + ctx.field("PatientsSex").value;
       MLAB.log(msg);
   if ctx.field("Info.sizeT").value < 2: 
     ## we have a image with one time point
    ctx.connectField("SetPageExt2.input0", "DicomTagModify.output0");
    wind = ctx.showWindow("SaveImageW");
    title = "Patient: " + ctx.field("PatientsName").value + "   with MRO: " + ctx.field("PatientID").value + " DOB: " + ctx.field("PatientsBirthDate").value + " " + ctx.field("PatientsSex").value;
    wind.setTitle(title);
    wind.showMaximized();
   else:
    ctx.connectField("SetPageExt.input0", "DicomTagModify.output0");
    ctx.connectField("SetPageExt1.input0", "DicomTagModify.output0"); 
    ctx.field("Subimage.fullSize").touch();
    ctx.field("Subimage.st").value=1;
    ctx.field("Subimage.t").value=0;
    ctx.field("Subimage1.fullSize").touch();
    ctx.field("Subimage1.t").value=1;
    ctx.field("Subimage1.st").value=1;
    ctx.field("Subimage1.apply").touch();
    ctx.field("Subimage.apply").touch();
    
    ctx.field("AngioRegManual.reset").touch();  
    ctx.field("AngioRegManual.interactive").setBoolValue(False);
    wind = ctx.showWindow("RegistrationManualW");
    title = "Patient: " + ctx.field("PatientsName").value + "   with MRO: " + ctx.field("PatientID").value + " DOB: " + ctx.field("PatientsBirthDate").value + " " + ctx.field("PatientsSex").value;
    wind.setTitle(title);
    wind.showMaximized();
    
## save the image data to a file
def saveToFile(type):
  imageName = saveImageToFile(ctx.parent().field("PatientID").value, getFileName(),type);
  ## we use the format: "patientID(time).dcm" as unique name for the image
  if type == "/Src":
    ctx.field("ImgSave1.filename").value =   imageName;
    ctx.field("MinMaxScan1.update").touch();
    ctx.field("ImgSave1.save").touch();
  elif type == "/Target":
    ctx.field("ImgSave.filename").value =  imageName;
    ctx.field("MinMaxScan.update").touch();
    ctx.field("ImgSave.save").touch();
  else:
    ctx.field("ImgSave2.filename").value =  imageName;
    ctx.field("ImgSave2.save").touch();
  MLAB.log("The image:" + imageName + " has been saved to the file system.");
  return  imageName;

## Get the inverse matrix so that we can save the inverse pair of the registered images in DB
def getInversedM(regMatrix):
  ctx.field("MatrixArithmetic.matrixA").setStringValue(regMatrix);
  ctx.field("MatrixArithmetic.operation").setStringValue("Inverse");
  return ctx.field("MatrixArithmetic.outputMatrixC").stringValue();

## save the image data into DB and return the record ID in DB
def saveToDB(dir, imageType, patientID, seriesDescr, modality):  
  queryString = "insert into images(imagetype, modality, seriesDescr, imageName,patientID) values(";
  queryString = queryString + str(imageType) + ", '" + modality + "','" + seriesDescr + "', '" + dir + "'," + str(patientID) + ");";
  result = sendQuery(queryString);
  queryString = "SELECT ImageID AS ImgID FROM Images where imageName = '" + dir +"';";
  result = sendQuery(queryString);
  imgID = None;
  for item in result:
    imgID = item["imgid"];    
  MLAB.log("The image: " + dir + " has been saved to the DB as record No." + str(imgID));
  return imgID;

## save the regisration in DB
def saveReg(insertOp, srcID, targetID, status, regS):
 saveRegToFile(regS, srcID, targetID, ctx.parent().field("PatientID").value);
 if (insertOp == 1): 
   queryS = "INSERT INTO REGMATRICES(srcImage, targetImage, affineM, status, type)values(" + str(srcID) + "," + str(targetID) + ",'" + regS + "'" + "," + str(status) + ", 0" + ");";
 else:
   queryS = "UPDATE REGMATRICES set affineM = " + "'" + regS + "'" + " where srcImage = " + str(srcID) + " AND targetImage = " + str(targetID) + ";"; 
 result = sendQuery(queryS);
 if (result == -1):
   return;
 query = "select matrixID from regmatrices where srcImage = " + str(srcID) + " and targetImage = " + str(targetID) + ";";
 result = sendQuery(query);
 if (result == ""):
   return;
 imgID = None;
 for item in result:
    imgID = item["matrixid"]; 
 return imgID;

## check the DB see if the registration already exists. We only allow one registration for a unique source and target image pair
def regExist(srcID, targetID):
  queryString = "SELECT matrixID as Num, affineM FROM REGMATRICES WHERE srcImage = " + str(srcID) + " AND type = 0 AND targetImage = " + str(targetID) + ";";
  result = sendQuery(queryString);
  if (str(result) == "()"):
    return;
  number = 0;
  number = result[0]["Num"];
  global regMatrix;
  regMatrix = result[0]["affineM"];
  return number;  

## propagate the registration. 
def propagate(targetID, srcID):
  queryString = "SELECT targetImage, affineM , approvalStatus, status FROM REGMATRICES, approvals WHERE objID = matrixID and srcImage = " + str(targetID) + " AND type = 0 " + ";";
  result = sendQuery(queryString);
  newTarget = 0;
  for item in result:
    newTarget = item["targetimage"];
    oldApprovalS = item["approvalstatus"];
    if (int(newTarget) == int(srcID)):
      continue;
    Affine = item["affinem"];
    if (newTarget != 0):
      insertOp = 0;
      status = 1; # inferred stsatus
      regS = Affine.replace("\n", " ");
      if (regExist(srcID, newTarget) == 0):
         insertOp = 1;
      regID = saveReg(insertOp, srcID, newTarget, status, regS);
      saveApprovalStatus(regID, oldApprovalS, insertOp);
      saveRegToFile(regS, srcID, newTarget, ctx.parent().field("PatientID").value);
       ## update the DB now, find the inverse of the reg
      inverseM = getInversedM(Affine);
      regS = inverseM.replace("\n", " "); 
      status = 1; # inferred status for inversed reg
      regID = saveReg(insertOp, newTarget, srcID, status, regS);
      saveApprovalStatus(regID, oldApprovalS, insertOp);
      saveRegToFile(regS, newTarget, srcID, ctx.parent().field("PatientID").value);
  
## save the Approval status of a registration
def saveApprovalStatus(imgID, status, insertOp):
 if (insertOp == 0):
   queryS = "Update APPROVALS SET approvalStatus = " + status + " where UserID = 1 AND objType = 0 AND objID = " + str(imgID) + ";";
 else:
   queryS = "INSERT INTO APPROVALS(userID, objID, objType, patientID, approvalStatus)VALUES(1," + str(imgID) + ", 0 , " + getPatientRecordID() + " , " + status + ");";
 result = sendQuery(queryS);
 MLAB.log("The data of :" + str(imgID) + " has been saved.");
 
## The function is called when the user clicks OK on RegManual window
def clickOKonRegManual():
  ##save the image files
  global regContent;
  srcDir = saveToFile("/Src");
  tgtDir = saveToFile("/Target");
  
  ##save the target and src image
  ##here we need to save the image to DB
  ## imagetype: 1--2D, 2--3D
  patientRecID = getPatientRecordID();
  imageType = 2;
  seriesDescr = ctx.field("DicomTagModify.tagValue4").value;
  modality = ctx.field("DicomTagModify.tagValue5").value;
  filename =  getFileName();
  srcDir = srcDir.replace("\\","/");
  tgtDir = tgtDir.replace("\\","/");
  srcID = saveToDB(srcDir,imageType, patientRecID, seriesDescr, modality);
  destID = saveToDB(tgtDir,imageType, patientRecID, seriesDescr, modality);
  
  saveRegMatrixInDB(srcID, destID);
  saveRegToFile(regContent, srcID, destID, ctx.parent().field("PatientID").value);
  saveRegMatrixInDB(destID, srcID);
  saveRegToFile(regContent, destID, srcID, ctx.parent().field("PatientID").value);
  propagate(destID, srcID);
  ctx.disconnectField("SetPageExt.input0");
  ctx.disconnectField("SetPageExt1.input0"); 
  MLAB.log("The user review the image: " + filename + " and saved it into " + srcDir + " and " + tgtDir);
  ctx.window().close();
  
## The get the formated file Name: MRI (time)
def getFileName():
  return  ctx.parent().field("PatientID").value + "(" + time.strftime("%Y%m%d-%H%M%S") + ")";
  
## This function is called when user clicks cancel button in RegManual Window
## It disconnect the image inputs from SetPageExt and SetPageExt2 and close the window
def clickCancelonRegManual():
  MLAB.log("The user reviewed the image and canceled.");
  ctx.disconnectField("SetPageExt.input0");
  ctx.disconnectField("SetPageExt1.input0"); 
  ctx.window().close();
  
## get the PatientRecordID 
def getPatientRecordID():
  patientID = 0;
  if (ctx.parent().field("PatientID").value == ""):
    return str(patientID);
  queryString = "SELECT PatientID FROM Patients WHERE MRO = " + "'" + ctx.parent().field("PatientID").value + "'";
  result = sendQuery(queryString);
  for item in result:
    patientID = item["patientid"];
  return str(patientID);

## save the Registration matrix in a database  
def saveRegMatrixInDB(srcImage, destImage):
  queryString = "INSERT INTO REGMATRICES (affineM, srcImage, targetImage, status, type) VALUES (";
  queryString = queryString + "'" + regContent + "'," + str (srcImage) +  "," + str(destImage) + "," + "0, 0" + ");";
#  print "regQuerystring:" + queryString
  result = sendQuery(queryString);
  if (result == -1):
    return;
  queryString = "SELECT matrixID from regmatrices where srcImage = " + str(srcImage) + " and targetImage = " + str(destImage) + ";";
  result = sendQuery(queryString);
  if (str(result) == "()"):
    return;
  matrixID = None;
  for item in result:
    matrixID = item["matrixid"]; 
  
  queryString = "INSERT INTO Approvals(UserID, objID, patientID, objType, approvalStatus) values(1," + str(matrixID) + "," + getPatientRecordID() + ",0, 1);"
  result = sendQuery(queryString);
  
## The function is called when user clicks OK on save Image  
def clickOKonSaveImg():
  #save the image files
  # we temporarily hardcode the directory c:/MLabUserData here, we need to retrive it from config file later
  saveDir = saveToFile("");
  patientRecID = getPatientRecordID();
  seriesDescr = ctx.field("DicomTagModify.tagValue4").value;
  modality = ctx.field("DicomTagModify.tagValue5").value;
  filename = getFileName();
  saveDir = saveDir.replace("\\","/");
  srcID = saveToDB(saveDir, 2, patientRecID, seriesDescr, modality);
  MLAB.log("The user review the image: " + filename + " and saved it into " + saveDir);
  ctx.disconnectField("SetPageExt2.input0");
  ctx.window().close();
  
## The function is called when click Cencel on Save Image  
def clickCancelonSaveImg():
  MLAB.log("The user reviewed the image and canceled.");
  ctx.disconnectField("SetPageExt2.input0");
  ctx.window().close(); 

## test Interactive
def testInteractive():
  if (ctx.field("AngioRegManual.interactive").value == True):
    ctx.disconnectField("Arithmetic2.input1");
    return;
  ctx.connectField("Arithmetic2.input1","AngioRegManual.outReformatImage");

## call back function for field "no Subtraction"
def testSubtraction():
  ctx.disconnectField("OrthoView2D.image");
  ctx.disconnectField("MinMaxScan1.input0");  
  if (ctx.field("AngioRegManual.nosubtraction").value == True):
    ctx.connectField("MinMaxScan1.input0", "Subimage1.output0");
    ctx.connectField("OrthoView2D.image", "Subimage1.output0");
  else:
    ctx.connectField("MinMaxScan1.input0", "Arithmetic2.output0");
    ctx.connectField("OrthoView2D.image", "Arithmetic2.output0");
    
## help
def help():
  ctx.showHtmlHelp();

## clean UP the DSA3D  
def cleanUp():
  ctx.field("OpenImage.previewLoad.close").touch();
  ctx.field("OpenImage.imgLoad.close").touch();


#//# 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

