from unicode import latin1_to_ascii
import json
import pdb
import re
import os
import sys
import time
import glob
import shutil
from pprint import pprint, pformat
import gzip
import csv
import platform
import inspect
from functools import partial
import os.path

#Globals

currOS = platform.platform()
if currOS.find('Windows') == 0:
    LOCALBASE = 'D:/website/'
    #LOCALBASE = 'A:/test/'
else:
    LOCALBASE = '/mnt/d/website/'
#    LOCALBASE = '/ssd/home/sarge/prog/python/fron'
TEMPLATE = LOCALBASE + 'templates/template.ft3'
ERRFILE = LOCALBASE + 'ft3form/temperr.txt'

def join_with_fslash(s1, s2):
    return(s1 + '/' + s2)
# end join_with_fslash

startTime = int(time.time())
#Data/input files contained in local website base
CONTRIBDIR = LOCALBASE + "contributors"

# For converting RTF files to latin1 and vice versa
RTFPREFIX = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset0 MS Shell\r\nDlg;}}\r\n\\viewkind4\\uc1\\pard\\f0\\fs-22 ";
RTFSUFFIX = "\\par\r\n}\r\n";
IMAGETYPES = ('.png', '.tif', '.pdf', '.jpg')

# Compiled regular expressions
#use re.DOTALL to match \n as well
# = number in a series; % = arbitrary number to make source unique
reUnicode = re.compile('(\\\\u.... )')
reSplitAndOr = re.compile('(..*) (?:and|or) (..*)')
reGetDate = re.compile('\D\((>?c?a?\.? ?[1-2][0-9]{3})\)', re.DOTALL)
#reGetDate = re.compile('\D\((c?a?\.? ?[1-2][0-9]{3})\)', re.DOTALL)
reApproxDate = re.compile('(ca?\.? ?)[1-2][0-9]{3}', re.DOTALL)
reParenContents = re.compile('^([^(]*)\(([^)]+)\)(.*)', re.DOTALL)
reEntabulated = re.compile('([IiEe]ntabulated)', re.DOTALL)
reEncoded = re.compile('[Ee](ncoded)', re.DOTALL)
reEdited = re.compile('[Ee](dited)', re.DOTALL)
reEnc = re.compile(r'[Ee](nc\.)', 1)
reEd = re.compile(r'[Ee](d\.)', 1)
reInfo = re.compile('^(...).*?: *(..*)$', re.DOTALL)
reKey = re.compile('^[A-G][b#]*[Mm]$',re.DOTALL)
reGetDictItem = re.compile('"([^"]*)"[^"]*"([^"]*)"')
reDeLang = re.compile(r'(\\lang[0-9]{4)}')

# These are for error messages
# for current func name, specify n = 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
funcName = lambda n=1: sys._getframe(n + 1).f_code.co_name
callerName = lambda n=2: sys._getframe(n + 1).f_code.co_name
lineNo = lambda n=1: sys._getframe(n + 1).f_lineno

# insert a string into another string at a specific location
def insert_str(stInsert, str, index):
    return str[:index] + strInsert + str[index:]

def at_eof(f):
    return f.tell() == os.fstat(f.fileno()).st_size

def show_tuple(t):
    for item in t:
        print("%s, " % item, end = "")
    print("")

difficulties = ["??", "Beginner", "Easy", "Medium", "Challenge", "Difficult", "Virtuoso"]

field_map = [
    ("Title", "title"),
    ("Subtitle", "subtitle"),
    ("Composer", "composer"),
    ("Orig. composer", "composer0"),
    ("Footnote", "footnote"),
    ("Source", "Source"),
    ("Document", "document"),
    ("Volume", "volume"),
    ("Date", "date"),
    ("Page", "page"),
    ("Editor", "editor"),
    ("Encoder", "encoder"),
    ("Arranger", "arranger"),
    ("Intabulator", "intabulator"),
    ("Concordances", "concordances"),
    ("Contributor", "contributor"),
    ("Info", "info"),
    ("Piece", "piece"),
    ("Section", "section"),
    ("Type", "type"),
    ("Key", "key"),
    ("Difficulty", "difficulty"),
    ("Ensemble", "ensemble"),
    ("Part", "part"),
    ("Remarks", "remarks"),
    ("Recording", "recurl"),
    ("Facsimile", "facurl"),
    ("Fronimo", "stFile"),
    ("PDF", "stPdf"),
    ("Midi", "stMidi"),
    ("Modified", "mtime"),
    ("Created", "ctime"),
    ]

