/*
 * Copyright (C) 2015-2016 Emanuel Moecklin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.onegravity.rteditor.fonts;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Typeface;

import com.onegravity.rteditor.utils.io.FilenameUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeMap;

/**
 * This class retrieves fonts from the assets and the Android system folders.
 */
public class FontManager {

    private static final String[] FONT_DIRS = {"/system/fonts", "/system/font", "/data/fonts"};

    private static final Map<String, String> ASSET_FONTS_BY_NAME = new TreeMap<String, String>();
    private static final Map<String, String> SYSTEM_FONTS_BY_PATH = new TreeMap<String, String>();
    private static final Map<String, String> SYSTEM_FONTS_BY_NAME = new TreeMap<String, String>();

    /*
     * Don't load the same font more than once -> cache them here.
     */
    private static final RTTypefaceSet ALL_FONTS = new RTTypefaceSet() {
        /**
         * @return The RTTypeface with the specified name or false if no such RTTypeface exists.
         */
        RTTypeface get(String fontName) {
            for (RTTypeface typeface : this) {
                if (typeface.getName().equals(fontName)) {
                    return typeface;
                }
            }
            return null;
        }

        /**
         * @return True if the collections contains an RTTypeface with the specified name, false otherwise.
         */
        boolean contains(String fontName) {
            return get(fontName) != null;
        }
    };

    /**
     * Use this method to preload fonts asynchronously e.g. when the app starts up.
     */
    public static void preLoadFonts(final Context context) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ASSET_FONTS_BY_NAME) {
                    getAssetFonts(context);
                }
                synchronized (SYSTEM_FONTS_BY_NAME) {
                    getSystemFonts();
                }
            }
        }).start();
    }

    /**
     * Retrieve the fonts from the asset and the system folder.
     *
     * @return A Map mapping the name of the font to the Typeface.
     * If the name can't be retrieved the file name will be used (e.g. arial.ttf).
     */
    public static SortedSet<RTTypeface> getFonts(Context context) {
        /*
         * Fonts from the assets folder
         */
        Map<String, String> assetFonts = getAssetFonts(context);
        AssetManager assets = context.getResources().getAssets();
        for (String fontName : assetFonts.keySet()) {
            String filePath = assetFonts.get(fontName);
            if (!ALL_FONTS.contains(fontName)) {
                try {
                    Typeface typeface = Typeface.createFromAsset(assets, filePath);
                    ALL_FONTS.add(new RTTypeface(fontName, typeface));
                }
                catch (Exception e) {
                    // this can happen if we don't have access to the font or it's not a font or...
                }
            }
        }

        /*
         * Fonts from the system
         */
        Map<String, String> systemFonts = getSystemFonts();
        for (String fontName : systemFonts.keySet()) {
            String filePath = systemFonts.get(fontName);
            if (!ALL_FONTS.contains(fontName)) {
                try {
                    Typeface typeface = Typeface.createFromFile(filePath);
                    ALL_FONTS.add(new RTTypeface(fontName, typeface));
                }
                catch (Exception e) {
                    // this can happen if we don't have access to the font or it's not a font or...
                }
            }
        }

        return ALL_FONTS;
    }

    /**
     * Returns the RTTypeface for a specific font identified by name.
     * This assumes that the fonts have already been loaded.
     *
     * @return The RTTypeface for a specific font name or Null of no such font exists.
     */
    public static RTTypeface getTypeface(String fontName) {
        return ALL_FONTS.get(fontName);
    }

    /**
     * Retrieve the fonts from the asset folder.
     *
     * @return A Map mapping name of the font to the file path.
     * If the name can't be retrieved the file name will be used (e.g. arial.ttf).
     */
    private static Map<String, String> getAssetFonts(Context context) {
        synchronized (ASSET_FONTS_BY_NAME) {
            /*
             * Let's do this only once because it's expensive and the result won't change in any case.
             */
            if (ASSET_FONTS_BY_NAME.isEmpty()) {
                AssetManager assets = context.getResources().getAssets();

                Collection<String> fontFiles = AssetIndex.getAssetIndex(context);
                if (fontFiles == null || fontFiles.isEmpty()) {
                    fontFiles = listFontFiles(context.getResources());
                }

                for (String filePath : fontFiles) {
                    if (filePath.toLowerCase(Locale.getDefault()).endsWith("ttf")) {
                        String fontName = TTFAnalyzer.getFontName(assets, filePath);
                        if (fontName == null) {
                            fontName = getFileName(filePath);
                        }
                        ASSET_FONTS_BY_NAME.put(fontName, filePath);
                    }
                }
            }

            return ASSET_FONTS_BY_NAME;
        }
    }

    private static Collection<String> listFontFiles(Resources res) {
        Collection<String> fonts = new ArrayList<String>();
        listFontFiles(res.getAssets(), fonts, "");
        return fonts;
    }

    private static void listFontFiles(AssetManager assets, Collection<String> fonts, String path) {
        try {
            String[] list = assets.list(path);
            if (list.length > 0) {
                // it's a folder
                for (String file : list) {
                    String prefix = "".equals(path) ? "" : path + File.separator;
                    listFontFiles(assets, fonts, prefix + file);
                }
            } else if (path.endsWith("ttf")) {
                // it's a font file
                fonts.add(path);
            }
        } catch (IOException ignore) {
        }
    }

    /**
     * Retrieve the fonts from the system folders.
     *
     * @return A Map mapping name of the font to the file path.
     * If the name can't be retrieved the file name will be used (e.g. arial.ttf).
     */
    private static Map<String, String> getSystemFonts() {
        synchronized (SYSTEM_FONTS_BY_NAME) {
            for (String fontDir : FONT_DIRS) {
                File dir = new File(fontDir);

                if (!dir.exists()) continue;

                File[] files = dir.listFiles();

                if (files == null) continue;

                for (File file : files) {
                    String filePath = file.getAbsolutePath();
                    if (!SYSTEM_FONTS_BY_PATH.containsKey(filePath)) {
                        String fontName = TTFAnalyzer.getFontName(file.getAbsolutePath());
                        if (fontName == null) {
                            fontName = getFileName(filePath);
                        }
                        SYSTEM_FONTS_BY_PATH.put(filePath, fontName);
                        SYSTEM_FONTS_BY_NAME.put(fontName, filePath);
                    }
                }
            }

            return SYSTEM_FONTS_BY_NAME;
        }
    }

    private static String getFileName(String path) {
        return FilenameUtils.getBaseName(path).replace(File.pathSeparator, "");
    }

}

@@ -6661,16 +6661,32 @@
 if (list
+ != null && list
 .length 

make loading the fonts a little bit more robust