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