key_order = [   # for dumping
    "title", "subtitle",
    "composer", "composer0",
    "footnote", "source", "document", "volume", "date", "page",
    "editor", "encoder", "arranger", "intabulator",
    "contributor", "concordances",
    "info", "piece", "section",
    "type", "key", "difficulty",
    "ensemble",
    "part",
    "remarks","recurl", "facurl", 
    "stFile", "stPdf", "stMidi",
    "ctime", "mtime",
    ]

#Creates fronimo object from file with name stIn
class Fronimo:
    def __init__(self, stIn = TEMPLATE):
        # set all attributes to empty string
        for col,att in field_map:
            setattr(self, att, "")
        self.base = LOCALBASE
        self.currProg = ''
        self.credits = ''
        self.begText = ''
        self.endText = ''
        self.performance = False
        self.simple = False
        self.ornamented = False
        self.footnote = ""
        #input file name set from argument stIn
        self.stFile = stIn
        self.stFron = ''
        self.flFron = None
        self.flOut = None
        self.flErr = None
        self.offset = None
        self.oldOffset = None
        self.volume = ""
        self.info = ""
        self.recurl = ""
        self.facurl = ""
        #keep track of padding of the last dir contacted that contains numerical pages
        #self.lastPageDir = ["", 0] 
        # Creates self.stFron from fronimo file: self.stFile
        if self.read_and_unzip_file() == False:
            # marker for failure of class instantiation
            self.start_offset = -1
        else:
            self.start_offset = self.get_start_offset()
            if self.start_offset > 0:
                # Fills in other values of fronimo object.
                if self.populate() == False:
                    self.start_offset = -1
# end of  __init___

    # Open error file
    @classmethod
    def open_error(kls, errfile):
        errfile = ERRFILE
        try:
            kls.flErr = open(errfile, "w", encoding = 'latin1')
        except OSError:
            print("OSError: Cannot open error File ", errfile,
            " for writing.", file=sys.stderr)
            return False
        except:
            print("Other error: Cannot open error File ", errfile,
            " for writing.", file=sys.stderr)
            return False
        return True
# End open_error

    @classmethod
    def open_file(kls, fl, mode):
        try:
            flName = open(fl, mode, encoding='latin1')
        except OSError:
            err= "OSError: Cannot open file %s in mode %s." % (fl,  mode)
            kls.print_error(fl, err)
            return None
        except:
            err= "Other error: Cannot open file %s in mode %s." % (fl,  mode)
            kls.print_error(fl, err)
            return None
        return flName
# end of openFile

# open and read all needed fronimo-related files
    @classmethod
    def open_files(kls):
        pass
# end open_files
    
# for future use
    @classmethod
    def make_contrib_dir(kls):
        s= self.contributor.casefold()
        s = s.replace('.', '')
        s - s.replace(' ', '_')
        self.contribDir = join_with_fslash(CONTRIBDIR, s)
        if not os.path.exists(self.contribDir):
            os.makedirs(self.contribDir)

# print helpful error message
    @classmethod
    def print_error(kls, currFile, errMsg):
        kls.currProg = 'load.py'
        print("In %s; caller:%s; line:%d of %s\nfile:'%s'; %s." % (funcName(), callerName(), lineNo(), kls.currProg, currFile, errMsg), file=kls.flErr)
        kls.flErr.flush()

# Get starting offset for reading/writing a fronimo file
    def get_start_offset(self):
        if (self.stFron[4] == '\x15') or (self.stFron[4] == '\x14'):
            return 364
        elif self.stFron[4] == '\x16':
            return 368
        else:
            val = hex(ord(self.stFron[4]))
            msg = "stFron[4] = %s (not \\x15 or \\x16), so starting offset is unknown" % (val)
            Fronimo.print_error(self.stFile, msg)
            return -1
