/*
 * 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.bridge;

import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import org.apache.weex.WXSDKInstance;
import org.apache.weex.WXSDKManager;
import org.apache.weex.performance.WXAnalyzerDataTransfer;
import org.apache.weex.utils.WXLogUtils;
import org.apache.weex.utils.WXReflectionUtils;

import java.lang.reflect.Type;

/**
 * Created by sospartan on 10/11/2016.
 */

public class NativeInvokeHelper {
  private String mInstanceId;

  public NativeInvokeHelper(String instanceId){
    mInstanceId = instanceId;
  }

  public Object invoke(final Object target,final Invoker invoker,JSONArray args) throws Exception {
    final Object[] params = prepareArguments(
            invoker.getParameterTypes(),
            args);

    if (WXAnalyzerDataTransfer.isInteractionLogOpen() && invoker instanceof MethodInvoker) {
      for (int i = 0; i < params.length; i++) {
        if (params[i] instanceof SimpleJSCallback) {
          final String callBackId = ((SimpleJSCallback)params[i]).getCallbackId();
          Log.d(WXAnalyzerDataTransfer.INTERACTION_TAG, "[client][callNativeModuleStart]," + mInstanceId + "," + ((MethodInvoker) invoker).mMethod.getDeclaringClass() + "," + ((MethodInvoker) invoker).mMethod.getName() + "," + callBackId);
          ((SimpleJSCallback) params[i]).setInvokerCallback(new SimpleJSCallback.InvokerCallback() {
            @Override
            public void onInvokeSuccess() {
              Log.d(WXAnalyzerDataTransfer.INTERACTION_TAG, "[client][callNativeModuleEnd]," + mInstanceId + "," + ((MethodInvoker) invoker).mMethod.getDeclaringClass() + "," + ((MethodInvoker) invoker).mMethod.getName() + "," + callBackId);
            }
          });
          break;
        }
      }
    }

    if (invoker.isRunOnUIThread()) {
      WXSDKManager.getInstance().postOnUiThread(new Runnable() {
        @Override
        public void run() {
          if (invoker != null) {
            try {
              WXSDKInstance targetInstance = WXSDKManager.getInstance().getSDKInstance(mInstanceId);
              if (null == targetInstance || targetInstance.isDestroy()){
                return;
              }
              invoker.invoke(target, params);
            } catch (Exception e) {
              WXLogUtils.e("NativeInvokeHelper",target + " Invoker " + invoker.toString()+" exception:"+e);
            }
          }
        }
      }, 0);
    } else {
      return invoker.invoke(target, params);
    }
    return null;
  }

  protected Object[] prepareArguments(Type[] paramClazzs, JSONArray args) throws Exception {
    Object[] params = new Object[paramClazzs.length];
    Object value;
    Type paramClazz;
    for (int i = 0; i < paramClazzs.length; i++) {
      paramClazz = paramClazzs[i];
      if(i>=args.size()){
        if(!paramClazz.getClass().isPrimitive()) {
          params[i] = null;
          continue;
        }else {
          throw new Exception("[prepareArguments] method argument list not match.");
        }
      }
      value = args.get(i);

      if (paramClazz == JSONObject.class) {
        if(value instanceof  JSONObject || value == null) {
          params[i] = value;
        }else if (value instanceof String){
          params[i] = JSON.parseObject(value.toString());
        }
      } else if(JSCallback.class == paramClazz){
        if(value instanceof String){
          params[i] = new SimpleJSCallback(mInstanceId,(String)value);
        }else{
          throw new Exception("Parameter type not match.");
        }
      } else {
        params[i] = WXReflectionUtils.parseArgument(paramClazz,value);
      }
    }
    return params;
  }
}
