001package armyc2.c5isr.renderer;
002
003import android.content.Context;
004import android.os.Build;
005import android.util.DisplayMetrics;
006import android.util.Log;
007import android.view.WindowManager;
008
009import java.util.HashMap;
010import java.util.Map;
011import java.util.concurrent.atomic.AtomicBoolean;
012import java.util.logging.Level;
013
014import armyc2.c5isr.renderer.utilities.C2DLookup;
015import armyc2.c5isr.renderer.utilities.DrawRules;
016import armyc2.c5isr.renderer.utilities.ErrorLogger;
017import armyc2.c5isr.renderer.utilities.GENCLookup;
018import armyc2.c5isr.renderer.utilities.ImageInfo;
019import armyc2.c5isr.renderer.utilities.MSInfo;
020import armyc2.c5isr.renderer.utilities.MSLookup;
021import armyc2.c5isr.renderer.utilities.MilStdAttributes;
022import armyc2.c5isr.renderer.utilities.RendererSettings;
023import armyc2.c5isr.renderer.utilities.SVGInfo;
024import armyc2.c5isr.renderer.utilities.SVGLookup;
025import armyc2.c5isr.renderer.utilities.SVGSymbolInfo;
026import armyc2.c5isr.renderer.utilities.SectorModUtils;
027import armyc2.c5isr.renderer.utilities.SymbolID;
028import armyc2.c5isr.renderer.utilities.SymbolUtilities;
029
030/**
031 * This class is used for rendering icons that represent the single point graphics in the MilStd 2525.
032 * It can also be used for rendering icon previews for multipoint graphics.
033 */
034public class MilStdIconRenderer
035/* implements IIconRenderer */ {
036
037    private String TAG = "MilStdIconRenderer";
038
039    private static MilStdIconRenderer _instance = null;
040    private AtomicBoolean _initSuccess = new AtomicBoolean(false);
041    private SinglePointRenderer _SPR = null;
042
043    private SinglePointSVGRenderer _SPSVGR = null;
044        public static synchronized MilStdIconRenderer getInstance()
045    {
046        if (_instance == null) {
047            _instance = new MilStdIconRenderer();
048        }
049        return _instance;
050    }
051
052    /**
053     *
054     * @param context
055     */
056    public synchronized void init(Context context)// List<Typeface> fonts, List<String> xml
057    {
058        try {
059            if (!_initSuccess.get()) {
060
061                //test SVGLookup////////////////////////////////////////////////////////////////////
062                SVGLookup.getInstance().init(context);
063                /*SVGInfo oct = SVGLookup.getInstance().getSVGLInfo("octagon");
064                System.out.println(oct.toString());//*/
065
066                //test MSLookup/////////////////////////////////////////////////////////////////////
067                MSLookup.getInstance().init(context);
068
069                /*MSInfo msi = MSLookup.getInstance().getMSLInfo("50110100",0);//
070                msi = MSLookup.getInstance().getMSLInfo("36190100",0);//"Non-Mine Mine–Like Object, Bottom"
071                System.out.println(msi.getPath());
072                System.out.println(msi.getName());
073                msi = MSLookup.getInstance().getMSLInfo("01110300",0);//"Unmanned Aircraft (UA) / Unmanned Aerial Vehicle (UAV) / Unmanned Aircraft System (UAS) / Remotely Piloted Vehicle (RPV)"
074                System.out.println(msi.getPath());
075                System.out.println(msi.getName());//*/
076
077                //https://stackoverflow.com/questions/3166501/getting-the-screen-density-programmatically-in-android
078                DisplayMetrics dm = new DisplayMetrics();
079                WindowManager manager;
080                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
081                    manager = context.getSystemService(WindowManager.class);//.getDefaultDisplay().getRealMetrics(dm);
082                }
083                else
084                {
085                    manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
086                }
087                manager.getDefaultDisplay().getRealMetrics(dm);
088
089                RendererSettings.getInstance().setDeviceDPI(dm.densityDpi);
090                RendererSettings.getInstance().setDeviceHeight(dm.heightPixels);
091                RendererSettings.getInstance().setDeviceWidth(dm.widthPixels);
092
093                //Country Codes
094                GENCLookup.getInstance().init(context);
095
096                //SectorMod lookups
097                SectorModUtils.getInstance().init(context);
098
099                //C2DLookup (2525C -> 2525Dch1 code conversion)
100                C2DLookup.getInstance().init(context);
101
102                // setup single point renderer
103                _SPR = SinglePointRenderer.getInstance();
104                _SPSVGR = SinglePointSVGRenderer.getInstance();
105
106                _initSuccess.set(true);
107            }
108
109        } catch (Exception exc) {
110            Log.e(TAG, exc.getMessage(), exc);
111        }
112    }
113
114    public synchronized boolean isReady()
115    {
116        return _initSuccess.get();
117    }
118
119    // @Override
120
121    /**
122     * Checks symbol codes and returns whether they can be rendered.
123     * For multi-point graphics, modifiers are ignored because we don't need that
124     * information to show preview icons in the SymbolPicker.
125     *
126     * @param symbolID 20-30 digit 2525D Symbol ID Code
127     * @param attributes (currently unused)
128     * @return true if the basic form of the graphic can be rendered
129     */
130    public Boolean CanRender(String symbolID, Map<String,String> attributes)
131    {
132        String message = "";
133        try {
134            // Extract 8-digit ID to use with SVGLookup.
135            // MSLookup can handle long codes, but SVGLookup can't because it also takes other strings.
136            String lookupID = SymbolUtilities.getBasicSymbolID(symbolID);
137            String lookupSVGID = SVGLookup.getMainIconID(symbolID);
138
139            // Renderer only supports 2525D at the moment. 2525E will be in the future.
140            /*
141            int symStd = -1;
142            int version = SymbolID.getVersion(symbolID);
143            //SymbolID.Version_2525Dch1
144            //SymbolID.Version_2525E
145            */
146
147            MSInfo msi = MSLookup.getInstance().getMSLInfo(symbolID);
148            if (msi == null)
149            {
150                if(SymbolID.getEntityCode(symbolID)==0)
151                {
152                    switch (SymbolID.getSymbolSet(symbolID))
153                    {
154                        case SymbolID.SymbolSet_Air:
155                        case SymbolID.SymbolSet_AirMissile:
156                        case SymbolID.SymbolSet_Space:
157                        case SymbolID.SymbolSet_SpaceMissile:
158                        case SymbolID.SymbolSet_LandUnit:
159                        case SymbolID.SymbolSet_LandCivilianUnit_Organization:
160                        case SymbolID.SymbolSet_LandEquipment:
161                        //case SymbolID.SymbolSet_ControlMeasure:
162                        case SymbolID.SymbolSet_DismountedIndividuals:
163                        case SymbolID.SymbolSet_SeaSurface:
164                        case SymbolID.SymbolSet_SeaSubsurface:
165                        case SymbolID.SymbolSet_MineWarfare:
166                        case SymbolID.SymbolSet_Activities:
167                        case SymbolID.SymbolSet_CyberSpace:
168                        //case SymbolID.SymbolSet_SignalsIntelligence:
169                        case SymbolID.SymbolSet_SignalsIntelligence_Air:
170                        case SymbolID.SymbolSet_SignalsIntelligence_Land:
171                        case SymbolID.SymbolSet_SignalsIntelligence_Space:
172                        case SymbolID.SymbolSet_SignalsIntelligence_SeaSurface:
173                        case SymbolID.SymbolSet_SignalsIntelligence_SeaSubsurface:
174                            return true;
175                    }
176                }
177                message = String.format("Cannot find %s in MSLookup", lookupID);
178            } else if (msi.getDrawRule() == DrawRules.DONOTDRAW) {
179                message = String.format("%s (%s) is DoNotDraw", lookupID, msi.getName());
180            } else
181            {
182                int version = SymbolID.getVersion(symbolID);
183                SVGInfo si = SVGLookup.getInstance().getSVGLInfo(lookupSVGID,version);
184                if (si != null)// || (SymbolID.getEntityCode(symbolID)==000000 && SVGLookup.getInstance().getSVGLInfo(SVGLookup.getFrameID(symbolID)) != null))
185                {
186                    return true;
187                }
188                else
189                {
190                    message = String.format("Cannot find %s (%s) in SVGLookup", lookupID, msi.getName());
191                }
192            }
193        } catch (Exception exc) {
194            ErrorLogger.LogException("MilStdIconRenderer", "CanRender", exc);
195        }
196        // ErrorLogger.LogMessage(this.getClass().getName(), "CanRender()", message, Level.FINE);
197        Log.d("MilStdIconR.CanRender()", message);
198        return false;
199    }
200
201
202
203    // @Override
204    public ImageInfo RenderIcon(String symbolID, Map<String,String> modifiers,
205            Map<String,String> attributes)
206    {
207
208
209        int ss = SymbolID.getSymbolSet(symbolID);
210
211        ImageInfo temp = null;
212        MSInfo msi = MSLookup.getInstance().getMSLInfo(symbolID);
213        /*if (msi == null)
214        {
215            //Attempt to fix the code or set if to invalid symbol if unfixable
216            symbolID = SymbolUtilities.reconcileSymbolID(symbolID);
217            msi = MSLookup.getInstance().getMSLInfo(symbolID);
218            if(msi == null)
219            {
220                if(SymbolID.getSymbolSet(symbolID)!=SymbolID.SymbolSet_LandUnit)
221
222                symbolID = "110098000010000000000000000000";//invalid symbol
223            }
224        }
225        if (msi != null && msi.getDrawRule() == DrawRules.DONOTDRAW) {
226            return null;
227        }//*/
228
229        if (ss==SymbolID.SymbolSet_ControlMeasure)
230        {
231            if (msi != null) {
232                //Point12 is actually a multipoint and 17 & 18 are rectangular target and sector range fan
233                if (SymbolUtilities.isMultiPoint(symbolID)==false) {
234                    temp = _SPR.RenderSP(symbolID, modifiers, attributes);
235                } else {
236                    temp = _SPR.RenderSP(symbolID, null, attributes);
237                }
238            }
239        }
240        else if(ss==SymbolID.SymbolSet_Atmospheric ||
241                ss==SymbolID.SymbolSet_Oceanographic ||
242                ss==SymbolID.SymbolSet_MeteorologicalSpace)
243        {
244            temp = _SPR.RenderSP(symbolID, modifiers, attributes);
245        }
246        else
247        {
248            temp = _SPR.RenderUnit(symbolID, modifiers, attributes);
249        }
250
251        return temp;
252    }
253
254    public SVGSymbolInfo RenderSVG(String symbolID, Map<String,String> modifiers,
255                                   Map<String,String> attributes)
256    {
257
258        //Update to use _SPSVGR.RenderUnit
259        int ss = SymbolID.getSymbolSet(symbolID);
260
261        ImageInfo temp = null;
262        SVGSymbolInfo svgTemp = null;
263        MSInfo msi = MSLookup.getInstance().getMSLInfo(symbolID);
264        if (msi == null)
265        {
266            //TODO: if null, try to fix the code so that something renders
267                /*symbolID = SymbolUtilities.reconcileSymbolID(symbolID);
268                basicSymbolID = SymbolUtilities.getBasicSymbolIDStrict(symbolID);
269                sd = SymbolDefTable.getInstance().getSymbolDef(basicSymbolID, symStd);//*/
270        }
271        if (msi != null && msi.getDrawRule() == DrawRules.DONOTDRAW)
272        {
273            return null;
274        }
275
276        if (ss==SymbolID.SymbolSet_ControlMeasure)
277        {
278            if (msi != null) {
279                //Point12 is actually a multipoint and 17 & 18 are rectangular target and sector range fan
280                if (SymbolUtilities.isMultiPoint(symbolID)==false) {
281                    svgTemp = _SPSVGR.RenderSP(symbolID, modifiers, attributes);
282                } else {
283                    svgTemp = _SPSVGR.RenderSP(symbolID, null, attributes);
284                }
285            }
286        }
287        else if(ss==SymbolID.SymbolSet_Atmospheric ||
288                ss==SymbolID.SymbolSet_Oceanographic ||
289                ss==SymbolID.SymbolSet_MeteorologicalSpace)
290        {
291            svgTemp = _SPSVGR.RenderSP(symbolID, modifiers, attributes);
292        }
293        else
294        {
295            svgTemp = _SPSVGR.RenderUnit(symbolID, modifiers, attributes);
296        }
297
298        return svgTemp;
299    }
300
301    // @Override
302    public String getRendererID()
303    {
304
305        return "milstd2525";
306    }
307
308    private Map<String,String> getDefaultAttributes(String symbolID)
309    {
310        Map<String,String> map = new HashMap<>();
311        try {
312            if (symbolID == null || symbolID.length() != 15) {
313                if (symbolID == null) {
314                    symbolID = "null";
315                }
316                ErrorLogger.LogMessage("MilStdIconRenderer", "getDefaultAttributes",
317                        "getDefaultAttributes passed bad symbolID: " + symbolID);
318                return null;
319            }
320
321            map.put(MilStdAttributes.Alpha, "1.0");
322            if (SymbolUtilities.hasDefaultFill(symbolID)) {
323                map.put(MilStdAttributes.FillColor,
324                        SymbolUtilities.getFillColorOfAffiliation(symbolID).toHexString());
325            }
326
327            map.put(MilStdAttributes.LineColor,
328                    SymbolUtilities.getLineColorOfAffiliation(symbolID).toHexString());
329
330            map.put(MilStdAttributes.OutlineSymbol, "false");
331            // attribute[MilStdAttributes.SymbolOutlineColor] = null;
332            // map.put(MilStdAttributes.OutlineWidth,"1");
333
334            map.put(MilStdAttributes.DrawAsIcon, "false");
335
336            RendererSettings rs = RendererSettings.getInstance();
337
338            map.put(MilStdAttributes.KeepUnitRatio, "true");
339            return map;
340        } catch (Exception exc) {
341            ErrorLogger.LogException("MilStdIconRenderer", "getDefaultAttributes", exc);
342        }
343        return map;
344    }
345
346    /**
347     * Add a custom framed symbol to the renderer's collection
348     * @param msInfo
349     * @param svgInfo
350     * @return
351     */
352    public boolean AddCustomSymbol(MSInfo msInfo, SVGInfo svgInfo)
353    {
354        boolean success = false;
355        if(msInfo.getBasicSymbolID().equals(svgInfo.getID()))//Make sure IDs match
356        {
357            //Make sure entry isn't already there
358            if(MSLookup.getInstance().getMSLInfo(msInfo.getBasicSymbolID(),msInfo.getVersion())==null &&
359                    SVGLookup.getInstance().getSVGLInfo(svgInfo.getID(),msInfo.getVersion())==null)
360            {
361                if(MSLookup.getInstance().addCustomSymbol(msInfo))
362                    success = SVGLookup.getInstance().addCustomSymbol(svgInfo,msInfo.getVersion());
363            }
364        }
365        else
366        {
367            ErrorLogger.LogMessage("Symbol Set and Entity Codes do not match", Level.INFO,false);
368        }
369        return success;
370    }
371}