# end get_start_offset
    
# read and unzip a fronimo file: self.flFile into self.stFron
# Fronimo files are zipped
    def read_and_unzip_file(self):
        self.flFron = gzip.open(self.stFile)
        if self.flFron == None:
            Fronimo.print_error(self.stFile, 'Cannot open and unzip fronimo file.')
            return False
        # Read entire file into a global byte array
        btFron = self.flFron.read()
        if len(btFron) < 100:
            Fronimo.print_error(self.stFile, 'Cannot read fronimo file.')
            return False
        self.stFron = btFron.decode("latin1")
        if len(self.stFron) < 100:
            Fronimo.print_error(self.stFile, 'Cannot decode fronimo file.')
            return False
        return True
# end of read_and_unzip_file

    # get x number of chars from string. Updates offset value
    def _get(self, numChars):
        if numChars == 0:
            return ""
        oldOffset = self.offset
        self.offset += numChars
        if (self.offset) > len(self.stFron):
            errMsg = "Cannot get %d chars starting at offset %d" % (numChars, oldOffset)
            Fronimo.print_error(self.stFile, errMsg)
            return ""
        return self.stFron[oldOffset:self.offset]

    # Get two bytes of info
    def _getWord(self, fSigned):
        inCh = self._get(1)
        if inCh == "":
            errMsg = "No first byte in stFron at offset %d" % (self.offset)
            Fronimo.print_error(self.stFile, errMsg)
            return -1
        word1 = ord(inCh)
        inCh = self._get(1)
        if inCh == "":
            errMsg = "No 2nd byte in stFron at offset %d" % (self.offset)
            Fronimo.print_error(self.stFile, errMsg)
            return -1
        word2 = 256 * ord(inCh)
        word = word1 + word2
        if (fSigned and word > 32768):
            word -= 65536
        return word

    # Get a fronimo-formatted string
    def _getBstr(self):
        # first byte is string length if < 255
        firstByte = self._get(1)
        if firstByte == False:
            errMsg = "No first byte in stFron at offset %d" % (self.offset)
            Fronimo.print_error(self.stFile, errMsg)
            return ""
        length = ord(firstByte)
        if length == 0:
            return ""
        # First char 255 means a long string.
        # Next 2 chars determine string length as an unsigned integer
        if length == 255:
            length = self._getWord(False)
            if length == -1:
                errMsg = "Zero string length from _getWord, offset %d" % (self.offset)
                Fronimo.print_error(self.stFile, errMsg)
                return ""
        stOut = self._get(length)
        if stOut == "":
            errMsg = "Result of get(length) is "", at offset %d" % (self.offset)
            Fronimo.print_error(self.stFile, errMsg)
            return ""
        return stOut
        
    def _put(self, stNew):
        # Assumes new string is RTF'd, if necessary, but not in fronimo string format,
        # with leading length indicator
        # Find length of old fronimo string
        ch = self._get(1)
        if ch == '\xFF': # means next 2 chars determine length
            byte1 = self._get(1)
            if byte1 == "":
                errMsg = "No first byte in stFron at offset %d" % (self.offset)
                Fronimo.print_error(self.stFile, errMsg)
                return False
            word1 = ord(byte1)
            byte2 = self._get(1)
            if byte2 == "":
                errMsg = "No second byte in stFron at offset %d" % (self.offset)
                Fronimo.print_error(self.stFile, errMsg)
                return False
            # second byte is higher order
            word2 = 256 * ord(byte2)
            # Length of actual string + 3 bytes to specify the length
            length = word1 + word2 + 3
            # reset offset to compensate for 3 _get(1)'s
            self.offset -= 3
        else:
            # Length of actual string + 1 byte to specify the length
            length = ord(ch) + 1
            # reset offset to compensate for 1 _get(1)
            self.offset -= 1
        # find length of new string
        newLen = len(stNew)
        if newLen < 255:
            stInsert = chr(newLen)
            # We will add one length indicator to the head of the string
            newLen += 1
        else:
            stInsert = chr(255) + chr(newLen % 256) + chr(int(newLen / 256))
            # We will add three length indicators to the head of the string
            newLen += 3
        # Add leading length indicator
        stNew = stInsert + stNew
        # splice in the new string
        stOut = self.stFron[:self.offset] + stNew + self.stFron[self.offset + length:]
        # set new offset
        self.offset += newLen
        self.stFron = stOut
        return True
