//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop

#include "Main.h"
#include "Settings.h"
#include "System.IOUtils.hpp"
#include "FMX.DialogService.hpp"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "Winsoft.Android.Camera"
#pragma link "Winsoft.FireMonkey.Obr"
#pragma resource "*.fmx"
TFormMain *FormMain;
//---------------------------------------------------------------------------
__fastcall TFormMain::TFormMain(TComponent* Owner)
  : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ACameraDataReady(TObject *Sender)
{
  TThread::Queue(NULL,
    [this]()
    {
      try
      {
        try
        {
          ACamera->DataToRgba(&RgbaData[0], ACamera->PreviewRotation, ACamera->PreviewHorizMirror);
        }
        __finally
        {
          ACamera->ReleaseData();
        }

        if (ACamera->PreviewRotation == ro0 || ACamera->PreviewRotation == ro180)
          TACamera::UpdateBitmap(Image->Bitmap, &RgbaData[0], RgbaDataWidth, RgbaDataHeight);
        else
          TACamera::UpdateBitmap(Image->Bitmap, &RgbaData[0], RgbaDataHeight, RgbaDataWidth);

        if (!Scanning)
        {
          if (ScanBarcode.Length() > 0)
            LabelBarcode->Text = ScanBarcode;

          if (!ScanRectangle.Size.IsZero())
          {
            Rectangle->Position->Point = ScanRectangle.Location + TPointF((Image->Width - Image->Bitmap->Width / Scale) / 2, (Image->Height - Image->Bitmap->Height / Scale) / 2);
            Rectangle->Size->Size = ScanRectangle.Size;
            Rectangle->Visible = true;
          }
          else
            Rectangle->Visible = false;

          if ((ACamera->PreviewRotation == ro0) || (ACamera->PreviewRotation == ro180))
            ACamera->UpdateBitmap(ScanBitmap, &RgbaData[0], RgbaDataWidth, RgbaDataHeight);
          else
            ACamera->UpdateBitmap(ScanBitmap, &RgbaData[0], RgbaDataHeight, RgbaDataWidth);

          Scanning = true;
        }
      }
      catch (Exception &e)
      {
        LabelBarcode->Text = e.Message;
      }
    });
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ButtonSettingsClick(TObject *Sender)
{
  if (!ACamera->PermissionGranted)
    ACamera->RequestPermission();
  else
  {
    ACamera->Active = true;
    Stop();
    FormSettings->Show();
    ButtonStartStop->Enabled = true;
  }
}
//---------------------------------------------------------------------------
volatile bool ThreadTerminated;

void __fastcall TFormMain::Start()
{
  if (ACamera->PreviewFormat != ifNV21)
  {
    ACamera->PreviewFormat = ifNV21;
    if (ACamera->PreviewFormat != ifNV21)
      throw new Exception("Preview format has to be NV21");
  }

  RgbaDataWidth = ACamera->PreviewSize.Width;
  RgbaDataHeight = ACamera->PreviewSize.Height;
  RgbaData.Length = RgbaDataWidth * RgbaDataHeight;

  LoadLibrary(); // show trial message in UI thread

  ACamera->Start();
  ButtonTakePicture->Enabled = true;
  ButtonStartStop->Text = "Stop";

  ThreadTerminated = false;
  TThread::CreateAnonymousThread([this]()
  {
    TFObr *Obr = new TFObr(NULL);
    Obr->Active = true;
    while (!ThreadTerminated)
      try
      {
        if (!Scanning)
          Sleep(100);
        else
          try
          {
            ScanBarcode = "";
            ScanRectangle = TRectF::Empty();
            Obr->Picture = ScanBitmap;
            Obr->Scan();
            if (Obr->BarcodeCount > 0)
            {
              TObrSymbol *Barcode = Obr->Barcode[0];
              ScanBarcode = Barcode->SymbologyName + Barcode->SymbologyAddonName
                + " " + Barcode->OrientationName + " " + Barcode->DataUtf8;

              if (Barcode->LocationCount >= 2)
              {
                ScanRectangle.Left = Barcode->LocationX[0] / Scale;
                ScanRectangle.Top = Barcode->LocationY[0] / Scale;

                for (int I = 1; I < Barcode->LocationCount; ++I)
                {
                  TPointF Point;
                  Point.X = Barcode->LocationX[I] / Scale;
                  Point.Y = Barcode->LocationY[I] / Scale;

                  if (Point.X < ScanRectangle.Left)
                    ScanRectangle.Left = Point.X;
                  if (Point.X > ScanRectangle.Right)
                    ScanRectangle.Right = Point.X;
                  if (Point.Y < ScanRectangle.Top)
                    ScanRectangle.Top = Point.Y;
                  if (Point.Y > ScanRectangle.Bottom)
                    ScanRectangle.Bottom = Point.Y;
                }

                if (ScanRectangle.Height() < Scale)
                  ScanRectangle.SetHeight(Scale);
                if (ScanRectangle.Width() < Scale)
                  ScanRectangle.SetWidth(Scale);
              }
            }
          }
          __finally
          {
            Scanning = false;
          }
      }
      catch (...)
      {
      }
  })->Start();
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::Stop()
{
  ACamera->Stop();
  ButtonTakePicture->Enabled = false;
  ThreadTerminated = true;
  ButtonStartStop->Text = "Start";
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ButtonStartStopClick(TObject *Sender)
{
  if (ACamera->Capturing)
    Stop();
  else
    Start();
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ButtonTakePictureClick(TObject *Sender)
{
  ACamera->TakePicture();
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ACameraPictureTaken(TObject *Sender, System::TArray__1<System::Byte> Data)
{
  TFile::WriteAllBytes(System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetPublicPath(), "picture_" + FormatDateTime("yyyymmdd_hhnnss", Now()) + ".jpeg"), Data);
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::FormCreate(TObject *Sender)
{
  ACamera->OnPictureTaken = ACameraPictureTaken;
  ACamera->OnDisplayRationale = ACameraDisplayRationale;

  ScanBitmap = new TBitmap();
  Scale = ACamera->ScreenScale;

  if (TPlatformServices::Current->SupportsPlatformService(__uuidof(IFMXApplicationEventService)))
  {
    FMXApplicationEventService = TPlatformServices::Current->GetPlatformService(__uuidof(IFMXApplicationEventService));
    if (FMXApplicationEventService)
      FMXApplicationEventService->SetApplicationEventHandler(HandleAppEvent);
  }
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ACameraPermission(TObject *Sender)
{
  if (ACamera->PermissionGranted)
    ButtonSettingsClick(this);
  else
    ShowMessage("Cannot access the camera because the required permission has not been granted");
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::ACameraDisplayRationale(TObject *Sender, const _di_TProc APostRationaleProc)
{
  // Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response!
  // After the user sees the explanation, invoke the post-rationale routine to request the permissions
  TDialogService::ShowMessage("The application needs to access the camera in order to work",
    [APostRationaleProc](const TModalResult AResult)
    {
      APostRationaleProc->Invoke();
    });
}
//---------------------------------------------------------------------------
bool __fastcall TFormMain::HandleAppEvent(TApplicationEvent AAppEvent, TObject *AContext)
{
  if (ACamera->PermissionGranted)
    switch (AAppEvent)
    {
      case TApplicationEvent::EnteredBackground:
      {
        Paused = ACamera->Capturing;
        Stop();
        ACamera->Active = false;
        break;
      }

      case TApplicationEvent::WillBecomeForeground:
      {
        ACamera->Active = true;
        FormSettings->ApplySettings();
        if (Paused)
        {
          Paused = false;
          Start();
        }
        break;
      }

      default:
        break;
    }

  return true;
}
//---------------------------------------------------------------------------