

class MetaDataContainer:

    def __init__(self):
        pass
        self.metaHeader = list();
        self.metaData = list();
        self.headerLookup = dict();
        self.syncLookup = dict();

    def getValueByName(self, entry, name):
        idx = self.headerLookup[name];
        return entry[idx];

    def getIndexByLookupFieldValue(self, value):

        try:
            index = self.syncLookup[value];



        except KeyError:
            return None
        return index;

    def getMetaDataByLookupFieldValue(self, value):

        index = self.syncLookup[value];
        if (index is not None):
            return self.metaData[index];


    def prepareSyncLookup(self, syncField):
        self.syncLookup = dict()
        self.syncField = syncField;

        for index, line in enumerate(self.metaData):
            #line = self.metaData[index];

            # prepare syncField speedup
            if self.syncField is not None:
                syncColNum = int(self.headerLookup[self.syncField]);
                #print syncColNum
                self.syncLookup[line[syncColNum]] = index;


    def loadFromFile(self, file):
        # only csv supported
        if (not file.strip().endswith(".csv")):
            # check first to avoid double onChange callback call
            if (nuke.thisNode().knob(KNOBNAME_METADATAFILE).value() != ""):
                nuke.message("This is not a .csv file!");
                nuke.thisNode().knob(KNOBNAME_METADATAFILE).setValue("")
            return;

        f = open(file, "r");
        content = f.readlines();
        for linenumber, line in enumerate(content):
            # line = content[linenumber];
            #print line
            if (linenumber == 0):
                self.metaHeader = line.split("\t");

                # store the column number for each name
                for col, data in enumerate(self.metaHeader):
                    self.headerLookup[data] = int(col);

                continue;

            dataColumns = line.split("\t");

            self.metaData.append(dataColumns);


            #dataEntry = dict();
            #for col, data in enumerate(metaHeader):
            #    # print col
            #    # key = header[col];
            #    dataEntry[data] = dataColumns[col];
            #self.metaData.append(dataEntry);


        #print self.metaData
import nuke
import threading


nodeTemplate = """
Group {
 name MetaDataFromFile
 addUserKnob {20 properties l Properties}
 addUserKnob {2 metadatafile l "Metadata File" t "CSV Metadata file"}
 addUserKnob {1 prefix l "Key Prefix" t}
 prefix "ext/"
 addUserKnob {3 offset l "Offset"}
 addUserKnob  {26 ""}
 addUserKnob {22 btn_sync l "Synchronize" T "metaDataFromFile.synchronize()"}
 addUserKnob {4 srcfield l "Sync Input Field" M {"No Sync" ""}}
 addUserKnob {4 targetfield l "Sync External Field" M {"No Sync" ""}}


 metadatafile ""
 knobChanged "metaDataFromFile.userKnobChanged()"
 updateUI "metaDataFromFile.updateUI()"
}
 Input {
  inputs 0
  name Input1
  xpos 0
 }
 ModifyMetaData {
  metadata {
  }
  name ModifyMetaData
  xpos 0
  ypos 150
 }
 Output {
  name Output1
  xpos 0
  ypos 300
 }

end_group
"""




metaDataLineTemplate = """{set "%s%s" "\[python metaDataFromFile.getMetaField(%s)]"}"""

userKnobTemplate = "addUserKnob {1 %s}"

KNOBNAME_METADATAFILE = "metadatafile"
KNOBNAME_OFFSET ="offset"
KNOBNAME_SRCFIELD = "srcfield"
KNOBNAME_TARGETFIELD = "targetfield"
KNOBNAME_PREFIX = "prefix"

def loadMetadataFile(node, file):
    meta = MetaDataContainer();
    meta.loadFromFile(file);

    metaKeys = list();

    prefix = node.knob(KNOBNAME_PREFIX).value()

    # set values for each frame
    for num, key in enumerate(meta.metaHeader):
        pass
        metaKeys.append(metaDataLineTemplate % (prefix.strip(), key, int(num)))


    # get the metadata node
    mmd = nuke.allNodes(group=nuke.thisGroup(), filter="ModifyMetaData")[0]
    mmd["metadata"].fromScript("\n".join(metaKeys));

    # fill src/target fields
    if node.input(0) is not None:
        srcFields = node.input(0).metadata().keys()
        node.knob(KNOBNAME_SRCFIELD).setValues(srcFields);

    targetFields = meta.metaHeader;
    node.knob(KNOBNAME_TARGETFIELD).setValues(targetFields);

    return meta

import linecache

def getMetaField(column, offset = None):
    try:
        if (offset is None):
            offset = int(nuke.thisParent().knob(KNOBNAME_OFFSET).value());

        # add 1, the first line is the header
        idx = int(nuke.frame())- offset + 1

        file = getMetaFile()

        return linecache.getline(file,int(idx)).split("\t")[column].strip()
    except Exception as e:
        return "metadata error: %s" %e


def getMetaFile():
    return nuke.thisParent().knob(KNOBNAME_METADATAFILE).value();

def updateUI():
    pass

def userKnobChanged():
    if ((nuke.thisKnob().name() == KNOBNAME_METADATAFILE) or
        (nuke.thisKnob().name() == "inputChange") or
            (nuke.thisKnob().name() == KNOBNAME_PREFIX)) :
        loadMetadataFile(nuke.thisNode(), nuke.thisNode().knob(KNOBNAME_METADATAFILE).value());




def createNode():
    from PySide2 import QtWidgets
    QtWidgets.QApplication.clipboard().setText(nodeTemplate)
    newNode = nuke.nodePaste("%clipboard%")
    #nuke.addKnobChanged(userKnobChanged, node=newNode)
    pass

def synchronize():

    meta = loadMetadataFile(nuke.thisNode(),nuke.thisNode().knob(KNOBNAME_METADATAFILE).value() );

    srcField = nuke.thisNode().knob(KNOBNAME_SRCFIELD).value();
    targetField = nuke.thisNode().knob(KNOBNAME_TARGETFIELD).value();

    meta.prepareSyncLookup(targetField);

    # search for a matching value at the current frame
    metaValue = nuke.thisNode().metadata(srcField)
    index = nuke.frame()-meta.getIndexByLookupFieldValue(metaValue) - 1;


    if (index is None):
        nuke.message("Cannot synchronize: meta data value '%s' (%s) not found in external field '%s'" % (metaValue, srcField, targetField))
        return

    nuke.thisNode().knob(KNOBNAME_OFFSET).setValue(index);




if __name__ == "__main__":
    load();