# end _put
        

    def latin2rtf(self, stIn):
        stOut = ""
        for i in range (len(stIn)):
            s = stIn[i]
            if ord(s) >= 127:
                # hex value of latin1 char --> last 2 chars of rtf code
                hexchars = hex(ord(s))
                hexchars = hexchars.replace('0x', '')
                s = "\\\'" + hexchars
            elif s == '|':
                s = '\\par\r\n'
            stOut += s
        return RTFPREFIX + stOut + RTFSUFFIX


    def replace_rtf_codes(self, stIn):
        accentLoc = stIn.find('\\\'')
        while accentLoc >= 0:
            # Get last 2 characters of string \'xx = hex char value
            rtfCode = stIn[accentLoc + 2:accentLoc + 4]
            # convert to latin1 character
            latin1 = chr(int(rtfCode, 16))
            if latin1:
                stIn = stIn.replace(rtfCode, latin1, 1)
            stIn = stIn.replace("\\'", '', 1)
            accentLoc = stIn.find('\\\'')
        stIn = stIn.replace('\\par', '|')
        stIn = stIn.replace('\\cf1', '')
        stIn = stIn.replace('\\cf0', '')
        stIn = re.sub(r'\\lang[0-9]*', '', stIn)
        stIn = stIn.replace('  ', ' ')
        # KLUDGE to handle unicode weirdness
        val = reUnicode.search(stIn)
        if val:
            uni = val.group(2)
            # lop off final character from unicode sequence
            stIn = stIn.replace(uni, uni[:-1])
        return(stIn)

    def rtf2latin(self, stRtf):
        if stRtf.find('{\\rtf', 0) != 0:
            return stRtf
        if len(stRtf) < 100:
            return stRtf
        start = stRtf.find('\\f0\\fs', 0)
        if start == -1:
            return stRtf
        start += 9
        end = stRtf.find('\\par\r\n}\r\n', start + 1)
        if end == -1:
            return stRtf
        stOut = stRtf[start:end]
        stOut = stOut.replace('\\par\r\n', '\n')
        stOut = stOut.replace('\\{', '{')
        stOut = stOut.replace('\\}', '}')
        # KLUDGE to get rid of \langxxxx peculiarity that sometimes shows up
        # Might want to reinstate this if we can figure out what it means
        val = reDeLang.search(stOut)
        if val:
            lang1234 = val.group(1)
            stOut = stOut.replace(lang1234, "")
        stOut = stOut.strip(' \t')
        stOut = self.replace_rtf_codes(stOut)
        return stOut

    def get_composer0(self):
        if self.subtitle == '':
            return ''
        #Leave subtitle intact but mine for composer0
        pcont = reParenContents.search(self.subtitle)
        if pcont:
            inParen = pcont.group(2)
            hyphenIndex = inParen.find(' - ')
            if  hyphenIndex != -1:
                inParen = inParen[hyphenIndex + 3:]
        else:
            return ''
        return inParen

    def parse_document(self, doc):
        if doc == "":
            Fronimo.print_error(self.stFile, "No document")
            return False
        val = reGetDate.search(doc)
        # We have a date
        if val:
            self.date = val.group(1)
            self.date = self.date.strip()
            val = reApproxDate.search(self.date)
            if val:
                caMark = val.group(1)
                if caMark != "":
                    self.date = self.date.replace(caMark, "")
                self.date = "c." + self.date
            val = re.search('\(\>?c?a?\.? ?[0-9]{4}\), *([fp#%]{1,2}\.* *[^.]*)\.?$',
                   doc, re.DOTALL)
            if val:
                self.page = val.group(1)
                self.page = self.page.replace(" ", "")
            else:
                self.page = ""
                # this is not really an error, per se.
                # Fronimo.print_error(self.stFile, "No page # in %s" % doc)
        else:
            self.date = ""
            Fronimo.print_error(self.stFile, "Cannot get date from %s" % doc)
            # Look for a page anyway (unlikely)
            val = re.search(', *([fp#%]{1,2}\.* *.*)\.$', doc, re.DOTALL)
            if val:
                self.page = val.group(1)
            else:
                self.page = ""
                Fronimo.print_error(self.stFile, "No page # in %s" % doc)
        # Get document without date and page
        val = re.search(' ?\(\>?c?a?\.? ?[12][0-9]{3}\)', doc)
        if val:
            end = val.span()[0]
            self.document = doc[:end]
        else:
            self.document = doc
        self.document = self.document.strip()
        val = re.search("([^,][^,]*), *v[. ] *([^,]+)", self.document)
        if val == None:
            self.volume = ''
        else:
            self.document = val.group(1)
            self.volume = val.group(2)
        return True
    #end of parse document

    def parse_credits(self, cred):
        # expand abbreviations [Ee]d. [Ee]nc, and &
        cred = cred.replace(' & ', ' and ')
        cred = re.sub('\.$', '', cred)
        if reEd.search(cred):
            cred = cred.replace('d.', 'dited', 1)
        if reEnc.search(cred):
            cred = cred.replace('nc.', 'ncoded', 1)
        # Change [IiEe]ntabulated to Encoded
        val = reEntabulated.search(cred)
        if val:
            src = val.group(1)
            cred = cred.replace(src, "Encoded")
        #Handle "by", "and", and  ";" in credits string
        val = re.search('[Ee](?:dited|ncoded) and [Ee](?:dited|ncoded) by (..*)$', cred, re.DOTALL)
        if val:
            if val.group(1) == 'S.Gerbode':
                self.editor = self.encoder = 'Sarge Gerbode'
            else:
                self.encoder = val.group(1)
                self.encoder = self.encoder.replace('S.Gerbode','Sarge Gerbode')
                self.editor = self.encoder
            return True
        val = re.search('(E(?:ncoded|dited)) by (..+) ?(?:[;.]|and) ([Ee](?:dited|ncoded)) by (..*)$', cred, re.DOTALL)
        if val:
            type1 = val.group(1)
            cred1 = val.group(2)
            type2 = val.group(3)
            cred2 = val.group(4)
            if type1 == "Edited":
                self.editor = cred1.strip()
                self.encoder= cred2.strip()
            else:
                self.encoder = cred1.strip()
                self.editor = cred2.strip()
            self.editor = self.editor.replace('S.Gerbode', 'Sarge Gerbode')
            self.encoder = self.encoder.replace('S.Gerbode', 'Sarge Gerbode')
            return True
        self.editor = self.encoder = ""
        return False
    # End parse_credits

    def parse_footnote(self):
        self.source = self.document =  self.volume = self.date = self.page = self.encoder = self.editor = ""
        if self.footnote == "":
            return
        lsParts = re.split('  +', self.footnote)
        numParts = len(lsParts)
        if numParts < 2 or numParts > 3:
            stErr = "Footnote \"%s\" has wrong # of parts (%d)" % (self.footnote, numParts)
            Fronimo.print_error(self.stFile, stErr)
            return False
        if numParts == 2:
           # source == ''; later, source will = composer
           doc, cred = lsParts
        else:
            self.source, doc, cred = lsParts
