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