#  GeoDict -- The Virtual Material Laboratory
#  
#  Copyright (C) 2011-2021, Math2Market GmbH
#
#  GeoDict executable & support materials
#  are sold exclusively by Math2Market GmbH.
#  This code may not be modified
#  without written consent by Math2Market GmbH.
#
#  File:        GrayValueRangesToAILabels.py
#  Type:        GeoPy example
#  Version:     Geodict 2022
#  Creation:    2021
#  Author:      Janine Hilden
#

from guf import GUF
from struct import *
import numpy as np
import datetime

Header =  {
  'Release'      : '2022',
  }

Description = '''Macro for GeoDict 2022 by Support of Math2Market GmbH

Label gray value ranges as given materials.

Input: 
* loaded gray value image
* The gray value ranges for the materials.
* The Z-slice ranges to label

(c) Math2Market GmbH, Kaiserslautern, Germany
'''


Variables = {
  'NumberOfVariables' : 5,
  'Variable1' : {
    'Name'           : 'gd_extendedAILabels',
    'Label'          : 'File Name for Result',
    'Type'           : 'filestring',
    'Unit'           : 'gld',
    'ToolTip'        : 'Browse for an empty *.gld file for the resulting label data.',
    'BuiltinDefault' : "ExtendedAILabels.gld",
    },
  'Variable2' : {
    'Name'           : 'gd_StartImage',
    'Label'          : 'Start Image',
    'Type'           : 'uint',
    'BuiltinDefault' : 50,
    'Check'          : 'min1',
    },
  'Variable3' : {
    'Name'           : 'gd_EndImage',
    'Label'          : 'End Image',
    'Type'           : 'uint',
    'BuiltinDefault' : 150,
    'Check'          : 'min1',
    },
  'Variable4' : {
    'Name'           : 'gd_NthImage',
    'Label'          : 'Every nth Image',
    'Type'           : 'uint',
    'BuiltinDefault' : 10,
    'Check'          : 'min1',
    },
  'Variable5' : {
    'Name'           : 'gd_Ranges',
    'Label'          : 'Grayvalue Ranges for Different Materials',
    'Type'           : 'table',
    'Unit'           : ['int','int','int'],
    'ColumnHeaders'  : ['Material ID','Range - Start','Range - End'],
    'BuiltinDefault' : [0,1,80,1,120,180],
    },
  }

   
# write header into new gld file
def writeHeader( filename, inputheader ):
  # write header into gld file
  with open( filename, 'w' ) as f:
      f.write(inputheader)
      f.seek(511)
      f.write(' ')
  
# write array with labels into new gld file  
def writeLabels( filename, grayvaluerange, material_ids):  
  with open( filename, 'ab' ) as f:    
      
      # write labels in gld file
      for material in range( len(material_ids) ):
        index_4     = material_ids[material] # get current material to label
        
        count_slices = 0
        
        for z_slice in range( gd_StartImage-1,gd_EndImage,gd_NthImage ):
          index_3   = z_slice # get Z-coordinates of voxels to label
            
          for c in range ( len( grayvaluerange[material][count_slices][0] ) ):
            if index_3 in range( gd_StartImage-1,gd_EndImage,gd_NthImage ): 
              index_1 = grayvaluerange[material][count_slices][0][c] # get X-coordinate of voxel to label
              index_2 = grayvaluerange[material][count_slices][1][c] # get Y-coordinate of voxel to label
              f.write( pack("=HHHB", index_1, index_2, index_3, index_4) ) # write labeled voxel to gld file as binary data
            
            
          count_slices += 1   
   
# read gray value image
grayvalueimage       = gd.getVolumeField(0)

# get properties and content for array 
material_ids   = [] # this list will contain the material ids
ranges_start   = []
ranges_end     = []
grayvaluerange = [] # this list will contain the voxel positions for the gray value ranges
number_of_rows = 0  # this variable will count the number of voxels to label

# get positions for all voxels from the given Z-slices with gray values in the given ranges
for material in range( len(gd_Ranges) ):
  material_id = gd_Ranges[material][0] # get material id
  RangeStart  = gd_Ranges[material][1] # get gray value range start
  RangeEnd    = gd_Ranges[material][2] # get gray value range end
  
  material_ids.append(material_id) # append material id in list of material ids
  
  labelgrayvalues = []
  for z_slice in range( gd_StartImage-1, gd_EndImage, gd_NthImage ):
    
    grayvalueslice  = grayvalueimage[:,:,z_slice] 
    findgrayvalues  = np.where( np.logical_and(grayvalueslice>=RangeStart, grayvalueslice<=RangeEnd) ) # get all voxels with gray values in the given gray value range
    labelgrayvalues.append(findgrayvalues) # add found voxels for the current slice to list
    
    number_of_rows += len(findgrayvalues[0]) # count number of rows
    
  grayvaluerange.append(labelgrayvalues)  # add found voxel positions to the list
  

# get array length for header 
array_length  = number_of_rows * 7

# get date properties for header
weekdays      = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]
months        = ["Jan","Feb","Mar","Apr","Jun","Jul","Aug","Sep","Okt","Nov","Dec"]
today_date    = datetime.date.today()
today_weekday = today_date.weekday()
today_date    = str(today_date).split('-')

#                 weekday                         #month                              day                    year                  time
DateOfCreation =  weekdays[today_weekday] + ' ' + months[int(today_date[1])-1] + ' ' + today_date[2] + ' ' + today_date[0] + ' ' + '00:00:00'

# define header for new *.gld file
final_header = f'''GufVersion GLD1
HeaderLength 512
DateOfCreation {DateOfCreation}
GeoDictEdition 2021
GeoDictRevision 49068
GeoDictPlatform 64 bit Windows
NumberOfArrays 1
NamesOfArrays TrainingLabels
Array1:NumberOfColumns 4
Array1:NumberOfRows {number_of_rows}
Array1:ColumnNames X,Y,Z,Label
Array1:Types uint16,uint16,uint16,uint8
Array1:Units Pixel,Pixel,Pixel,
Array1:Offset {512}
Array1:Length {array_length}
  '''      
      
writeHeader( gd_extendedAILabels, final_header )  
writeLabels( gd_extendedAILabels, grayvaluerange, material_ids )