/*
 * 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.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Looper;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.util.LruCache;
import android.text.TextUtils;
import org.apache.weex.WXEnvironment;
import org.apache.weex.WXSDKManager;
import org.apache.weex.adapter.IWXConfigAdapter;
import org.apache.weex.common.Constants;

public class WXUtils {

  final static LruCache<String, Integer> sCache = new LruCache<>(64);
  public static final char PERCENT = '%';
  private static final int HUNDRED =100;
  /**
   * Tell whether current thread is UI(main) thread.
   * @return true for UI(main) thread
   */
  public static boolean isUiThread() {
    return Thread.currentThread().getId() == Looper.getMainLooper().getThread().getId();
  }

  public static boolean isUndefined(float value) {
    return Float.isNaN(value);
  }


  public static float getFloatByViewport(Object value, int viewport) {
    if (value == null) {
      return Float.NaN;
    }
    String temp = value.toString().trim();
    if (Constants.Name.AUTO.equals(temp)
            || Constants.Name.UNDEFINED.equals(temp)
            || TextUtils.isEmpty(temp)) {
      WXLogUtils.e("Argument Warning ! value is " + temp + "And default Value:" + Float.NaN);
      return Float.NaN;
    }

    if (temp.endsWith("wx")) {
      try {
        return transferWx(temp, viewport);
      } catch (NumberFormatException e) {
        WXLogUtils.e("Argument format error! value is " + value, e);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    } else if (temp.endsWith("px")) {
      try {
        temp = temp.substring(0, temp.indexOf("px"));
        return Float.parseFloat(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    } else {
      try {
        return Float.parseFloat(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }
    return Float.NaN;
  }

  public static float getFloat(Object value) {
    return getFloat(value, Float.NaN);
  }

  public static Float getFloat(Object value, @Nullable Float df) {
    if (value == null) {
      return df;
    }

    String temp = value.toString().trim();
    if(Constants.Name.AUTO.equals(temp)
            || Constants.Name.UNDEFINED.equals(temp)
            || TextUtils.isEmpty(temp)){
      WXLogUtils.e("Argument Warning ! value is " + temp + "And default Value:"+Float.NaN);
      return df;
    }
    if (temp.endsWith("wx")) {
      try {
        return transferWx(temp, 750);
      } catch (NumberFormatException e) {
        WXLogUtils.e("Argument format error! value is " + value, e);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }else if (temp.endsWith("px")) {
      try {
        temp = temp.substring(0, temp.indexOf("px"));
        return Float.parseFloat(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }else {
      try {
        return Float.parseFloat(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }
    return df;
  }

  private static float transferWx(String stringWithWXPostfix, int viewport) {
    if(null == stringWithWXPostfix) {
      return 0;
    }
    String temp = stringWithWXPostfix;
    if(stringWithWXPostfix.endsWith("wx")) {
      temp = stringWithWXPostfix.substring(0, stringWithWXPostfix.indexOf("wx"));
    }
    Float f = Float.parseFloat(temp);
    float density = WXEnvironment.sApplication.getResources().getDisplayMetrics().density;
    return density * f * viewport / WXViewUtils.getScreenWidth();
  }

  public static float fastGetFloat(String raw, int precision){
    if(!TextUtils.isEmpty(raw)){
      boolean positive=true;
      int loc=0;
      if(raw.charAt(0)=='-'){
        positive=false;
        loc++;
      }
      else if(raw.charAt(0)=='+'){
        loc++;
      }

      char digit;
      float result=0;
      while (loc<raw.length() && (digit=raw.charAt(loc))>='0'&&digit<='9'){
        result=result*10+digit-'0';
        loc++;
      }

      if(loc<raw.length()){
        if(raw.charAt(loc)=='.'){
          loc++;
          int remainderLength=10;
          int counter=0;
          while(loc<raw.length() &&
                  counter<precision &&
                  ((digit=raw.charAt(loc))>='0'&& digit<='9')){
            result+=(digit-'0')/(float)remainderLength;
            remainderLength*=10;
            loc++;
            counter++;
          }
        }
        else{
          // throw new NumberFormatException("Illegal separator");
        }
      }

      if(!positive)
        result*=-1;
      return result;
    }
    return 0;
    // throw new NumberFormatException("NullNumber");
  }

  /**
   * Parse string representation of float. This method intend to be faster than
   * {@link Float#parseFloat(String)}, but less accuracy.
   * @param raw
   * @return
   */
  public static float fastGetFloat(String raw){
    return fastGetFloat(raw, Integer.MAX_VALUE);
  }

  public static int parseInt(String value) {
    try {
      if (!TextUtils.isEmpty(value) && !value.contains(".")) {
        return Integer.parseInt(value);
      }
    } catch (NumberFormatException e) {
      if (WXEnvironment.isApkDebugable()) {
        WXLogUtils.e(WXLogUtils.getStackTrace(e));
      }
    }
    return 0;
  }

  public static int parseInt(Object value) {
    return parseInt(String.valueOf(value));
  }

  public static float parseFloat(Object value) {
    return parseFloat(String.valueOf(value));
  }

  public static float parseFloat(String value) {
    try {
      if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "null")) {
        return Float.parseFloat(value);
      } else {
        if (WXEnvironment.isApkDebugable()) {
          WXLogUtils.e("WXUtils parseFloat illegal value is " + value);
        }
      }
    } catch (NumberFormatException e) {
      if (WXEnvironment.isApkDebugable()) {
        WXLogUtils.e(WXLogUtils.getStackTrace(e));
      }
    }
    return 0;
  }

  public static int getInt(Object value) {
    return getInteger(value, 0);
  }

  public static @Nullable Integer getInteger(@Nullable Object value, @Nullable Integer df) {

    if (value == null) {
      return df;
    }

    String temp = value.toString().trim();
    String key = temp;
    Integer cache = sCache.get(key);
    if (cache != null) {
      return cache;
    } else {
      Integer ret = df;
      String suffix = "";
      if (temp.length() >= 2) {
        suffix = temp.substring(temp.length() - 2, temp.length());
      }

      if (TextUtils.equals("wx", suffix)) {
        if (WXEnvironment.isApkDebugable()) {
          WXLogUtils
              .w("the value of " + value
                  + " use wx unit, which will be not supported soon after.");
        }
        try {
          ret = (int)transferWx(temp, 750);
        } catch (NumberFormatException e) {
          WXLogUtils.e("Argument format error! value is " + value, e);
        } catch (Exception e) {
          WXLogUtils.e("Argument error! value is " + value, e);
        }
      } else if (TextUtils.equals("px", suffix)) {
        try {
          temp = temp.substring(0, temp.length() - 2);
          if (!TextUtils.isEmpty(temp) && temp.contains(".")) {
            ret = (int)WXUtils.parseFloat(temp);
          } else {
            ret = Integer.parseInt(temp);
          }
        } catch (NumberFormatException nfe) {
          WXLogUtils.e("Argument format error! value is " + value, nfe);
        } catch (Exception e) {
          WXLogUtils.e("Argument error! value is " + value, e);
        }
      } else {
        try {
          if (!TextUtils.isEmpty(temp)) {
            if (temp.contains(".")) {
              ret = (int)WXUtils.parseFloat(temp);
            } else {
              ret = Integer.parseInt(temp);
            }
          } else {
            if (WXEnvironment.isApkDebugable()) {
              WXLogUtils.e("Argument value is null, df is" + df);
            }
          }
        } catch (NumberFormatException nfe) {
          if (WXEnvironment.isApkDebugable()) {
            WXLogUtils.w("The parameter format is not supported", nfe);
          }
        } catch (Exception e) {
          WXLogUtils.e("Argument error! value is " + value, e);
        }
      }
      if (ret != null && !ret.equals(df)) {
        sCache.put(key, ret);
      }
      return ret;
    }
  }

  /**
   * Looks like no longer usage exists, Mark deprecate first.
   * @param value
   * @return
   */
  @Deprecated
  public static long getLong(Object value) {
    if (value == null) {
      return 0;
    }
    long result = 0;
    String temp = value.toString().trim();
    if (temp.endsWith("wx")) {
      if (WXEnvironment.isApkDebugable()) {
        WXLogUtils.w("the value of " + value + " use wx unit, which will be not supported soon after.");
      }
      try {
        return (long)transferWx(temp, 750);
      } catch (NumberFormatException e) {
        WXLogUtils.e("Argument format error! value is " + value, e);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }else if (temp.endsWith("px")) {
      try {
        temp = temp.substring(0, temp.indexOf("px"));
        return Long.parseLong(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }else {
      try {
        return Long.parseLong(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }
    return result;
  }

  /**
   * Looks like no longer usage exists, Mark deprecate first.
   * @param value
   * @return
   */
  @Deprecated
  public static double getDouble(Object value) {
    if (value == null) {
      return 0;
    }
    double result = 0;
    String temp = value.toString().trim();
    if (temp.endsWith("wx")) {
      if (WXEnvironment.isApkDebugable()) {
        WXLogUtils.w("the value of " + value + " use wx unit, which will be not supported soon after.");
      }
      try {
        return transferWx(temp, 750);
      } catch (NumberFormatException e) {
        WXLogUtils.e("Argument format error! value is " + value, e);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }else if (temp.endsWith("px")) {
      try {
        temp = temp.substring(0, temp.indexOf("px"));
        return Double.parseDouble(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }else {
      try {
        return Double.parseDouble(temp);
      } catch (NumberFormatException nfe) {
        WXLogUtils.e("Argument format error! value is " + value, nfe);
      } catch (Exception e) {
        WXLogUtils.e("Argument error! value is " + value, e);
      }
    }

    return result;
  }

  @Deprecated
  public static boolean isTabletDevice() {
    try{
      return (WXEnvironment.getApplication().getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }catch (Exception e){
      //
    }
    return false;
  }

  public static Boolean getBoolean(@Nullable Object value, @Nullable Boolean df) {

    if (value == null)
      return df;
    if (TextUtils.equals("false",value.toString())) {
      return false;
    } else if (TextUtils.equals("true",value.toString())) {
      return true;
    }
    return df;
  }

  public static long getAvailMemory(Context context){
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
    am.getMemoryInfo(mi);
    //mi.availMem;
    //return Formatter.formatFileSize(context, mi.availMem);
    WXLogUtils.w("app AvailMemory ---->>>"+mi.availMem/(1024*1024));
    return mi.availMem/(1024*1024);
  }

  public static String getString(@Nullable Object value,@Nullable String df) {

    if (value == null)
      return df;

    String originValue;
    if (value instanceof String) {
      originValue = (String) value;
    } else {
      originValue = value.toString();
    }

    return originValue;
  }

  public static int parseUnitOrPercent(String raw, int unit) {
    int suffix;
    if ((suffix = raw.lastIndexOf(WXUtils.PERCENT)) != -1) {
      return parsePercent(raw.substring(0, suffix), unit);
    }
    else {
      return WXUtils.parseInt(raw);
    }
  }

  private static int parsePercent(String raw, int unit){
    return (int)(Float.parseFloat(raw) / HUNDRED * unit);
  }

  /**
   * Get a banner data in JSON format from a bundle content.
   * A bundle banner looks like below:
   * \/*!count
   *  * {
   *  *   version: "0.2.1.20170104-release",
   *  *   create: "20170207171112",
   *  *   git: "banner--772c915",
   *  *   digest: "c709b7f91867e371b24f54d6aeea232a"
   *  * }
   *  !*\/
   *
   * @param content a bundle content
   * @return
   */
  public static String getBundleBanner(String content) {
    final String commentBegin = "/*!";
    final String commentEnd = "!*/";
    final String asteriskRegex = "\\*";
    final String replacement = "";

    int offsetCountBegin = content.indexOf(commentBegin);
    if (offsetCountBegin == -1) {
      return null;
    }
    offsetCountBegin += commentBegin.length();
    int offsetCountEnd = indexLineBreak(content, offsetCountBegin);
    if (offsetCountEnd == -1) {
      return null;
    }
    String countStr = content.substring(offsetCountBegin, offsetCountEnd);
    int count = Integer.parseInt(countStr);

    String commentBody = content.substring(offsetCountEnd + 1, offsetCountEnd + 1 + count);
    int offsetBodyEnd = commentBody.lastIndexOf(commentEnd);
    if (offsetBodyEnd == -1) {
      return null;
    }
    commentBody = commentBody.substring(0, offsetBodyEnd);

    StringBuilder commentBodyBuilder = new StringBuilder();
    String[] items = splitLineBreak(commentBody);

    for (String item : items) {
      commentBodyBuilder.append(item.replaceFirst(asteriskRegex, replacement));
    }

    return commentBodyBuilder.toString();
  }

  private static int indexLineBreak(String str, int fromIndex) {
    final String lineBreakIos = "\r";
    final String lineBreakUnix = "\n";
    final String linebreakWin = "\r\n";

    int index = str.indexOf(lineBreakIos, fromIndex);
    if (index == -1) {
      index = str.indexOf(lineBreakUnix, fromIndex);
    }
    if (index == -1) {
      index = str.indexOf(linebreakWin, fromIndex);
    }

    return index;
  }

  private static String[] splitLineBreak(String str) {
    final String lineBreakIos = "\r";
    final String lineBreakUnix = "\n";
    final String linebreakWin = "\r\n";
    String[] items = str.split(lineBreakIos);

    if (items.length == 1) {
      items = str.split(lineBreakUnix);
    }
    if (items.length == 1) {
      items = str.split(linebreakWin);
    }

    return items;
  }


  
  /**
   * get number
   * */
  public static int getNumberInt(Object value, int defaultValue){
    if(value == null){
      return  defaultValue;
    }
    if(value instanceof  Number){
      return  ((Number) value).intValue();
    }
    try{
      String number = value.toString();
      if(number.indexOf('.') >= 0) {
        return (int) Float.parseFloat(value.toString());
      }else{
        return  Integer.parseInt(number);
      }
    }catch (Exception e){return  defaultValue;}
  }

  public static boolean checkGreyConfig(String group,String key,String defaultValue){
      IWXConfigAdapter configAdapter = WXSDKManager.getInstance().getWxConfigAdapter();
      if (null == configAdapter) {
        return false;
      }
      double randomValue = Math.random() * 100;
      double max = 100;
      try {
        String configValue = configAdapter.getConfig(group, key, defaultValue);
        max = Double.valueOf(configValue);
      } catch (Exception e) {
        e.printStackTrace();
      }
      return randomValue < max;
  }

  private static final long sInterval = System.currentTimeMillis() - SystemClock.uptimeMillis();

  public static long getFixUnixTime(){
    return  sInterval + SystemClock.uptimeMillis();
  }

}
