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        {
492            mainIconID = "98100000";//invalid symbol, inverted question mark
493        }
494
495        return mainIconID;
496    }
497
498
499    private static String getPostFixForIcon(String symbolID)
500    {
501        int aff = SymbolID.getAffiliation(symbolID);
502        String pf = "";
503        if(aff == SymbolID.StandardIdentity_Affiliation_Friend ||
504                aff == SymbolID.StandardIdentity_Affiliation_AssumedFriend)
505            pf += "_1";
506        else if(aff == SymbolID.StandardIdentity_Affiliation_Neutral)
507            pf += "_2";
508        else if(aff == SymbolID.StandardIdentity_Affiliation_Hostile_Faker ||
509                aff == SymbolID.StandardIdentity_Affiliation_Suspect_Joker)
510            pf += "_3";
511        else if(aff == SymbolID.StandardIdentity_Affiliation_Unknown ||
512                aff == SymbolID.StandardIdentity_Affiliation_Pending)
513            pf += "_0";
514
515        return pf;
516    }
517
518    public static String getMod1ID(String symbolID)
519    {
520        String mod1ID = null;
521
522
523        if((SymbolID.getVersion(symbolID)>=SymbolID.Version_2525E) && symbolID.charAt(20) !='0')
524        {//2525E with Modifier 1 Indicator set
525            mod1ID = symbolID.substring(20,21) + symbolID.substring(16, 18) + "_1";
526            if(mod1ID.equals("175_1"))
527            {
528                //TODO: as this is a common modifier, it can fit more than land units and will probably need its own function and more SVGs
529                mod1ID += getPostFixForIcon(symbolID);
530            }
531        }
532        else //2525D or no Modifier 1 Indicator set
533        {
534            //SIDC positions 5-6 + 17-18 + "1"
535            mod1ID = symbolID.substring(4, 6) + symbolID.substring(16, 18) + "1";
536
537            if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_LandUnit)
538            {
539                switch(SymbolID.getModifier1(symbolID))
540                {
541                    case 98:
542                        mod1ID += getPostFixForIcon(symbolID);
543                        break;
544                    default:
545                        break;
546                }
547            }
548        }
549        return mod1ID;
550    }
551
552    public static String getMod2ID(String symbolID)
553    {
554        String mod2ID = null;
555        if((SymbolID.getVersion(symbolID)>=SymbolID.Version_2525E) && symbolID.charAt(21) != '0')
556        {//2525E with Modifier 1 Indicator set
557            mod2ID = symbolID.substring(21,22) + symbolID.substring(18, 20) + "_2";
558            if(mod2ID.equals("131_2") || mod2ID.equals("134_2"))
559            {
560                //TODO: as this is a common modifier, it can fit more than land units and will probably need its own function and more SVGs
561                mod2ID += getPostFixForIcon(symbolID);
562            }
563        }
564        else //2525D or no Modifier 1 Indicator set
565        {
566            //SIDC positions 5-6 + 19-20 + "2"
567            mod2ID = symbolID.substring(4, 6) + symbolID.substring(18, 20) + "2";
568
569            if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_LandUnit)
570            {
571                switch(SymbolID.getModifier2(symbolID))
572                {
573                    case 60:
574                    case 62:
575                    case 84:
576                    case 89:
577                        mod2ID += getPostFixForIcon(symbolID);
578                        break;
579                    default:
580                        break;
581                }
582            }
583        }
584        return mod2ID;
585    }
586
587    public static String getEchelonAmplifier(String symbolID)
588    {
589        String amp = null;
590        int ver = SymbolID.getVersion(symbolID);
591        if (ver < SymbolID.Version_2525E)
592        {
593            amp = symbolID.charAt(3) + symbolID.substring(8,10);
594        }
595        else // >= 2525E
596        {
597            //This will eventually be different with the introduction of the frame shape modifier
598            amp = symbolID.charAt(3) + symbolID.substring(8,10);
599        }
600        return amp;
601    }
602
603    public static String getHQTFFD(String symbolID) {
604        String hqtffd = null;
605        int ver = SymbolID.getVersion(symbolID);
606        if (ver < SymbolID.Version_2525E)
607        {
608            hqtffd = symbolID.substring(3, 6) + symbolID.charAt(7);
609        }
610        else // >= 2525E
611        {
612            //This will eventually be different with the introduction of the frame shape modifier
613            hqtffd = symbolID.substring(3, 6) + symbolID.charAt(7);
614        }
615        return hqtffd;
616    }
617
618    public static String getOCA(String symbolID, boolean useSlash)
619    {
620        if(useSlash)
621        {
622            int status = SymbolID.getStatus(symbolID);
623            if(status == SymbolID.Status_Present_Damaged || status == SymbolID.Status_Present_Destroyed)
624                return String.valueOf(status);
625            else
626                return null;
627        }
628        else//get the bar
629        {
630            String oca = null;
631            int ver = SymbolID.getVersion(symbolID);
632            if(ver < SymbolID.Version_2525E)
633            {
634                oca = symbolID.substring(2,7) + "2";
635            }
636            else // >= 2525E
637            {
638                //This will eventually be different with the introduction of the frame shape modifier
639                oca = symbolID.substring(2,7) + "2";
640            }
641            return oca;
642        }
643    }
644    public static List<String> getAllKeys()
645    {
646        List<String> kl = new ArrayList();
647        Set<String> keys = _SVGLookupD.keySet();
648        for(String key: keys) {
649            //System.out.println(key);
650            kl.add(key);
651        }
652        return kl;
653    }
654
655
656    /*
657     * For use only by MilStdIconRenderer.addCustomSymbol()
658     * @param svgInfo with the basic symbol id, bounds of the SVG, SVG group
659     * @param version like SymbolID.Version_2525E
660     * @return
661     */
662    public boolean addCustomSymbol(SVGInfo svgInfo, int version)
663    {
664        boolean success = false;
665        try
666        {
667            String basicID = svgInfo.getID();
668            if (version < SymbolID.Version_2525E)
669            {
670                if(SVGLookup._SVGLookupD.containsKey(svgInfo.getID()) == false)
671                {
672                    SVGLookup._SVGLookupD.put(svgInfo.getID(),svgInfo);
673                }
674            }
675            else if (version >= SymbolID.Version_2525E)
676            {
677                if(SVGLookup._SVGLookupE.containsKey(svgInfo.getID()) == false)
678                {
679                    SVGLookup._SVGLookupE.put(svgInfo.getID(),svgInfo);
680                }
681            }
682        }
683        catch(Exception e)
684        {
685            ErrorLogger.LogException("SVGLookup","addCUstomSymbol",e);
686        }
687        return success;
688    }
689}
690
691