# Sample script showing how to loop through the Roster entries and export
# specific information to a .csv file.
#
# Now includes a simple FileChooser, information message dialogs and
# output of specific CV values
#
# Inspired by Bob Jacobsen's 'RosterLoop' and 'CsvToTurnouts'
#
# Note: this will replace any existing file
#
# Author: Matthew Harris, copyright 2010, 2016
# Part of the JMRI distribution

import jmri
import jmri.jmrit.roster
import org.apache.commons.csv
from javax.swing import JFileChooser, JOptionPane
from jmri.jmrit.symbolicprog import CvTableModel
import java
import java.awt
import java.io

# Define some default values

# Default filename - located in the same location as the Roster itself
# The script does show a pop-up 'Save As' dialog allowing this to be
# changed when executed
outFile = jmri.jmrit.roster.Roster.getDefault().getRosterLocation()+"roster.csv"
print "Default output file:", outFile

# Determine if to output the header or not
# Set to 'True' if required; 'False' if not
outputHeader = True

# Define functions for later use

# Write the header
# Make sure that the headers match the detail!!
def writeHeader(csvFile):
    # Write the header line
    # Entries from the Basic roster entry
    csvFile.print("RosterID")
    csvFile.print("RoadName")
    csvFile.print("RoadNumber")
    csvFile.print("Manufacturer")
    csvFile.print("Owner")
    csvFile.print("Model")
    csvFile.print("Address")
    csvFile.print("Is Long?")
    csvFile.print("Speed Limit")
    csvFile.print("Comment")
    csvFile.print("Decoder Family")
    csvFile.print("Decoder Model")
    csvFile.print("Decoder Comment")

    # Function labels
    for func in range(0,28+1):
        csvFile.print("Fn%02d" % func)

    # Now add some CV values
    csvFile.print("CV19")
    csvFile.print("CV7")
    csvFile.print("CV8")

    # Lastly, add some single bits from CV29
    csvFile.print("Speed Steps (CV29.1)")
    csvFile.print("DC mode (CV29.2)")

    # Notify the writer of the end of the header record
    csvFile.println();
    print "Header written"

# Write the value of the specified CV in the specified format
# If the CV doesn't exist, a blank is written
def writeCvValue(cvValue, format):
    if cvValue != None:
        return format % cvValue.getValue()
    else:
        return ""

# Write the details of each roster entry
# Make sure that the details match the header!!
def writeDetails(csvFile):
    # Get a list of matched roster entries;
    # the list of None's means match everything
    rosterlist = jmri.jmrit.roster.Roster.getDefault().matchingList(None, None, None, None, None, None, None)

    # now loop through the matched entries, outputing things
    for entry in rosterlist.toArray() :
        # Most parameters are text-based, so can be output directly
        csvFile.print(entry.getId())
        csvFile.print(entry.getRoadName())
        csvFile.print(entry.getRoadNumber())
        csvFile.print(entry.getMfg())
        csvFile.print(entry.getOwner())
        csvFile.print(entry.getModel())
        csvFile.print(entry.getDccAddress())

        # 'isLongAddress' is a boolean function so we need
        # to deal with outputting that in this way
        if entry.longAddress:
            csvFile.print("Yes")
        else:
            csvFile.print("No")

        # Max Speed is a number - we need to convert to a string
        csvFile.print("%d%%" % entry.getMaxSpeedPCT())

        csvFile.print(entry.getComment())
        csvFile.print(entry.getDecoderFamily())
        csvFile.print(entry.getDecoderModel())
        csvFile.print(entry.getDecoderComment())

        # Now output function labels
        for func in range(0,28+1):
            csvFile.print(entry.getFunctionLabel(func))

        # Finally, we deal with reading in the CV values and
        # outputing those we're interested in

        # First we need to create and populate the CV tables - remember 
        # to call the readFile method before trying to populate the CV tables
        cvTable = CvTableModel(None, None)
        entry.readFile()
        entry.loadCvModel(None, cvTable)  # no variables, just load CVs

        # Now we can grab the CV values we're interested in
        # Bear in mind that these need to be converted from
        # integers into strings so we use the 'writeCvValue'
        # function defined earlier.
        # These examples are all in decimal - if you require
        # hex, change "%d" to "0x%x" or "0x%X"
        csvFile.print(writeCvValue(cvTable.getCvByNumber("19"), "%d"))
        csvFile.print(writeCvValue(cvTable.getCvByNumber("7"), "%d"))
        csvFile.print(writeCvValue(cvTable.getCvByNumber("8"), "%d"))

        # Lastly, we deal with examining specific bits of CV29.
        #
        # First, we read CV29 and store it temporarily as we will then use
        # bitwise comparisons later.
        #
        # For the bitwise comparisons, use the following pattern to read the
        # individual bits:
        #
        #   if (cv29Value & {value}) == {value}:
        #       csvFile.print("bit set")
        #   else:
        #       csvFile.print("bit clear")
        #
        # where {value} is one of the following:
        #
        #   Pos  Bit  Value
        #   1st    0      1
        #   2nd    1      2
        #   3rd    2      4
        #   4th    3      8
        #   5th    4     16
        #   6th    5     32
        #   7th    6     64
        #   8th    7    128

        # OK, read the value of CV29
        cv29Value = 0;
        if cvTable.getCvByNumber("29") != None:
            cv29Value = cvTable.getCvByNumber("29").getValue()
        else:
            print "Did not find a CV29 value, using zero"
        
        # Now do the bitwise comparisons.
        # First example is speedsteps, which is the second bit 
        if (cv29Value & 2) == 2:
            csvFile.print("28/128 Steps")
        else:
            csvFile.print("14 Steps")

        # Second example is DC mode, which is the third bit 
        if (cv29Value & 4) == 4:
            csvFile.print("On")
        else:
            csvFile.print("Off")

        # Notify the writer of the end of this detail record
        csvFile.println()
        csvFile.flush()
        print "Entry", entry.getId(), "written"

# Now do the actual work here

# Create output file.
# Unless modified here, this will create the output file in the same
# location as the roster itself.
# Default behaviour is for this to be in the user preferences directory
fc = JFileChooser()
fc.setSelectedFile(java.io.File(outFile))
if (java.awt.GraphicsEnvironment.isHeadless()) :
    ret = JFileChooser.APPROVE_OPTION
else :
    ret = fc.showSaveDialog(None)

if ret == JFileChooser.APPROVE_OPTION:
    # We've got a valid filename
    outFile = fc.getSelectedFile().toString()
    print "Output file:", outFile
    csvFile = org.apache.commons.csv.CSVPrinter(java.io.BufferedWriter(java.io.FileWriter(outFile)),org.apache.commons.csv.CSVFormat.DEFAULT)

    # Output the header if required
    if outputHeader==True:
        writeHeader(csvFile)

    # Output details
    writeDetails(csvFile)

    # Flush the write buffer and close the file
    csvFile.flush()
    csvFile.close()
    print "Export complete"
    if (not java.awt.GraphicsEnvironment.isHeadless()) :
        JOptionPane.showMessageDialog(None,"Roster export completed","Roster export",JOptionPane.INFORMATION_MESSAGE)
else:
    print "No export"
    if (not java.awt.GraphicsEnvironment.isHeadless()) :
        JOptionPane.showMessageDialog(None,"Roster not exported","Roster export",JOptionPane.INFORMATION_MESSAGE)
