package com.visagetechnologies.shapeanalysis;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.visagetechnologies.androidcameraplugin.CameraActivity;
import com.visagetechnologies.bsc.FrameProvider;
import com.visagetechnologies.makeupsdk.Effect;
import com.visagetechnologies.makeupsdk.IApplicationLifecycleHandler;
import com.visagetechnologies.makeupsdk.IMakeupObserver;
import com.visagetechnologies.makeupsdk.MakeupEngine;
import com.visagetechnologies.makeupsdk.SDKProvider;
import com.visagetechnologies.makeupsdk.FaceShapeAnalyzer;
import com.visagetechnologies.makeupsdk.EyeShapeAnalyzer;
import com.visagetechnologies.makeupsdk.LicenseTypeEnum;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.EnumMap;
import java.util.Map;

class ExternalMakeupObserver implements IMakeupObserver {
  private static String TAG = "ExternalMakeupObserver";

  @Override
  public void OnEffectCreated(MakeupEngine sender, Effect effect) {
    sender.DisplayMakeup(effect);
    Log.d(TAG, "OnEffectCreated effectID: " + Long.toString(effect.GetID()));
  }

  @Override
  public void OnEffectRemoved(MakeupEngine sender, Effect effect) {
    Log.d(TAG, "OnEffectRemoved effectID: " + Long.toString(effect.GetID()));
  }

  @Override
  public void OnEffectHidden(MakeupEngine sender, Effect effect) {
    Log.d(TAG, "OnEffectHidden effectID: " + Long.toString(effect.GetID()));
  }

  @Override
  public void OnEffectDisplayed(MakeupEngine sender, Effect effect) {
    Log.d(TAG, "OnEffectDisplayed effectID: " + Long.toString(effect.GetID()));
  }

  @Override
  public void OnClear(MakeupEngine effect) {
    Log.d(TAG, "OnClear");
  }

  @Override
  public void OnStart(MakeupEngine effect) {
    Log.d(TAG, "OnStart");
  }

  public void OnEffectUpdated(MakeupEngine sender, Effect effect) {
    Log.d(TAG, "OnEffectUpdated effectID: " + Long.toString(effect.GetID()));
  }
}

public class MainActivity extends Activity {

  static {
    System.loadLibrary("BeautySightCore");
    System.loadLibrary("FrameProvider");
  }

  public static String TAG = "MainActivity";

  SDKProvider sdkProvider;
  MakeupEngine makeupEngine;
  IApplicationLifecycleHandler lifecycleHandler;
  ExternalMakeupObserver externalObserver = new ExternalMakeupObserver();
  CameraActivity camera;

  FaceShapeAnalyzer faceShapeAnalyzer;
  private final int finishedAfterFramesFaceShape = 5;
  private int nFramesFaceShape;

  EyeShapeAnalyzer eyeShapeAnalyzer;
  private final int finishedAfterFramesEyeShape = 5;
  private int nFramesEyeShape;

  private String loadLicenceCached(String path, String mapName)  {
    AssetManager manager = this.getAssets();

    File f = new File(getCacheDir() + "/" + mapName);
    if (!f.exists())
      try {
        InputStream is = manager.open(path);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();

        FileOutputStream fos = new FileOutputStream(f);
        fos.write(buffer);
        fos.close();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      return f.getPath();
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);

    nFramesFaceShape = 0;
    nFramesEyeShape = 0;

    Map<LicenseTypeEnum, String> licenses = new EnumMap<LicenseTypeEnum, String>(LicenseTypeEnum.class);
    licenses.put(LicenseTypeEnum.MAKEUP, loadLicenceCached("license.vlc","ml.map"));
    licenses.put(LicenseTypeEnum.SKINTONE, loadLicenceCached("license_shadefinder.vlc","stl.map"));

    sdkProvider = SDKProvider.GetProvider(licenses, this);

    FrameProvider frameProvider = null;
    try {
      frameProvider = sdkProvider.GetFrameProvider();
    } catch (Exception e) {
      e.printStackTrace();
    }

    try {
      makeupEngine = sdkProvider.CreateMakeupEngine();
    } catch (Exception e) {
      e.printStackTrace();
    }

    try {
      faceShapeAnalyzer = sdkProvider.CreateFaceShapeAnalyzer();
    } catch (Exception e) {
      e.printStackTrace();
    }

    try {
      eyeShapeAnalyzer = sdkProvider.CreateEyeShapeAnalyzer();
    } catch (Exception e) {
      e.printStackTrace();
    }

    camera = new CameraActivity(this, frameProvider);
    camera.GrabFromCamera();

    makeupEngine.AddObserver(externalObserver);

    View unityPlayer = makeupEngine.GetView();
    lifecycleHandler = makeupEngine.GetLifecycleHandler();

    setContentView(R.layout.activity_main);
    ConstraintLayout constraintLayout = findViewById(R.id.constraint_layout);
    ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT,
            ConstraintLayout.LayoutParams.MATCH_PARENT);