#            return False
        if not self.parse_document(doc):
            Fronimo.print_error(self.stFile, "Cannot parse document:%s" % doc)
            return False
        if not self.parse_credits(cred):
            Fronimo.print_error(self.stFile, "Cannot parse credits: %s." % cred)
        return True
    # End parse_footnote

    def parse_info(self):
        # initialize with existing values
        global instCount
        global partCount
        lsInfo = re.split('\n', self.info)
        isRemark = False
        remarks = ""
        for datum in lsInfo:
            datum = datum.strip()
            # Ignore blank lnes
            if datum == "":
                continue
            if isRemark:
                if remarks:
                    remarks = remarks + '|' + datum 
                else:
                    remarks = datum
            else:
                isRemark = (datum.find('--') == 0)
                if isRemark:
                # everything after line stating with '--' is a remark
                    continue
                if datum.find(':') == -1:
                    continue
                # so it is a field
                # decode it
                reItem = reInfo.search(datum)
                if reItem:
                    field = reItem.group(1)
                    value = reItem.group(2)
                    value = value.strip()
                    field = field.lower()
                    if field in ["tra", "rea", "arr"]:
                        self.arranger = value
                    elif field in ["lib", "sou", "pub" ]:
                        self.source = value
                    elif field in["ins", "ens" ]:
                        self.ensemble = value
                    elif field in ["doc"]:
                        self.document = value
                    elif field in ["ori", "co0"]:
                        self.composer0 = value
                    elif field == "tit":
                        self.title = value
                    elif field == "sub":
                        self.subtitle = value
                    elif field == "com":
                        self.composer = value
                    elif field == "doc":
                        self.document = value
                    elif field == "pag":
                        self.page = value
                    elif field == "edi":
                        self.editor = value
                    elif field == "enc":
                        self.encoder = value
                    elif field == "int":
                        self.intabulator = value
                    elif field == "con":
                        self.concordances = value
                    elif field == "ctr":
                        self.contributor = value
                    elif field == "pie":
                        self.piece = value
                    elif field == "fac":
                        self.facurl = value
                    elif field == "rec":
                        self.recurl = value
                    elif field == "sec":
                        self.section = value
                    elif field == "typ":
                        self.type = value
                    elif field == "key":
                        self.key = value
                    elif field == "dif":
                        self.difficulty = self.get_diff_val(value)
                    elif field == "par":
                        self.part = value
                    else:
                        stErr = "Info field \"%s\ ""not found" %(field)
                        Fronimo.print_error(self.stFile, stErr)
                else:
                    continue
        self.remarks = remarks

