package com.wisqo.opensource.qrcode;

import android.content.Context;
import android.content.res.TypedArray;
import android.hardware.Camera;
import android.os.Handler;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import cn.bingoogolapple.qrcode.core.BGAQRCodeUtil;
import cn.bingoogolapple.qrcode.core.CameraPreview;
import cn.bingoogolapple.qrcode.core.ProcessDataTask;

import static java.security.AccessController.getContext;

public abstract class QrCodeView extends RelativeLayout
    implements Camera.PreviewCallback, ProcessDataTask.Delegate {
  protected Camera mCamera;
  protected CameraPreview mPreview;
  protected ScanBoxView mScanBoxView;
  protected Delegate mDelegate;
  protected Handler mHandler;
  protected boolean mSpotAble = false;
  protected ProcessDataTask mProcessDataTask;
  private int mOrientation;
  private int startupDelay = 1500;

  public QrCodeView(Context context, AttributeSet attrs) {
    super(context);

    mHandler = new Handler();
    initView(context, attrs);
  }

  private void initView(Context context, AttributeSet attrs) {
    mPreview = new CameraPreview(getContext());

    mScanBoxView = new ScanBoxView(getContext());

    // TODO: Initialize with arguments from ReactNative,
    // not this one.
    mScanBoxView.initCustomAttrs(context, attrs);

    // Set preview id.
    mPreview.setId(cn.bingoogolapple.qrcode.core.R.id.bgaqrcode_camera_preview);
    addView(mPreview);

    // Add the scan box inside.
    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
        RelativeLayout.LayoutParams.MATCH_PARENT,
        RelativeLayout.LayoutParams.MATCH_PARENT);
    layoutParams.addRule(RelativeLayout.ALIGN_TOP, mPreview.getId());
    layoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mPreview.getId());
    addView(mScanBoxView, layoutParams);

    mOrientation = BGAQRCodeUtil.getOrientation(context);

    // Fill the parent
