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