001package armyc2.c5isr.renderer.symbolpicker;
002
003import android.content.Context;
004
005import java.io.BufferedReader;
006import java.io.IOException;
007import java.io.InputStream;
008import java.io.InputStreamReader;
009import java.util.Arrays;
010import java.util.HashSet;
011import java.util.Set;
012import java.util.Stack;
013
014import armyc2.c5isr.renderer.R;
015import armyc2.c5isr.renderer.utilities.ErrorLogger;
016import armyc2.c5isr.renderer.utilities.MSInfo;
017import armyc2.c5isr.renderer.utilities.SymbolID;
018import armyc2.c5isr.renderer.utilities.SymbolUtilities;
019
020public class TreeManager {
021    private static final Set<String> SYMBOL_BLACKLIST = new HashSet<>(Arrays.asList(
022            // Symbols with no SVG or drawing in standard
023            "47", // Meteorological space is an empty Symbol Set currently
024
025            // Symbols with ambiguous draw rules
026            "45162004" // Tropical Storm Wind Areas
027    ));
028
029    public Node mil2525Tree;
030
031    /** Reads the symbols from msd.txt and builds a tree.
032     * @param context Application context in which to use the tree.
033     * @param versions mil std 2525 versions to add to tree
034     * @throws IOException if there is an error reading msd.txt
035     */
036    public void buildTree(Context context, int[] versions) throws IOException {
037        mil2525Tree = new Node("Root", "XX", "XX", "XX");
038        for (int version : versions){
039            addToTree(context, version);
040        }
041    }
042
043    private void addToTree(Context context, int version) throws IOException {
044        Stack<Node> parentStack = new Stack<>();
045        Node child = mil2525Tree;
046        String line;
047        String symbolSet = "";
048
049        InputStream is;
050        if (version >= SymbolID.Version_2525E) {
051            is = context.getResources().openRawResource(R.raw.mse);
052        } else {
053            is = context.getResources().openRawResource(R.raw.msd);
054        }
055        BufferedReader br = new BufferedReader(new InputStreamReader(is));
056
057        try {
058            while ((line = br.readLine()) != null) {
059
060                boolean newSymbolSet = false;
061                String[] segments = line.split("\\t");
062                //lookup doesn't have 13 values.  Add 13 wherever there's a 15 for now.
063                if(segments[5].contains("15")) {
064                    segments[5] = "13," + segments[5];
065
066                    line = "";
067
068                    for(int lcv = 0; lcv < segments.length; lcv++)
069                    {
070                        line += segments[lcv];
071                        if(lcv < segments.length-1)
072                            line += "\t";
073                    }
074                }
075
076                if (segments[5].contains(String.valueOf(version))) {
077                    // count tabs to calculate nodeDepth
078                    int nodeDepth = 1;
079
080                    if(SymbolUtilities.isNumber(line.substring(0,2))) {
081                        newSymbolSet = true;
082                        line = line.substring(2);
083                    }
084                    while (line.charAt(0) == '\t') {
085                        line = line.substring(1);
086                        nodeDepth++;
087                    }
088
089                    if (nodeDepth > parentStack.size()) {
090                        parentStack.push(child);
091                    }
092                    while (nodeDepth < parentStack.size()) {
093                        parentStack.pop();
094                    }
095
096                    // special case for parsing the Symbol Set codes since they're only 2 digits
097                    if(newSymbolSet){//(nodeDepth == 1 && !segments[0].isEmpty()) {
098                        symbolSet = segments[0];
099
100                        if (SYMBOL_BLACKLIST.contains(symbolSet)) {
101                            continue;
102                        }
103
104                        child = getChild(parentStack.peek(), symbolSet, "000000");
105                        if (child == null) {
106                            child = new Node(MSInfo.parseSymbolSetName(symbolSet, version), String.valueOf(version), symbolSet, "000000");
107
108                            //new symbol set so go back to the root to add it to the root
109                            while(parentStack.size() > 1)
110                                parentStack.pop();
111
112                            parentStack.peek().addChild(child);
113                        }
114
115                        if (segments[1].equals("Unspecified")) {
116                            // Ignore rest of line
117                            continue;
118                        } else {
119                            // There is a subfolder on this line, add parent and continue parsing
120                            parentStack.push(child);
121                        }
122                    }
123
124                    // skip "{Reserved for future use}" codes
125                    if (!line.toLowerCase().contains("{reserved for future use}")) {
126                        segments = line.split("\\t");
127                        String name;
128
129                        name = segments[0];
130
131                        // XXXXXX would indicate an error reading the file where it couldn't find 6 digits
132                        String code = "XXXXXX";
133                        // extract 6-digit decimal code from remainder of line segments
134                        for (int i = 1; i < segments.length; i++) {
135                            if (segments[i].matches("\\d{6}")) {
136                                code = segments[i];
137                                break;
138                            }
139                        }
140
141                        if (SYMBOL_BLACKLIST.contains(symbolSet) || SYMBOL_BLACKLIST.contains(symbolSet + code)) {
142                            continue;
143                        }
144
145                        child = getChild(parentStack.peek(), symbolSet, code);
146                        if (child == null) {
147                            child = new Node(name, String.valueOf(version), symbolSet, code);
148                            parentStack.peek().addChild(child);
149                        }
150                    }
151                }
152            }
153            br.close();
154        } catch (Exception e) {
155            throw new RuntimeException(e);
156        }
157    }
158
159    private Node getChild(Node parent, String symbolSet, String entityCode) {
160        for (Node child : parent.getChildren()){
161            if (child.getSymbolSetCode().equals(symbolSet) && child.getCode().equals(entityCode))
162                return child;
163        }
164        return null;
165    }
166}