package com.decoding.twodtg.barcodereader2dtg;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Trace;
import android.preference.PreferenceManager;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.VideoView;

import java.nio.charset.Charset;

import decoding.twodtg.com.cameralib.CameraImageSize;
import decoding.twodtg.com.cameralib.TriggerListener;
import decoding.twodtg.com.twodtgdecodelib2.*;

import static android.view.KeyEvent.ACTION_UP;

public class MainActivity extends Activity implements DecodeOkDelegate, DialogInterface.OnKeyListener, TriggerListener, DialogInterface.OnCancelListener
{
  private TextView tapToScan;
  private SharedPreferences sp;
  private volatile boolean scan = false; // Flag the run of decoding
  private volatile BarcodeDecoder dec = null; // Decoder
  private BarcodeDecoderLibrary decLib = null; // Object for managment trial/licens
  private AlertDialog alertDialog = null; // MessageBox
  private VideoView video = null;   // View for show video from the camera
  private ImageView tapSettings = null;  // Icon sttings
  private ImageView zoomPlus = null;  // Icon for increment the camera zoom
  private ImageView zoomMinus = null; // Icon for decrement the camera zoom
  private TextView zoomTxt = null;  // Icon the camera zoom
  private ImageView flash = null;  // Flag the on/off camera light

  @Override
  protected void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    tapToScan = (TextView) this.findViewById(R.id.textViewTapToScan);
    video = (VideoView) this.findViewById(R.id.videoView);
    tapSettings = (ImageView) this.findViewById(R.id.imageViewSettings);
    zoomPlus = (ImageView) this.findViewById(R.id.imageViewZoomInc);
    zoomMinus = (ImageView) this.findViewById(R.id.imageViewZoomDec);
    zoomTxt = (TextView) this.findViewById(R.id.textViewZoom);
    flash = (ImageView) this.findViewById(R.id.imageViewFlash);

    sp = PreferenceManager.getDefaultSharedPreferences(this);

    decLib = new BarcodeDecoderLibrary();

    zoomPlus.setVisibility(View.INVISIBLE);
    zoomMinus.setVisibility(View.INVISIBLE);
    zoomTxt.setVisibility(View.INVISIBLE);

