#!/usr/bin/env python

'''
Scales all numbers in a Therion th2 file by a specified scale.
Inputs: two args are required:
        1. a Therion map file (th2) to scale.
        2. the scale (integer or float) to divide all numbers by.
Output: a new file to stdout

Example: ./scale_th2 bigpitch.th2 2.5

Why this script is needed: I had a Therion survey where the sketches had been scanned at 
600 dpi and the scraps had been drawn from these images. When I scale the images to less 
pixels the images no longer line up under the scraps in the map editor. I needed to have
lower size images but still have the th2 scraps aligning with the underlying images.     

The Therion th2 files are like this:

  encoding  utf-8
  ##XTHERION## xth_me_area_adjust -136.0 -136.0 942.0 1252.0
  ##XTHERION## xth_me_area_zoom_to 50
  ##XTHERION## xth_me_image_insert {-8 1 1.0} {1200 {}} images/bathroom.jpg 0 {}

  scrap scrapbathroom -projection plan -scale [-64.0 269.0 491.5 269.0 0.0 0.0 14.1 0.0 m]
  
  point 1325.0 -767.0 remark -orientation 271.9 -text "rubble slope"
  
  line rock-edge -close on
    192.0 502.8
    189.2 503.5 187.2 508.2 188.8 511.2
    190.3 514.3 194.5 516.3 196.8 514.2
    198.2 513.0 200.5 506.8 199.8 505.0
    198.6 502.4 194.9 502.0 192.0 502.8
  endline
  
  point 589.0 939.0 station -name bolt@bathroom
  
  etc ...

Author:     Mike Lake
Versions:   2014.09.08 first version

'''

import sys, re
from string import join

DEBUG = False

####################################
# Define patterns to search for here
####################################

# A comment line
p_commentline = re.compile(r'''^#''')

# An all blank line. 
p_blankline = re.compile(r'''\s*$''')

# White space at start of line.
p_whitespace = re.compile(r'''\s*''')
  
# Line containing "-orientation". We don't want to scale numbers after "-orientation".
# e.g. point 1325.0 -767.0 remark -orientation 271.9 -text "rubble slope"
p_orientation = re.compile(r'''(.*) (-orientation .*)''')

# Line containing -scale [ ... ]. We want to scale on the first 4 numbers within the []'s. 
# e.g. scrap scrapthepit -projection plan -scale [1359.0 -172.0 1945.0 -173.0 0.0 0.0 10 0.0 m]
#                                                 ^^^^   ^^^^   ^^^^   ^^^^  
#                                                 scale  scale  scale  scale  no_scale_rest_of_line
# This matches a float: [-+]?[0-9]*\.?[0-9]+
# We just match four floats followed by anything with the []'s.
p_scale = re.compile(r'''(.* -scale )\[([-+]?[0-9]*\.?[0-9]+ [-+]?[0-9]*\.?[0-9]+ [-+]?[0-9]*\.?[0-9]+ [-+]?[0-9]*\.?[0-9]+) (.*)\]''')
    

def scale_line(line, scale): 
    '''
    This takes a string and splits it into 'words' on whitespace. 
    Then if any of those words can be cast to a number it does so and divides
    by the scale, finally joining the words together again. 
    Whitespace at the front of the string is preserved.
    '''
    # Extract the whitespace indent at the start to use later.
    indent = p_whitespace.match(line).group()

    words = line.split()
    new_words = []
    for word in words:
        try:
            new_value = '%.1f' % (float(word) / scale)
        except ValueError:
            new_value = word

        new_words.append(new_value)

    new_line = indent + join(new_words, ' ')

    return new_line



###############
# Start program 
###############

# There must be two args.
if len(sys.argv) != 3:
    print 'Usage: %s th2 scale' % sys.argv[0]
    print '       th2 is a Therion map file.'
    print '       scale is an integer or float to divide all numbers by.'
    sys.exit()

# No error checking here, user must have entereded a valid filename and an int or float.
thfile = sys.argv[1]
scale  = float(sys.argv[2]) 

# Open the Therion file.
fh = open(thfile, 'r')
lines = fh.readlines()
fh.close()

for line in lines:
    # Remove newline at end. 
    line = line.rstrip()

    # Print comment lines, no processing 
    if p_commentline.match(line):
        print line
        continue

    # Print blank lines, no processing 
    #if re.match('^\s*$', line):
    if p_blankline.match(line):
        print line
        continue

    # Match -orientation lines 
    m = p_orientation.match(line)
    if m is not None: 
        if DEBUG:   
            print '------ ORIENTATION ------'
            print line
        # Scale the part before the -orientation but not after.
        print scale_line(m.group(1), scale), m.group(2)
        continue

    # Match -scale lines
    m = p_scale.match(line)
    if  m is not None:
        if DEBUG:
            print '------ SCALE ------'
            print line
        # Scales just the first four floats within the []'s.
        print '%s[%s %s]' % (m.group(1), scale_line(m.group(2),scale), m.group(3))
        continue

    # For everything else we just scale all numbers in the line.
    print scale_line(line, scale)