#       checking to see of all parts list items are in the ensemble list.
#       first collect all items in the ensemble, including tags
        ensList = self.ensemble.split(",")
        insList = []
        ensItems = []
        for ens in ensList:
            ens = ens.strip()
            ens = ens.lower()
            if ens.find(":") > 0:
                instag = ens.split(":")
#           Include tags for part check but not for instrument check
                ensItems.append(instag[0].strip())
                ensItems.append(instag[1].strip())
                insList.append(instag[1].strip())
            else:
                insList.append(ens)
                ensItems.append(ens)
#       Then check part list item against them
        if self.part != "":
            partList = self.part.split(",")
            for ins in partList:
                ins = ins.strip()
                ins = ins.lower()
                if ins == "score":
                    continue
                if not ins in ensItems:
                    stErr = "Part \"%s\" not in ensemble list" % (ins)
                    partCount += 1
                    Fronimo.print_error(self.stFile, stErr)
        for ins in insList:
            found = False
            ins = ins.strip()
            ins = ins.lower()
            for item in Fronimo.instList:
#               if item[0] == ins:
                if item[1] == ins:
                    found = True
                    break
            if not found:
                instCount += 1
                stErr = "Inst. \"%s\" not found in \"%s\"." % (ins, self.stFile)
                print(stErr, file=Fronimo.flNewInsts)
                Fronimo.flNewInsts.flush()
        # end of for datum in lsInfo
        return True