    // Create decoder and camera
    try
    {
      CreateDecoder();
    }catch(Exception e)
    {
      Utils.Failed(e);
    }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu)
  {
    MenuItem mi = menu.add(0, 1, 0, "Settings");
    mi.setIntent(new Intent(this, PrefsActivity.class));
    return super.onCreateOptionsMenu(menu);
  }

  public static void Create(Activity a)
  {
    Intent intent = new Intent(a, MainActivity.class);
    a.startActivity(intent);
  }

  // Show settings activity
  public void SettingsClick(View v)
  {
    // Destroy decoder and camera
    DestroyDecoder();
    Intent intent = new Intent(this, PrefsActivity.class);
    startActivity(intent);
  }

  // Start/stop scan and decoding
  public void VideoClick(View v)
  {
    synchronized(this)
    {
      try
      {
        if(scan)
        {
          StopScan();
          uiWait();
        }
        else
        {
          if(alertDialog != null)
          {
            alertDialog.cancel();
            alertDialog = null;
          }
          CameraImageSize imageSize = CameraSizeId();

          CreateDecoder();

          // Check trial end
          if(decLib.EvaluationDayCount(this) <= 0)
          {
            Intent intent = new Intent(this, ToSiteActivity.class);
            Bundle b = new Bundle();
            b.putBoolean("trialEnd", true);
            intent.putExtras(b);
            startActivity(intent);
            return;
          }
          scan = true;
          video.setBackgroundColor(Color.TRANSPARENT);// setBackground(null);

          // Start decoding
          if(!dec.DecodeCamera(video.getHolder(), this, imageSize))
          {
            scan = false;
            uiWait();
            DestroyDecoder();
          }
          UiScan();
        }
      } catch(Exception e)
      {
        Utils.Failed(e);
      }
    }
  }

  public void ZoomPlusClick(View v)
  {
    try
    {
      if(dec.getCamera().getZoom() < dec.getCamera().getMaxZoom())
      {
        dec.getCamera().IncreaseZoom();
        ShowZoom();
      }
      if(dec.getCamera().getZoom() == dec.getCamera().getMaxZoom())
        zoomPlus.setVisibility(View.INVISIBLE);
      zoomMinus.setVisibility(View.VISIBLE);
    }catch(Exception e)
    {
      Utils.Failed(e);
    }
  }

  public void ZoomMinusClick(View v)
  {
    try
    {
      if(dec.getCamera().getZoom() > 1)
      {
        dec.getCamera().DecreaseZoom();
        ShowZoom();
      }
      if(dec.getCamera().getZoom() == 1)
        zoomMinus.setVisibility(View.INVISIBLE);
      zoomPlus.setVisibility(View.VISIBLE);
    }catch(Exception e)
    {
      Utils.Failed(e);
    }
  }

  public void FlashClick(View v)
  {
    try
    {
      dec.getCamera().FlashSwitch();
    }catch(Exception e)
    {
      Utils.Failed(e);
    }
  }

  // Create decoder and camera
  private void CreateDecoder() throws DecoderException
  {
    synchronized(this)
    {
      if(dec != null) return;
      // Set the decoder options
      SetOptions();
      // Create decoder and camera
      dec = BarcodeDecoder.CreateDecoderForCamera(this, Barcodes());
      // Set view for show video from camera
      dec.setVideoView(video);
      // Set listener for trigger event.
      // Used for devices equipped with a hardware trigger, for example CT-50.
      dec.setTriggerListener(this);
    }
  }

  // Destroy decoder
  private void DestroyDecoder()
  {
    synchronized(this)
    {
      if(dec == null) return;
      dec.Destroy();
      dec = null;
    }
  }

  private void StopScan()
  {
    synchronized(this)
    {
      if(!scan) return;
      if(dec != null)
        dec.StopDecodeCamera();
      scan = false;
    }
  }

  private void UiScan()
  {
    tapToScan.setVisibility(View.INVISIBLE);
    tapSettings.setVisibility(View.INVISIBLE);
    if(dec.getCamera().getZoom() > 1)
      zoomMinus.setVisibility(View.VISIBLE);
    try
    {
      if((dec.getCamera().getMaxZoom() > 1) && (dec.getCamera().getZoom() < dec.getCamera().getMaxZoom()))
        zoomPlus.setVisibility(View.VISIBLE);
      if(dec.getCamera().getMaxZoom() > 1)
      {
        ShowZoom();
        zoomTxt.setVisibility(View.VISIBLE);
      }
    }
    catch(Exception e)
    {
      Utils.Failed(e);
    }
  }

  private void uiWait()
  {
    tapToScan.setVisibility(View.VISIBLE);
    tapSettings.setVisibility(View.VISIBLE);
    zoomPlus.setVisibility(View.INVISIBLE);
    zoomMinus.setVisibility(View.INVISIBLE);
    zoomTxt.setVisibility(View.INVISIBLE);
  }

  private void ShowZoom()
  {
    zoomTxt.setText(dec.getCamera().getZoom() + "X");
  }

  // Decode OK callback
  public void DecodeOK()
  {
    final Activity a = this;
    // Show results
    this.runOnUiThread(new Runnable()
    {
      @Override
      public void run()
      {
        scan = false;
        // Obtain the decoded text
        Charset charset = Charset.forName("UTF-8");
        String result = null;
        try
        {
          result = dec.GetDecodeResult(charset);
        } catch(Exception e)
        {
          Utils.Failed(e);
          return;
        }
        try
        {
          // Show the decoded string
          MessageBox(result, dec.CodeName());

          // Show the decoded bytes
          //MessageBox(ResultAsHexString(), dec.CodeName());
        }
        catch(Exception e)
        {
          Utils.MessageBoxError(a, e.getMessage());
        }
        uiWait();
      }
    });
  }

  public String ResultAsHexString()
  {
    byte[] data = dec.GetDecodeBytes();
    StringBuilder sb = new StringBuilder(data.length * 2);
    for(byte b: data)
      sb.append(String.format("%02x", b));
    return sb.toString();
  }

  @Override
  protected void onResume()
  {
    super.onResume();
    try
    {
      CreateDecoder();
    }catch(Exception e)
    {
      Utils.Failed(e);
    }
  }

  @Override
  protected void onPause()
  {
    super.onPause();
    StopScan();
    DestroyDecoder();
  }

  @Override
  protected void onStop()
  {
    super.onStop();
    StopScan();
    DestroyDecoder();
  }

  @Override
  protected void onDestroy()
  {
    super.onDestroy();
    StopScan();
    DestroyDecoder();
  }

  boolean triggerUp = false;
  long alertDialogCancelTime = 0;

  // Event from trigger
  public boolean TriggerEvent(boolean pressed)
  {
    if(alertDialog != null) return false;
    // Check closed alerdDialog
    if((System.currentTimeMillis() - alertDialogCancelTime) < 100 )
      return false;
    // Check that the decoding is already started by tapping the screen
    if(pressed == scan) return false;
    // Check the run of system decoder.
    // On some devices, simultaneous operation with the system decoder is not possible, example 70e black.
    if(dec.getCamera().SystemScanWedgeExists(this))
    {
      MessageBox("A system scanwedge is detected, please disable it!", "Warning!");
      return false;
    }
    //Trace("TriggerEvent " + pressed);
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        VideoClick(null);
      }
    });
    return true;
  }

  public void onCancel(DialogInterface dialog)
  {
    alertDialog = null;
  }

  public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
  {
    if(dec.getCamera().isTriggerKey(keyCode))
    {
      if(event.getAction() == ACTION_UP)
      {
        triggerUp = true;
      } else if(triggerUp)
      {
        alertDialog.cancel();
        alertDialog = null;
        triggerUp = false;
        alertDialogCancelTime = System.currentTimeMillis();
      }
      return true;
    }
    return false;
  }

  private void MessageBox(String msg, String caption)
  {
    try
    {
      alertDialog = new AlertDialog.Builder(this).create();
      alertDialog.setTitle(caption);
      alertDialog.setMessage(msg);
      alertDialog.getWindow().setGravity(Gravity.BOTTOM);
      alertDialog.setOnCancelListener(this);
      alertDialog.setOnKeyListener(this);
      alertDialog.show();
    }
    catch(Exception e)
    {
    }
  }

  private String getPreference(String key)
  {
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
    String str = SP.getString(key, "");
    if(str != null && str.length() == 0) return null;
    return str;
  }

  private Boolean getPreference(String key, boolean def)
  {
    try
    {
      SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
      return SP.getBoolean(key, def);
    } catch(Exception e)
    {
      Utils.Failed("get key", e);
      return def;
    }
  }

  private CameraImageSize CameraSizeId()
  {
    String cameraSizeStr = getPreference("FrameSize");
    CameraImageSize res;
    try
    {
      int w, h;
      int pos = cameraSizeStr.indexOf('x');
      w = Integer.parseInt(cameraSizeStr.substring(0, pos));
      h = Integer.parseInt(cameraSizeStr.substring(pos + 1));
      res = new CameraImageSize(w, h);
    } catch(Exception e)
    {
      res = new CameraImageSize(640, 480);
    }
    return res;
  }

  private boolean Linear()
  {
    return getPreference("Linear", true);
  }

  private boolean QR()
  {
    return getPreference("QR", true);
  }

  private boolean DMatrix()
  {
    return getPreference("DMatrix", true);
  }

  private boolean PDF417()
  {
    return getPreference("PDF417", false);
  }

  private boolean PharmaCode()
  {
    return getPreference("PharmaCode", false);
  }

  private boolean GS1Databar()
  {
    return getPreference("GS1", false);
  }

  private boolean Aztec()
  {
    return getPreference("Aztec", false);
  }

  private int Barcodes()
  {
    int res = 0;
    if(Linear())
      res = res | BarcodeDecoder.BarcodeType.LINEAR;
    if(QR())
      res = res | BarcodeDecoder.BarcodeType.QRCODE;
    if(DMatrix())
      res = res | BarcodeDecoder.BarcodeType.DMATRIX;
    if(PharmaCode())
      res = res | BarcodeDecoder.BarcodeType.PHARMACODE;
    if(PDF417())
      res = res | BarcodeDecoder.BarcodeType.PDF417;
    if(GS1Databar())
      res = res | BarcodeDecoder.BarcodeType.GS1DATABAR;
    if(Aztec())
      res = res | BarcodeDecoder.BarcodeType.AZTEC;
    return res;
  }

  private void SetOptions()
  {
    try
    {
      BarcodeDecoderOptions opt = BarcodeDecoder.Options();
      opt.DataMatrix_LabelMode = getDataMatrixLabelMode();
      opt.DataMatrix_SpeedMode = getDataMatrixSpeedMode();
      opt.QRCode_CellColor = getQRCodeCellColor();
      opt.PDF417_SpeedMode = getPdf417SpeedMode();
      opt.PDF417_DirectMode = getPdf417DirectMode();

      opt.UseImagerIfExistsAsVideoSource = getPreference("UseImagerIfExistsAsVideoSource", true);
    }catch(Exception e)
    {
      Utils.Failed("SetOptions", e);
    }
  }

  private int getPdf417SpeedMode() throws DecoderException
  {
    String str = getPreference("PDFSpeedMode");
    if(str == null) return BarcodeDecoderOptions.PDF417SpeedMode.SP_Fast;
    if(str.equals("SP_Robust")) return BarcodeDecoderOptions.PDF417SpeedMode.SP_Robust;
    if(str.equals("SP_Fast")) return BarcodeDecoderOptions.PDF417SpeedMode.SP_Fast;
    throw new DecoderException("Error option");
  }

  private int getPdf417DirectMode() throws DecoderException
  {
    String str = getPreference("PDFDirectMode");
    if(str == null) return BarcodeDecoderOptions.PDF417DirectMode.All;
    if(str.equals("Horizontal")) return BarcodeDecoderOptions.PDF417DirectMode.Horizontal;
    if(str.equals("All")) return BarcodeDecoderOptions.PDF417DirectMode.All;
    if(str.equals("Vertical")) return BarcodeDecoderOptions.PDF417DirectMode.Vertical;
    if(str.equals("LeftRight")) return BarcodeDecoderOptions.PDF417DirectMode.LeftRight;
    throw new DecoderException("Error option");
  }

  private int getDataMatrixSpeedMode() throws DecoderException
  {
    String str = getPreference("SpeedMode");
    if(str == null) return  BarcodeDecoderOptions.DataMatrixSpeeMode.DMSP_REGULAR;
    if(str.equals("SP_REGULAR")) return  BarcodeDecoderOptions.DataMatrixSpeeMode.DMSP_REGULAR;
    if(str.equals("SP_ULTIMATE")) return  BarcodeDecoderOptions.DataMatrixSpeeMode.DMSP_ULTIMATE;
    if(str.equals("SP_EXPRESS")) return  BarcodeDecoderOptions.DataMatrixSpeeMode.DMSP_EXPRESS;
    if(str.equals("SP_ULTIMATEPLUS")) return  BarcodeDecoderOptions.DataMatrixSpeeMode.DMSP_ULTIMATEPLUS;
    throw new DecoderException("Error option");
  }

  private int getDataMatrixLabelMode() throws DecoderException
  {
    String str = getPreference("LabelMode");
    if(str == null) return  BarcodeDecoderOptions.DataMatrixLabelMode.LM_REG_DOT;
    if(str.equals("LM_REGULAR")) return  BarcodeDecoderOptions.DataMatrixLabelMode.LM_REGULAR;
    if(str.equals("LM_REG_DOT")) return  BarcodeDecoderOptions.DataMatrixLabelMode.LM_REG_DOT;
    throw new DecoderException("Error option");
  }

  private int getQRCodeCellColor() throws DecoderException
  {
    String str = getPreference("QRCellColor");
    if(str == null) return BarcodeDecoderOptions.QRCodeCellColor.QRCL_BLACKONWHITE;
    if(str.equals("CL_BLACKONWHITE")) return BarcodeDecoderOptions.QRCodeCellColor.QRCL_BLACKONWHITE;
    if(str.equals("CL_WHITEONBLACK")) return BarcodeDecoderOptions.QRCodeCellColor.QRCL_WHITEONBLACK;
    if(str.equals("CL_ANY")) return BarcodeDecoderOptions.QRCodeCellColor.QRCL_ANY;
    throw new DecoderException("Error option");
  }

  @Override
  public void onBackPressed()
  {
    startActivity(new Intent(this, IntroActivity.class));
  }

}


