/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.weex.utils;

import android.content.Intent;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.net.Uri;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;

import org.apache.weex.WXEnvironment;
import org.apache.weex.WXSDKManager;
import org.apache.weex.adapter.IWXHttpAdapter;
import org.apache.weex.common.WXRequest;
import org.apache.weex.common.WXResponse;
import org.apache.weex.dom.WXStyle;
import org.apache.weex.font.FontAdapter;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by sospartan on 7/13/16.
 */
public class TypefaceUtil {
  public static final String FONT_CACHE_DIR_NAME = "font-family";
  private final static String TAG = "TypefaceUtil";
  private final static Map<String, FontDO> sCacheMap = new HashMap<>(); //Key: fontFamilyName

  public static final String ACTION_TYPE_FACE_AVAILABLE = "type_face_available";

  public static void putFontDO(FontDO fontDO) {
    if (fontDO != null && !TextUtils.isEmpty(fontDO.getFontFamilyName())) {
      sCacheMap.put(fontDO.getFontFamilyName(), fontDO);
    }
  }

  public static void registerNativeFont(Map<String, Typeface> fonts) {
    if (fonts != null && fonts.size() > 0) {
      for (Map.Entry<String, Typeface> font : fonts.entrySet()) {
        FontDO fontDO = new FontDO(font.getKey(), font.getValue());
        putFontDO(fontDO);
        if (WXEnvironment.isApkDebugable()){
          WXLogUtils.d("TypefaceUtil", "register new typeface: " + font.getKey());
        }
      }
    }
  }

  public static FontDO getFontDO(String fontFamilyName) {
    return sCacheMap.get(fontFamilyName);
  }

  public static void removeFontDO(String fontFamilyName) {
    sCacheMap.remove(fontFamilyName);
  }

  public static void applyFontStyle(Paint paint, int style, int weight, String family) {
    int oldStyle;
    Typeface typeface = paint.getTypeface();
    if (typeface == null) {
      oldStyle = 0;
    } else {
      oldStyle = typeface.getStyle();
    }

    int want = 0;
    if ((weight == Typeface.BOLD)
            || ((oldStyle & Typeface.BOLD) != 0 && weight == WXStyle.UNSET)) {
      want |= Typeface.BOLD;
    }

    if ((style == Typeface.ITALIC)
            || ((oldStyle & Typeface.ITALIC) != 0 && style == WXStyle.UNSET)) {
      want |= Typeface.ITALIC;
    }

    if (family != null) {
      typeface = getOrCreateTypeface(family, want);
    }

    if (typeface != null) {
      paint.setTypeface(Typeface.create(typeface, want));
    } else {
      paint.setTypeface(Typeface.defaultFromStyle(want));
    }
  }

  public static Typeface getOrCreateTypeface(String family, int style) {
    FontDO fontDo = sCacheMap.get(family);
    if (fontDo != null && fontDo.getTypeface() != null) {
      return fontDo.getTypeface();
    }

    return Typeface.create(family, style);
  }

  private static void loadFromAsset(FontDO fontDo,String path){
    try {
      Typeface typeface = Typeface.createFromAsset(WXEnvironment.getApplication().getAssets(), path);
      if (typeface != null) {
        if(WXEnvironment.isApkDebugable()) {
          WXLogUtils.d(TAG, "load asset file success");
        }
        fontDo.setState(FontDO.STATE_SUCCESS);
        fontDo.setTypeface(typeface);
      } else {
        WXLogUtils.e(TAG, "Font asset file not found " + fontDo.getUrl());
      }
    } catch (Exception e) {
      WXLogUtils.e(TAG, e.toString());
    }
  }

  public static void loadTypeface(final FontDO fontDo, boolean notify) {
    if (fontDo != null && fontDo.getTypeface() == null &&
            (fontDo.getState() == FontDO.STATE_FAILED || fontDo.getState() == FontDO.STATE_INIT)) {
      fontDo.setState(FontDO.STATE_LOADING);
      if (fontDo.getType() == FontDO.TYPE_LOCAL) {
        Uri uri = Uri.parse(fontDo.getUrl());
        loadFromAsset(fontDo,uri.getPath().substring(1));//exclude slash
      } else if (fontDo.getType() == FontDO.TYPE_NETWORK) {
        final String url = fontDo.getUrl();
        final String fontFamily = fontDo.getFontFamilyName();
        final String fileName = WXFileUtils.md5(url);
        //url.replace('/', '_').replace(':', '_');
        File dir = new File(getFontCacheDir());
        if(!dir.exists()){
          dir.mkdirs();
        }
        final String fullPath =  dir.getAbsolutePath()+ File.separator +fileName;
        if (!loadLocalFontFile(fullPath, fontFamily, false)) {
          downloadFontByNetwork(url, fullPath, fontFamily);
        }
      } else if (fontDo.getType() == FontDO.TYPE_FILE || fontDo.getType() == FontDO.TYPE_BASE64) {
        boolean result = loadLocalFontFile(fontDo.getUrl(), fontDo.getFontFamilyName(), false);
        if (!result) {
          fontDo.setState(FontDO.STATE_FAILED);
        }
      }
      return;
    }
    if(notify){
         notifyFontAvailable(false, fontDo);
    }
  }