# end of parse_info
    # starts with an uncompressed fronimo string (stFron) and reads values into
    # an instance of a fronimo class object.
    def populate(self):
        global facCount
        #Assume we can parse footnote unless proven otherwise.
        fFootnote = True
        self.offset = self.start_offset
        pg = self._getBstr() # Skip page number string
        self.footnote = self._getBstr()
        self.footnote = self.footnote.strip()
        if not self.parse_footnote():
            Fronimo.print_error(self.stFile,
            "Cannot parse footnote:\n%s" % self.footnote) 
            fFootnote = False
        newPos = self.stFron.find('CPiece')
        # This is very unlikely to happen
        if newPos == -1:
            Fronimo.print_error(self.stFile, "Cannot find pattern 'CPiece'")
            return False
        self.offset = newPos + 14
        self.title = self._getBstr()
        self.title = self.title.strip(' \t')
        self.title = self.rtf2latin(self.title)
        self.subtitle = self._getBstr()
        self.subtitle = self.subtitle.strip('[\t ]')
        self.subtitle = self.rtf2latin(self.subtitle)
        self.subtitle = self.subtitle.replace('\n', '|')
        self.composer0 = self.get_composer0()
        self.composer = self._getBstr()
        self.composer = self.composer.strip('[\t ]')
        self.composer = self.rtf2latin(self.composer)
        if self.source == "":
            self.source = self.composer.replace('?', '')
        if self.source == 'Anonymous':
            self.source = 'Unknown'
        self._getBstr() # Discard text at beginning and end of section
        self._getBstr()
        self.info = self._getBstr()
        self.info = self.info.strip()
        self.info = self.info.replace ('\r', '')
        # Skip key field; will get key from info field
        # offset = stFron.find('CBAR',offset) + 10
        # keyNum = self._getWord(True)
        self.composer = self.composer.replace('Anon.', 'Anonymous')
        self.composer0 = self.composer0.replace('Anon.', 'Anonymous')
        #test to see if facsimile file exists
        #only if footnote parse ws OK.
        if fFootnote:
            self.facurl = self.get_facurl()
            if not self.facurl:
                facCount += 1
        if self.info:
            if not self.parse_info():
                Fronimo.print_error(self.stFile, "Cannot parse info string.")
        else:
            Fronimo.print_error(self.stFile, "No info data.")
        self.mtime = int(os.path.getmtime(self.stFile))
        self.ctime = int(os.path.getctime(self.stFile))
        self.stMidi = self.find_file("midi")
        self.stMidi = self.stMidi.replace(LOCALBASE, self.base, 1)
        self.stPdf = self.find_file("pdf")
        self.stPdf = self.stPdf.replace(LOCALBASE, self.base, 1)
        return True
# End Populate
    
    def find_file(self, type):
        fronDir = os.path.dirname(self.stFile)
        fronDir = fronDir.replace('\\', '/')
        fronType = os.path.basename(self.stFile)
        fronType = fronType.replace('\\', '/')
        if type == "midi":
            fronType= fronType.replace('.ft3', '.mid')
        elif type == 'pdf':
            fronType = fronType.replace('.ft3', '.pdf')
        else:
            fronType = fronType.replace('.ft3', '.tab')
        typeDir = join_with_fslash(fronDir, type)
        stFl = join_with_fslash(typeDir, fronType)
        if not os.path.exists(stFl):
            return("")
        else:
            stFl.replace('\\', '/')
            return(stFl)
# End find_file

# Takes data from a fronimo class instance and inserts it into existing stFron
    def depopulate(self):
        self.offset = self.start_offset
        # throw away page number string and update offset
        pg = self._getBstr()
        self._put(self.footnote)
        newPos = self.stFron.find('CPiece')
        # This is very unlikely to happen
        if newPos == -1:
            Fronimo.print_error(self.stFile, "Cannot find pattern 'CPiece'")
            return False
        self.offset = newPos + 14
        title = self.latin2rtf(self.title)
        self._put(title)
        self.subtitle = self.subtitle.replace('\|', '\r\n')
        self.subtitle = self.latin2rtf(self.subtitle)
        self._put(self.subtitle)
        composer = self.latin2rtf(self.composer)
        self._put(composer)
        # Discard text at beginning and end of section
        self._getBstr()
        self._getBstr()
        info = self.info
        info = info.replace('|', '\r\n')
        self._put(info)
        return True
