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.MSInfo;
016import armyc2.c5isr.renderer.utilities.SymbolID;
017
018public class TreeManager {
019    private static final Set<String> SYMBOL_BLACKLIST = new HashSet<>(Arrays.asList(
020            // Symbols with no SVG or drawing in standard
021            "25342900", // Advance to contact
022            "25343000", // Capture
023            "25343100", // Conduct Exploitation
024            "25343200", // Control
025            "25343300", // Demonstration
026            "25343400", // Deny
027            "25343500", // Envelop
028            "25343600", // Escort
029            "25343700", // Exfiltrate
030            "25343800", // Infiltrate
031            "25343900", // Locate
032            "25350000", // Space debris
033            "25350100", "25350101", "25350102", "25350103", // Man made space debris
034            "25350200", "25350201", "25350202", "25350203", // Natural space debris
035            "46120313", // Hydrography Ports and Harbors Facilities
036            "46120301", // Hydrography Ports and Harbors Ports
037            "46120325", // Hydrography Ports and Harbors Shoreline Protection
038            "46120400", // Hydrography Aids to Navigation
039            "47", // Meteorological space
040
041            // Symbols with drawing in standard but no SVG
042            "10163601", // Floating Craft
043
044            // Symbols with ambiguous draw rules
045            "45162004" // Tropical Storm Wind Areas
046    ));
047
048    public Node mil2525Tree;
049
050    /** Reads the symbols from msd.txt and builds a tree.
051     * @param context Application context in which to use the tree.
052     * @param versions mil std 2525 versions to add to tree
053     * @throws IOException if there is an error reading msd.txt
054     */
055    public void buildTree(Context context, int[] versions) throws IOException {
056        mil2525Tree = new Node("Root", "XX", "XX", "XX");
057        for (int version : versions){
058            addToTree(context, version);
059        }
060    }
061
062    private void addToTree(Context context, int version) throws IOException {
063        Stack<Node> parentStack = new Stack<>();
064        Node child = mil2525Tree;
065        String line;
066        String symbolSet = "";
067
068        InputStream is;
069        if (version >= SymbolID.Version_2525E) {
070            is = context.getResources().openRawResource(R.raw.mse);
071        } else {
072            is = context.getResources().openRawResource(R.raw.msd);
073        }
074        BufferedReader br = new BufferedReader(new InputStreamReader(is));
075
076        try {
077            while ((line = br.readLine()) != null) {
078                if (line.split("\t")[5].contains(String.valueOf(version))) {
079                    // count tabs to calculate nodeDepth
080                    int nodeDepth = 1;
081                    while (line.charAt(0) == '\t') {
082                        line = line.substring(1);
083                        nodeDepth++;
084                    }
085
086                    if (nodeDepth > parentStack.size()) {
087                        parentStack.push(child);
088                    }
089                    while (nodeDepth < parentStack.size()) {
090                        parentStack.pop();
091                    }
092
093                    // special case for parsing the Symbol Set codes since they're only 2 digits
094                    if (nodeDepth == 1) {
095                        String[] segments = line.split("\\t");
096                        symbolSet = segments[0];
097
098                        if (SYMBOL_BLACKLIST.contains(symbolSet)) {
099                            continue;
100                        }
101
102                        child = getChild(parentStack.peek(), symbolSet, "000000");
103                        if (child == null) {
104                            child = new Node(MSInfo.parseSymbolSetName(symbolSet, version), String.valueOf(version), symbolSet, "000000");
105                            parentStack.peek().addChild(child);
106                        }
107
108                        if (segments[1].equals("Unspecified")) {
109                            // Ignore rest of line
110                            continue;
111                        } else {
112                            // There is a subfolder on this line, add parent and continue parsing
113                            parentStack.push(child);
114                        }
115                    }
116
117                    // skip "{Reserved for future use}" codes
118                    if (!line.toLowerCase().contains("{reserved for future use}")) {
119                        String[] segments = line.split("\t+");
120                        String name;
121                        if (nodeDepth == 1) {
122                            name = segments[1];
123                        } else {
124                            name = segments[0];
125                        }
126
127                        // XXXXXX would indicate an error reading the file where it couldn't find 6 digits
128                        String code = "XXXXXX";
129                        // extract 6-digit decimal code from remainder of line segments
130                        for (int i = 1; i < segments.length; i++) {
131                            if (segments[i].matches("\\d{6}")) {
132                                code = segments[i];
133                                break;
134                            }
135                        }
136
137                        if (SYMBOL_BLACKLIST.contains(symbolSet) || SYMBOL_BLACKLIST.contains(symbolSet + code)) {
138                            continue;
139                        }
140
141                        child = getChild(parentStack.peek(), symbolSet, code);
142                        if (child == null) {
143                            child = new Node(name, String.valueOf(version), symbolSet, code);
144                            parentStack.peek().addChild(child);
145                        }
146                    }
147                }
148            }
149            br.close();
150        } catch (Exception e) {
151            throw new RuntimeException(e);
152        }
153    }
154
155    private Node getChild(Node parent, String symbolSet, String entityCode) {
156        for (Node child : parent.getChildren()){
157            if (child.getSymbolSetCode().equals(symbolSet) && child.getCode().equals(entityCode))
158                return child;
159        }
160        return null;
161    }
162}