  private static void downloadFontByNetwork(final String url, final String fullPath, final String fontFamily) {
    IWXHttpAdapter adapter = WXSDKManager.getInstance().getIWXHttpAdapter();
    if (adapter == null) {
      WXLogUtils.e(TAG, "downloadFontByNetwork() IWXHttpAdapter == null");
      return;
    }
    WXRequest request = new WXRequest();
    request.url = url;
    request.method = "GET";
    adapter.sendRequest(request, new IWXHttpAdapter.OnHttpListener() {
      @Override
      public void onHttpStart() {
        if(WXEnvironment.isApkDebugable()) {
          WXLogUtils.d(TAG, "downloadFontByNetwork begin url:" + url);
        }
      }

      @Override
      public void onHeadersReceived(int statusCode, Map<String, List<String>> headers) {

      }

      @Override
      public void onHttpUploadProgress(int uploadProgress) {

      }

      @Override
      public void onHttpResponseProgress(int loadedLength) {

      }

      @Override
      public void onHttpFinish(WXResponse response) {
        int statusCode = 0;
        if (!TextUtils.isEmpty(response.statusCode)) {
          try {
            statusCode = Integer.parseInt(response.statusCode);
          } catch (NumberFormatException e) {
            statusCode = 0;
            WXLogUtils.e(TAG, "IWXHttpAdapter onHttpFinish statusCode:" + response.statusCode);
          }
        }
        boolean result;
        if (statusCode >= 200 && statusCode <= 299 && response.originalData != null) {
          result = WXFileUtils.saveFile(fullPath, response.originalData, WXEnvironment.getApplication());
          if (result) {
            result = loadLocalFontFile(fullPath, fontFamily, true);
          } else {
            if(WXEnvironment.isApkDebugable()) {
              WXLogUtils.d(TAG, "downloadFontByNetwork() onHttpFinish success, but save file failed.");
            }
          }
        } else {
          result = false;
        }

        if (!result) {
          FontDO fontDO = sCacheMap.get(fontFamily);
          if (fontDO != null) {
            fontDO.setState(FontDO.STATE_FAILED);
          }
        }
      }
    });
  }

  private static boolean loadLocalFontFile(final String path, final String fontFamily, boolean hasNetworkDowload) {
    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(fontFamily)) {
      return false;
    }
    try {
      File file = new File(path);
      if (!file.exists()) {
        return false;
      }
      Typeface typeface = Typeface.createFromFile(path);
      if (typeface != null) {
        final FontDO fontDo = sCacheMap.get(fontFamily);
        if (fontDo != null) {
          fontDo.setState(FontDO.STATE_SUCCESS);
          fontDo.setTypeface(typeface);
          fontDo.setFilePath(path);
          if(WXEnvironment.isApkDebugable()) {
            WXLogUtils.d(TAG, "load local font file success");
          }

          if(hasNetworkDowload) {
            /**
             * wxtext may be measured when font not load, when register broadcast receiver,
             * this broadcast has been send, which cause textview not rendered right.
             * delay broadcast ensure text will render right
             * */
            WXSDKManager.getInstance().getWXRenderManager().postOnUiThread(new Runnable() {
              @Override
              public void run() {
               notifyFontAvailable(true, fontDo);
              }
            }, 100);
          }else{
             notifyFontAvailable(true, fontDo);
          }
          return true;
        }
      } else {
        WXLogUtils.e(TAG, "load local font file failed, can't create font.");
      }
    } catch (Exception e) {
      WXLogUtils.e(TAG, e.toString());
    }
    return false;
  }

  private static void notifyFontAvailable(boolean sendBroadcast, FontDO fontDO){
    if(sendBroadcast){
      Intent intent = new Intent(ACTION_TYPE_FACE_AVAILABLE);
      intent.putExtra("fontFamily", fontDO.getFontFamilyName());
      intent.putExtra("filePath", fontDO.getFilePath());
      intent.putExtra("fontUrl", fontDO.getUrl());
      LocalBroadcastManager.getInstance(WXEnvironment.getApplication()).sendBroadcast(intent);
    }
    FontAdapter fontAdapter = WXSDKManager.getInstance().getFontAdapter();
    if(fontAdapter != null){
        fontAdapter.onFontLoad(fontDO.getFontFamilyName(), fontDO.getUrl(), fontDO.getFilePath());
    }
  }

  private static String getFontCacheDir() {
    return WXEnvironment.getApplication().getCacheDir() + "/" + FONT_CACHE_DIR_NAME;
  }
}