# end depopulate

    # If from a template, prompts where to write it to.
    # If from an existing file, writes to that file
    # Maybe with a backup file, until all is known to be kosher
    def write_file(self, fPrompt):
        flOut = None
        btFron = bytes(self.stFron, 'latin1')
        if not fPrompt:
            stInput = self.stFile
        else:
            if self.stFile == TEMPLATE:
                stFlOut = ""
            else:
                stFlOut = self.stFile
            while flOut == None:
                if stFlOut:
                    stInput = input("Write file to (%s): " % stFlOut)
                    if stInput == "":
                        stInput = stFlOut
                    if stInput[-4:] != ".ft3":
                        stInput += '.ft3'
                else:
                    stInput = ""
                    while stInput == "":
                        stInput = input("Write file to: ")
                    if stInput[-4:] != ".ft3":
                        stInput += '.ft3'
        flOut = gzip.open(stInput, "wb")
        if flOut.write(btFron) != len(btFron):
            return False
        return True

    def make_changes(self, form):
        self.title = form.title.data
        self.subtitle = form.subtitle.data
        self.section = form.section.data
        self.composer = form.composer.data
        self.composer0 = form.orig_composer.data
        self.ensemble = form.ensemble.data
        self.mkey = form.mkey.data
        self.difficulty = form.difficulty.data
        self.mtype = form.mtype.data
        self.Source = form.source.data
        self.document = form.document.data
        self.volume = form.volume.data
        self.page = form.page.data
        self.editor = form.editor.data
        self.encoder = form.encoder.data
        self.arranger = form.arranger.data
        self.intabulator = form.intabulator.data
        self.contributor = form.contributor.data
        self.remarks = form.remarks.data
        self.part = form.part.data
        self.footnote = self.make_footnote()
        self.info = self.make_info()

    def make_info(self):
#        diDifficulty = {0: 'beginner', 1: 'easy', 2: 'medium', 3: 'challenging', 4: 'hard', 5: 'virtuoso'}
        stInfo = 'key:' + self.mkey + '\n' + 'type:' + self.mtype + \
                'dif:' + '\n' + self.difficulty + '\n' + \
                'ens:' + '\n' + self.ensemble
        if self.section:
            stInfo = stInfo + '\n' + 'section:' +  self.section
        if self.part:
            stInfo = stInfo + '\n' + 'part:' +  self.part
        if self.concordances:
            stInfo = stInfo + '\n' + 'con:' + self.concordances
        if self.recurl:
            stInfo = stInfo + '\n' + 'rec:' + self.recurl
        if self.contributor:
            stInfo = stInfo + '\n' + 'ctr:' + self.contributor
        if self.remarks:
            stInfo = stInfo + '\n' + '--' + '\n' + self.remarks 
        return stInfo

    def make_footnote(self):
        if self.source != self.composer:
            stFootnote = self.source + '  ' 
        stFootnote = stFootnote + self.document + ', '
        if self.volume >= 1:
            stFootnote = stFootnote + 'v.' + str(self.volume) + ', '
        stFootnote = stFootnote + self.page + '.  '
        if self.intabulator == self.editor:
            stFootnote = stFootnote + 'Encoded and edited by ' + self.editor
        else:
            stFootnote = stFootnote + 'Encoded by ' + self.encoder +\
                    ' and edited by ' + 'self. editor'
        stFootnote = stFootnote + '.'
        return stFootnote

# end of write_file
# end of class Fronimo

def process_form(form):
    if not Fronimo.open_error(ERRFILE):
        print("Cannot open error file.")
        return
    stIn = form.inputfile.data
    #Read in template file into a string and populate Fronimo object
    print('B4 fronimo = Fronimo(stIn), Fronimo.stFile = ', Fronimo.stFile)
    input()
    fronimo = Fronimo(stIn)
    print('af fronimo = Fronimo(stIn), fronimo.stFile = ', fronimo.stFile)
    input()
    #make changes to fronimo class from form
    fronimo.make_changes(form)
    #depopulate fronimo class into a string
    fronimo.write_file("Write to:")
    #compress string and write to output fronimo file in contributor's directory

