001package armyc2.c5isr.renderer.utilities;
002
003import android.content.Context;
004import android.graphics.RectF;
005
006import java.io.BufferedReader;
007import java.io.IOException;
008import java.io.InputStream;
009import java.io.InputStreamReader;
010import java.util.ArrayList;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Map;
014import java.util.Set;
015
016
017import armyc2.c5isr.renderer.R;
018
019public class SVGLookup {
020    private static SVGLookup _instance = null;
021    private static Boolean _initCalled = false;
022    private static Map<String, SVGInfo> _SVGLookupD = null;
023    private static Map<String, SVGInfo> _SVGLookupE = null;
024    private static Map<String, SVGInfo> _SVGLookup6D = null;
025    private static Map<String, SVGInfo> _SVGLookup6E = null;
026    private String TAG = "SVGLookup";
027
028
029    /*
030     * Holds SymbolDefs for all symbols. (basicSymbolID, Description, MinPoint, MaxPoints, etc...) Call
031     * getInstance().
032     *
033     */
034    private SVGLookup() {
035        // init(null);
036        // _initCalled=true;
037    }
038
039    public static synchronized SVGLookup getInstance() {
040        if (_instance == null) {
041            _instance = new SVGLookup();
042        }
043
044        return _instance;
045    }
046
047    public final synchronized void init(Context context)
048    {
049        if (_initCalled == false)
050        {
051            _SVGLookupD = new HashMap<>();
052            _SVGLookupE = new HashMap<>();
053            _SVGLookup6D = new HashMap<>();
054            _SVGLookup6E = new HashMap<>();
055
056            try
057            {
058                InputStream isD = context.getResources().openRawResource(R.raw.svgd);
059                loadData(isD, SymbolID.Version_2525Dch1);
060                isD.close();
061
062                InputStream is6D = context.getResources().openRawResource(R.raw.svg6d);
063                loadData(is6D, SymbolID.Version_2525Dch1);
064                is6D.close();
065
066                InputStream isE = context.getResources().openRawResource(R.raw.svge);
067                loadData(isE, SymbolID.Version_2525Ech1);
068                isE.close();
069
070                InputStream is6E = context.getResources().openRawResource(R.raw.svg6e);
071                loadData(is6E, SymbolID.Version_2525E);
072                is6E.close();
073
074                _initCalled = true;
075            }
076            catch(Exception e)
077            {
078                System.out.println(e.getMessage());
079            }
080        }
081
082    }
083
084    private void loadData(InputStream is, int version)
085    {
086        String[] temp = null;
087        String id = null;
088        RectF bbox = null;
089        String svg = null;
090        String delimiter = "~";
091
092        try
093        {
094            BufferedReader br = new BufferedReader(new InputStreamReader(is));
095            String line = br.readLine();
096
097            Map<String, SVGInfo> lookup = null;
098            if(version == SymbolID.Version_2525E || version == SymbolID.Version_2525Ech1)
099                lookup = _SVGLookupE;
100            else if(version == SymbolID.Version_2525Dch1)
101                lookup = _SVGLookupD;
102            else if(version == SymbolID.Version_APP6D)// || version == SymbolID.Version_APP6Dch2)
103                lookup = _SVGLookup6D;
104            else if(version == SymbolID.Version_APP6Ech1 || version == SymbolID.Version_APP6Ech2)
105                lookup = _SVGLookup6E;
106
107            while (line != null)
108            {
109                //parse first line
110                temp = line.split(delimiter);
111                id = temp[0];
112                float left, top, width, height;
113                left = Float.parseFloat(temp[1]);
114                top = Float.parseFloat(temp[2]);
115                width = Float.parseFloat(temp[3]);
116                height = Float.parseFloat(temp[4]);
117                bbox = RectUtilities.makeRectF(left, top, width, height);
118
119                if(id.startsWith("461206"))//Hydrography Bottom Features
120                {
121                    //grow bbox as android uses a different serif italic font than browsers and windows
122                    bbox = RectUtilities.makeRectF((int)(left + (left * -0.04)), (int)(top + (top * -0.1)), (int)(width * 1.2), (int)(height * 1.1));
123                }
124                    /*if(id.equals("25130100"))
125                        Log.e("action point",id);//*/
126
127                //read 2nd line to get SVG
128                svg = br.readLine();
129
130                //increase icon stroke width for high dpi screens
131                //ldpi <=120, mdpi >= 160, hdpi >= 240, xhdpi >= 320, xxhdpi >= 480, xxxhdpi >= 640
132                /*if(svg != null && id.length() == 8 && !id.startsWith("25") && !id.startsWith("4")) {
133                    //int dpi = RendererSettings.getInstance().getDeviceDPI();
134                    svg = RendererUtilities.increaseStrokeWidth(svg, 2);
135                }//*/
136
137                if(lookup != null)
138                    lookup.put(id, new SVGInfo(id, bbox, svg));
139
140                //read next line for next loop
141                line = br.readLine();
142            }
143            _initCalled = true;
144        }
145        catch(IOException ioe)
146        {
147            System.out.println(ioe.getMessage());
148        }
149        catch(Exception e)
150        {
151            System.out.println(e.getMessage());
152        }
153    }
154
155    /**
156     *
157     * @param id
158     * @param version
159     * @return
160     */
161    public SVGInfo getSVGLInfo(String id, int version)
162    {
163        if(version == SymbolID.Version_2525E || version == SymbolID.Version_2525Ech1)
164        {
165            if (_SVGLookupE.containsKey(id))
166                return _SVGLookupE.get(id);
167        }
168        else if(version == SymbolID.Version_2525Dch1)
169        {
170            if (_SVGLookupD.containsKey(id))
171                return _SVGLookupD.get(id);
172        }
173        else if(version == SymbolID.Version_APP6Ech2 || version == SymbolID.Version_APP6Ech1)
174        {
175            if (_SVGLookup6E.containsKey(id))
176                return _SVGLookup6E.get(id);
177            else if (_SVGLookupE.containsKey(id))
178                return _SVGLookupE.get(id);
179        }
180        else if(version == SymbolID.Version_APP6D)
181        {
182            if (_SVGLookup6D.containsKey(id))
183                return _SVGLookup6D.get(id);
184            else if (_SVGLookupD.containsKey(id))
185                return _SVGLookupD.get(id);
186            else if (_SVGLookupE.containsKey(id))//Dismounted Individual
187                return _SVGLookupE.get(id);
188        }
189
190        return null;
191    }
192
193
194    public static String getFrameID(String symbolID)
195    {
196        //SIDC positions 3_456_7
197        // String frameID = symbolID.charAt(2) + "_" + symbolID.substring(3, 6) + "_" + symbolID.charAt(6);
198
199        String frameID = null;
200        String ss;
201
202        int affiliation = SymbolID.getAffiliation(symbolID);
203        if(affiliation > SymbolID.StandardIdentity_Affiliation_Hostile_Faker)
204            affiliation = SymbolID.StandardIdentity_Affiliation_Unknown;
205
206        int status = SymbolID.getStatus(symbolID);
207        //Some affiliations are always dashed and only have one SVG for status with a value of 0
208        if(affiliation == SymbolID.StandardIdentity_Affiliation_Pending ||
209                affiliation == SymbolID.StandardIdentity_Affiliation_AssumedFriend ||
210                affiliation == SymbolID.StandardIdentity_Affiliation_Suspect_Joker)
211        {
212            // "When the frame is assumed friend, suspect, or pending, the status shall not be displayed."
213            status = 0;
214        }
215        if(status > 1) // Anything above 1 for status means present for the frame
216            status = 0;
217
218        int context = SymbolID.getContext(symbolID);
219        //they didn't make duplicate frame so I have to change the number for
220        //the lookup to work.
221
222        if(SymbolID.getVersion(symbolID) < SymbolID.Version_2525E)//2525Dch1 or less
223        {
224            switch(SymbolID.getSymbolSet(symbolID))
225            {
226                case 01: //Air
227                case 02: //Air Missile
228                case 51: //Air SIGINT
229                    ss = "01";
230                    break;
231                case 05: //Space
232                case 06: //Space Missile
233                case 50: //Space SIGINT
234                    ss = "05";
235                    break;
236                case 10: //Land Unit
237                case 11://Land Civilian Unit/Org
238                    ss = "10";
239                    break;
240                case 15://Land Equipment
241                case 52://Land SigInt
242                case 53://Sea Surface SIGINT
243                    ss = "30";
244                    break;
245                case 30://Sea Surface
246                    ss = "30";
247                    if(SymbolID.getEntityCode(symbolID)==150000)
248                        return "octagon";//this symbol has no frame and a size of 1L x 1L.
249                    break;
250                case 20: //Land Installation
251                    ss = "20";
252                    break;
253                case SymbolID.SymbolSet_DismountedIndividuals: //Dismounted Individuals
254                    ss = "27";
255                    break;
256                case 35: //Sea Subsurface
257                case 36: //Mine Warfare
258                case 54: //Sea Subsurface SigInt
259                    ss = "35";
260                    break;
261                case 40: //Activities/Events
262                    ss = "40";
263                    break;
264                case 60: //Cyberspace
265                    ss = "60"; //No cyberspace SVG frame at the moment so setting to activities
266                    break;
267                default:
268                    ss = "00";
269
270                    if(context == SymbolID.StandardIdentity_Context_Exercise && affiliation > SymbolID.StandardIdentity_Affiliation_Unknown)
271                    {
272                        //really there are no unknown exercise symbols outside of pending and unknown
273                        //default to unknown
274                        affiliation = SymbolID.StandardIdentity_Affiliation_Unknown;
275                    }
276            }
277            if(context == SymbolID.StandardIdentity_Context_Restricted_Target_Reality || context == SymbolID.StandardIdentity_Context_No_Strike_Entity_Reality)
278                context = SymbolID.StandardIdentity_Context_Reality;
279            frameID = context + "_" + affiliation + ss + "_" + status;
280        }
281        else//2525E or above
282        {
283            char frameShape = SymbolID.getFrameShape(symbolID);
284            if(frameShape==SymbolID.FrameShape_Unknown)
285            {
286
287                /*if(SymbolID.getSymbolSet(symbolID) != SymbolID.SymbolSet_SignalsIntelligence)
288                {//get frame shape associated with symbol set
289                    frameShape=SymbolID.getDefaultFrameShape(symbolID);
290                }//*/
291                frameShape=SymbolID.getDefaultFrameShape(symbolID);
292                if(context == SymbolID.StandardIdentity_Context_Exercise &&
293                        SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_Unknown &&
294                        affiliation > SymbolID.StandardIdentity_Affiliation_Unknown)
295                {
296                    //really there are no unknown exercise symbols outside pending and unknown affiliations
297                    //default to unknown
298                    affiliation = SymbolID.StandardIdentity_Affiliation_Unknown;
299                }
300            }
301            if(SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_SeaSurface &&
302                    SymbolID.getEntityCode(symbolID)==150000 &&  //Own Ship
303                    (frameShape == SymbolID.FrameShape_SeaSurface || frameShape == SymbolID.FrameShape_Unknown))
304            {
305                return "octagon";
306            }
307            if(context == SymbolID.StandardIdentity_Context_Restricted_Target_Reality || context == SymbolID.StandardIdentity_Context_No_Strike_Entity_Reality)
308                context = SymbolID.StandardIdentity_Context_Reality;
309            frameID = context + "_" + affiliation + frameShape + "_" + status;
310        }
311        return frameID;
312    }
313
314    public static String getMainIconID(String symbolID)
315    {
316        //SIDC positions 5-6 + 11-16
317        String mainIconID = symbolID.substring(4, 6) + symbolID.substring(10, 16);
318        int ss = SymbolID.getSymbolSet(symbolID);
319
320        if(ss == SymbolID.SymbolSet_MineWarfare)
321        {
322            if(RendererSettings.getInstance().getSeaMineRenderMethod() == RendererSettings.SeaMineRenderMethod_ALT ||
323                    mainIconID.equals("36110600") || mainIconID.equals("36110700"))
324            {
325                mainIconID += "_a";
326            }
327        }
328        else if(ss == SymbolID.SymbolSet_LandUnit)
329        {
330            switch(SymbolID.getEntityCode(symbolID))
331            {
332                case 110501:
333                case 111000:
334                case 111001:
335                case 111002:
336                case 111003:
337                case 111004:
338                case 111005:
339                case 111500:
340                case 120100:
341                case 120300:
342                case 120400:
343                case 120401:
344                case 120402:
345                case 120501:
346                case 120502:
347                case 120601:
348                case 120801:
349                case 121100:
350                case 121101:
351                case 121102:
352                case 121103:
353                case 121104:
354                case 121105:
355                case 121106:
356                case 121107:
357                case 121300:
358                case 121301:
359                case 121302:
360                case 121303:
361                case 121304:
362                case 121802:
363                case 122100:
364                case 130100:
365                case 130101:
366                case 130102:
367                case 130103:
368                case 130200:
369                case 130302:
370                case 130303:
371                case 140102:
372                case 140103:
373                case 140104:
374                case 140105:
375                case 140702:
376                case 140703:
377                case 141702:
378                case 150504:
379                case 150800:
380                case 160200:
381                case 161200:
382                case 161300:
383                case 161400:
384                case 161700:
385                case 161800:
386                case 161900:
387                case 162000:
388                case 162100:
389                case 162200:
390                case 163400:
391                case 163700:
392                case 163800:
393                case 163900:
394                case 164000:
395                case 164100:
396                case 164200:
397                case 164300:
398                case 164400:
399                case 164500:
400                case 164600:
401                case 165000://NATO Only
402                    //do thing to append correct number
403                    mainIconID += getPostFixForIcon(symbolID);
404                    break;
405                default:
406                    break;
407            }
408
409        }
410        else if(ss == SymbolID.SymbolSet_LandEquipment)
411        {
412            switch (SymbolID.getEntityCode(symbolID))
413            {
414                case 120111:
415                    //do thing to append correct number
416                    mainIconID += getPostFixForIcon(symbolID);
417                    break;
418                default:
419                    break;
420            }
421        }
422        else if(ss == SymbolID.SymbolSet_LandInstallation)
423        {
424            switch (SymbolID.getEntityCode(symbolID))
425            {
426                case 110300:
427                case 111200:
428                case 120103:
429                case 120105:
430                case 120106:
431                case 120107:
432                case 120701:
433                case 120702:
434                    //do thing to append correct number
435                    mainIconID += getPostFixForIcon(symbolID);
436                    break;
437                default:
438                    break;
439            }
440        }
441        else if(ss == SymbolID.SymbolSet_DismountedIndividuals)
442        {
443            switch (SymbolID.getEntityCode(symbolID))
444            {
445                case 110101:
446                case 110102:
447                case 110103:
448                case 110104:
449                    //do thing to append correct number
450                    mainIconID += getPostFixForIcon(symbolID);
451                    break;
452                default:
453                    break;
454            }
455        }
456        else if(ss == SymbolID.SymbolSet_Activities)
457        {
458            switch (SymbolID.getEntityCode(symbolID))
459            {
460                case 110303:
461                case 130201:
462                case 131202:
463                case 131208:
464                    //do thing to append correct number
465                    mainIconID += getPostFixForIcon(symbolID);
466                    break;
467                default:
468                    break;
469            }
470        }
471        else if(ss == SymbolID.SymbolSet_Unknown)
472            mainIconID = "00000000";//unknown with question mark
473        else if (ss != SymbolID.SymbolSet_Air &&
474                ss != SymbolID.SymbolSet_AirMissile &&
475                ss != SymbolID.SymbolSet_Space &&
476                ss != SymbolID.SymbolSet_SpaceMissile &&
477                ss != SymbolID.SymbolSet_LandCivilianUnit_Organization &&
478                ss != SymbolID.SymbolSet_DismountedIndividuals &&
479                ss != SymbolID.SymbolSet_ControlMeasure &&
480                ss != SymbolID.SymbolSet_SeaSurface &&
481                ss != SymbolID.SymbolSet_SeaSubsurface &&
482                ss != SymbolID.SymbolSet_Atmospheric &&
483                ss != SymbolID.SymbolSet_Oceanographic &&
484                ss != SymbolID.SymbolSet_MeteorologicalSpace &&
485                ss != SymbolID.SymbolSet_SignalsIntelligence_Space &&
486                ss != SymbolID.SymbolSet_SignalsIntelligence_Air &&
487                ss != SymbolID.SymbolSet_SignalsIntelligence_Land &&
488                ss != SymbolID.SymbolSet_SignalsIntelligence_SeaSurface &&
489                ss != SymbolID.SymbolSet_SignalsIntelligence_SeaSubsurface &&
490                ss != SymbolID.SymbolSet_CyberSpace &&
491                ss != SymbolID.SymbolSet_CyberSpace_Equipment)
492        {
493            mainIconID = "98100000";//invalid symbol, inverted question mark
494        }
495
496        return mainIconID;
497    }
498
499
500    private static String getPostFixForIcon(String symbolID)
501    {
502        int aff = SymbolID.getAffiliation(symbolID);
503        String pf = "";
504        if(aff == SymbolID.StandardIdentity_Affiliation_Friend ||
505                aff == SymbolID.StandardIdentity_Affiliation_AssumedFriend)
506            pf += "_1";
507        else if(aff == SymbolID.StandardIdentity_Affiliation_Neutral)
508            pf += "_2";
509        else if(aff == SymbolID.StandardIdentity_Affiliation_Hostile_Faker ||
510                aff == SymbolID.StandardIdentity_Affiliation_Suspect_Joker)
511            pf += "_3";
512        else if(aff == SymbolID.StandardIdentity_Affiliation_Unknown ||
513                aff == SymbolID.StandardIdentity_Affiliation_Pending)
514            pf += "_0";
515
516        return pf;
517    }
518
519    public static String getMod1ID(String symbolID)
520    {
521        String mod1ID = null;
522
523
524        if((SymbolID.getVersion(symbolID)>=SymbolID.Version_2525E) && symbolID.charAt(20) !='0')
525        {//2525E with Modifier 1 Indicator set
526            mod1ID = symbolID.substring(20,21) + symbolID.substring(16, 18) + "_1";
527            if(mod1ID.equals("175_1"))
528            {
529                //TODO: as this is a common modifier, it can fit more than land units and will probably need its own function and more SVGs
530                mod1ID += getPostFixForIcon(symbolID);
531            }
532        }
533        else //2525D or no Modifier 1 Indicator set
534        {
535            //SIDC positions 5-6 + 17-18 + "1"
536            mod1ID = symbolID.substring(4, 6) + symbolID.substring(16, 18) + "1";
537
538            if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_LandUnit)
539            {
540                switch(SymbolID.getModifier1(symbolID))
541                {
542                    case 98:
543                        mod1ID += getPostFixForIcon(symbolID);
544                        break;
545                    default:
546                        break;
547                }
548            }
549        }
550        return mod1ID;
551    }
552
553    public static String getMod2ID(String symbolID)
554    {
555        String mod2ID = null;
556        if((SymbolID.getVersion(symbolID)>=SymbolID.Version_2525E) && symbolID.charAt(21) != '0')
557        {//2525E with Modifier 1 Indicator set
558            mod2ID = symbolID.substring(21,22) + symbolID.substring(18, 20) + "_2";
559            if(mod2ID.equals("131_2") || mod2ID.equals("134_2"))
560            {
561                //TODO: as this is a common modifier, it can fit more than land units and will probably need its own function and more SVGs
562                mod2ID += getPostFixForIcon(symbolID);
563            }
564        }
565        else //2525D or no Modifier 1 Indicator set
566        {
567            //SIDC positions 5-6 + 19-20 + "2"
568            mod2ID = symbolID.substring(4, 6) + symbolID.substring(18, 20) + "2";
569
570            if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_LandUnit)
571            {
572                switch(SymbolID.getModifier2(symbolID))
573                {
574                    case 60:
575                    case 62:
576                    case 84:
577                    case 89:
578                        mod2ID += getPostFixForIcon(symbolID);
579                        break;
580                    default:
581                        break;
582                }
583            }
584        }
585        return mod2ID;
586    }
587
588    public static String getEchelonAmplifier(String symbolID)
589    {
590        String amp = null;
591        int ver = SymbolID.getVersion(symbolID);
592        if (ver < SymbolID.Version_2525E)
593        {
594            amp = symbolID.charAt(3) + symbolID.substring(8,10);
595        }
596        else // >= 2525E
597        {
598            //This will eventually be different with the introduction of the frame shape modifier
599            amp = symbolID.charAt(3) + symbolID.substring(8,10);
600        }
601        return amp;
602    }
603
604    public static String getHQTFFD(String symbolID) {
605        String hqtffd = null;
606        int ver = SymbolID.getVersion(symbolID);
607        if (ver < SymbolID.Version_2525E)
608        {
609            hqtffd = symbolID.substring(3, 6) + symbolID.charAt(7);
610        }
611        else // >= 2525E
612        {
613            //This will eventually be different with the introduction of the frame shape modifier
614            hqtffd = symbolID.substring(3, 6) + symbolID.charAt(7);
615        }
616        return hqtffd;
617    }
618
619    public static String getOCA(String symbolID, boolean useSlash)
620    {
621        if(useSlash)
622        {
623            int status = SymbolID.getStatus(symbolID);
624            if(status == SymbolID.Status_Present_Damaged || status == SymbolID.Status_Present_Destroyed)
625                return String.valueOf(status);
626            else
627                return null;
628        }
629        else//get the bar
630        {
631            String oca = null;
632            int ver = SymbolID.getVersion(symbolID);
633            if(ver < SymbolID.Version_2525E)
634            {
635                oca = symbolID.substring(2,7) + "2";
636            }
637            else // >= 2525E
638            {
639                //This will eventually be different with the introduction of the frame shape modifier
640                oca = symbolID.substring(2,7) + "2";
641            }
642            return oca;
643        }
644    }
645    public static List<String> getAllKeys()
646    {
647        List<String> kl = new ArrayList();
648        Set<String> keys = _SVGLookupD.keySet();
649        for(String key: keys) {
650            //System.out.println(key);
651            kl.add(key);
652        }
653        return kl;
654    }
655
656
657    /*
658     * For use only by MilStdIconRenderer.addCustomSymbol()
659     * @param svgInfo with the basic symbol id, bounds of the SVG, SVG group
660     * @param version like SymbolID.Version_2525E
661     * @return
662     */
663    public boolean addCustomSymbol(SVGInfo svgInfo, int version)
664    {
665        boolean success = false;
666        try
667        {
668            String basicID = svgInfo.getID();
669            if (version < SymbolID.Version_2525E)
670            {
671                if(SVGLookup._SVGLookupD.containsKey(svgInfo.getID()) == false)
672                {
673                    SVGLookup._SVGLookupD.put(svgInfo.getID(),svgInfo);
674                }
675            }
676            else if (version >= SymbolID.Version_2525E)
677            {
678                if(SVGLookup._SVGLookupE.containsKey(svgInfo.getID()) == false)
679                {
680                    SVGLookup._SVGLookupE.put(svgInfo.getID(),svgInfo);
681                }
682            }
683        }
684        catch(Exception e)
685        {
686            ErrorLogger.LogException("SVGLookup","addCUstomSymbol",e);
687        }
688        return success;
689    }
690}
691
692