    constraintLayout.addView(unityPlayer, layoutParams);

    unityPlayer.requestFocus();

    lifecycleHandler.onResume();
  }

  public void analyzeFaceShape(View v){
    faceShapeAnalyzer.StartAnalysis(new com.visagetechnologies.makeupsdk.FaceShapeCallback() {
        @Override
        public void done(com.visagetechnologies.bsc.FaceShapeResult result) {
        if(++nFramesFaceShape < finishedAfterFramesFaceShape) {
          return;
        }
        nFramesFaceShape = 0;
        faceShapeAnalyzer.StopAnalysis();
        
        runOnUiThread(new Runnable() {
          private com.visagetechnologies.bsc.FaceShapeEstimates estimates = result.getEstimates();
          private float heart = estimates.getHeart();
          private float oblong = estimates.getOblong();
          private float oval = estimates.getOval();
          private float round = estimates.getRound();
          private float square = estimates.getSquare();

          @Override
          public void run() {
            AlertDialog dialog = (new AlertDialog.Builder(MainActivity.this))
                    .setTitle(R.string.face_shape_dialog_title)
                    .setMessage(String.format(getString(R.string.face_shape_dialog_message), heart, oblong, oval, round, square))
                    .setPositiveButton(R.string.face_shape_dialog_button, new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int id) { }
                    }).create();

            dialog.show();
          }
        });
        }
    });
  }

  public void analyzeEyeShape(View v){
    eyeShapeAnalyzer.StartAnalysis(new com.visagetechnologies.makeupsdk.EyeShapeCallback() {
      @Override
      public void done(com.visagetechnologies.bsc.EyeShapeResult result) {
        if(++nFramesEyeShape < finishedAfterFramesEyeShape) {
          return;
        }
        nFramesEyeShape = 0;
        eyeShapeAnalyzer.StopAnalysis();

        runOnUiThread(new Runnable() {
          private com.visagetechnologies.bsc.EyeShapeEstimates estimates = result.getEstimates();
          private float round = estimates.getRound();
          private float almond = estimates.getAlmond();
          private float hooded = estimates.getHooded();
          private float monolid = estimates.getMonolid();

          @Override
          public void run() {
            AlertDialog dialog = (new AlertDialog.Builder(MainActivity.this))
                    .setTitle(R.string.eye_shape_dialog_title)
                    .setMessage(String.format(getString(R.string.eye_shape_dialog_message), round, almond, hooded, monolid))
                    .setPositiveButton(R.string.eye_shape_dialog_button, new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int id) { }
                    }).create();

            dialog.show();
          }
        });
      }
    });
  }

  // Quit Unity
  @Override
  protected void onDestroy() {
    camera.onDestroy();
    lifecycleHandler.onDestroy();
    super.onDestroy();
  }

  // Pause Unity
  @Override
  protected void onPause() {
    camera.onPause();
    super.onPause();
    lifecycleHandler.onPause();
  }

  // Resume Unity
  @Override
  protected void onResume() {
    camera.onResume();
    super.onResume();
    lifecycleHandler.onResume();
  }

  // Notify Unity of the focus change.
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    lifecycleHandler.onWindowFocusChanged(hasFocus);
  }
}