//    this.setLayoutParams(new RelativeLayout.LayoutParams(
//        RelativeLayout.LayoutParams.MATCH_PARENT,
//        RelativeLayout.LayoutParams.MATCH_PARENT)
//    );
    startSpotAndShowRect();

    this.setDelegate(new EventHandler());
  }

  /**
   * 设置扫描二维码的代理
   *
   * @param delegate 扫描二维码的代理
   */
  public void setDelegate(Delegate delegate) {
    mDelegate = delegate;
  }

  public ScanBoxView getScanBoxView() {
    return mScanBoxView;
  }

  /**
   * 显示扫描框
   */
  public void showScanRect() {
    if (mScanBoxView != null) {
      mScanBoxView.setVisibility(View.VISIBLE);
    }
  }

  /**
   * 隐藏扫描框
   */
  public void hiddenScanRect() {
    if (mScanBoxView != null) {
      mScanBoxView.setVisibility(View.GONE);
    }
  }

  /**
   * 打开后置摄像头开始预览，但是并未开始识别
   */
  public void startCamera() {
    startCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
  }

  /**
   * 打开指定摄像头开始预览，但是并未开始识别
   *
   * @param cameraFacing
   */
  public void startCamera(int cameraFacing) {
    if (mCamera != null) {
      return;
    }
    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    for (int cameraId = 0; cameraId < Camera.getNumberOfCameras(); cameraId++) {
      Camera.getCameraInfo(cameraId, cameraInfo);
      if (cameraInfo.facing == cameraFacing) {
        startCameraById(cameraId);
        break;
      }
    }
  }

  private void startCameraById(int cameraId) {
    try {
      mCamera = Camera.open(cameraId);
      mPreview.setCamera(mCamera);
    } catch (Exception e) {
      if (mDelegate != null) {
        mDelegate.onScanQRCodeOpenCameraError();
      }
    }
  }

  /**
   * 关闭摄像头预览，并且隐藏扫描框
   */
  public void stopCamera() {
    try {
      stopSpotAndHiddenRect();
      if (mCamera != null) {
        mPreview.stopCameraPreview();
        mPreview.setCamera(null);
        mCamera.release();
        mCamera = null;
      }
    } catch (Exception e) {
    }
  }

  /**
   * 延迟100m秒后开始识别
   */
  public void startSpot() {
    startSpotDelay(startupDelay);
  }

  /**
   * 延迟delay毫秒后开始识别
   *
   * @param delay
   */
  public void startSpotDelay(int delay) {
    mSpotAble = true;

    startCamera();
    // 开始前先移除之前的任务
    mHandler.removeCallbacks(mOneShotPreviewCallbackTask);
    mHandler.postDelayed(mOneShotPreviewCallbackTask, delay);
  }

  /**
   * 停止识别
   */
  public void stopSpot() {
    cancelProcessDataTask();

    mSpotAble = false;

    if (mCamera != null) {
      try {
        mCamera.setOneShotPreviewCallback(null);
      } catch (Exception e) {
      }
    }
    if (mHandler != null) {
      mHandler.removeCallbacks(mOneShotPreviewCallbackTask);
    }
  }

  /**
   * 停止识别，并且隐藏扫描框
   */
  public void stopSpotAndHiddenRect() {
    stopSpot();
    hiddenScanRect();
  }

  /**
   * 显示扫描框，并且延迟1.5秒后开始识别
   */
  public void startSpotAndShowRect() {
    startSpot();
    showScanRect();
  }

  /**
   * 打开闪光灯
   */
  public void openFlashlight() {
    mPreview.openFlashlight();
  }

  /**
   * 关闭散光灯
   */
  public void closeFlashlight() {
    mPreview.closeFlashlight();
  }

  /**
   * 销毁二维码扫描控件
   */
  public void onDestroy() {
    stopCamera();
    mHandler = null;
    mDelegate = null;
    mOneShotPreviewCallbackTask = null;
  }

  /**
   * 取消数据处理任务
   */
  protected void cancelProcessDataTask() {
    if (mProcessDataTask != null) {
      mProcessDataTask.cancelTask();
      mProcessDataTask = null;
    }
  }

  /**
   * 切换成扫描条码样式
   */
  public void changeToScanBarcodeStyle() {
    if (!mScanBoxView.getIsBarcode()) {
      mScanBoxView.setIsBarcode(true);
    }
  }

  /**
   * 切换成扫描二维码样式
   */
  public void changeToScanQRCodeStyle() {
    if (mScanBoxView.getIsBarcode()) {
      mScanBoxView.setIsBarcode(false);
    }
  }

  /**
   * 当前是否为条码扫描样式
   *
   * @return
   */
  public boolean getIsScanBarcodeStyle() {
    return mScanBoxView.getIsBarcode();
  }

  @Override
  public void onPreviewFrame(final byte[] data, final Camera camera) {
    if (mSpotAble) {
      cancelProcessDataTask();
      mProcessDataTask = new ProcessDataTask(camera, data, this, mOrientation) {
        @Override
        protected void onPostExecute(String result) {
          if (mSpotAble) {
            if (mDelegate != null && !TextUtils.isEmpty(result)) {
              try {
                mDelegate.onScanQRCodeSuccess(result);
              } catch (Exception e) {
              }
            } else {
              try {
                camera.setOneShotPreviewCallback(QrCodeView.this);
              } catch (Exception e) {
              }
            }
          }
        }
      }.perform();
    }
  }

  private Runnable mOneShotPreviewCallbackTask = new Runnable() {
    @Override
    public void run() {
      if (mCamera != null && mSpotAble) {
        try {
          mCamera.setOneShotPreviewCallback(QrCodeView.this);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  };

  public interface Delegate {
    /**
     * 处理扫描结果
     *
     * @param result
     */
    void onScanQRCodeSuccess(String result);

    /**
     * 处理打开相机出错
     */
    void onScanQRCodeOpenCameraError();
  }

  public void setStartupDelay(int delay) {
    this.startupDelay = delay;
  }

  private class EventHandler implements QrCodeView.Delegate {
    @Override
    public void onScanQRCodeSuccess(String result) {
      WritableMap event = Arguments.createMap();
      event.putString("result", result);
      ReactContext reactContext = (ReactContext)getContext();
      reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
          getId(),
          "success",
          event);

      System.out.println("System now received message:" + result);
    }

    @Override
    public void onScanQRCodeOpenCameraError() {
      WritableMap event = Arguments.createMap();
      event.putString("error", "Failed to open camera");
      ReactContext reactContext = (ReactContext)getContext();
      reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
          getId(),
          "error",
          event);
    }

  }
}