/*
 * File:        main.cpp
 *
 * Production:  Cock Hero Inferno Canto 1
 *
 * Author:      Doremi and friends
 *
 * Date:        2019/11/29
 *
 * Version:     1.0
 *
 * Description: Play a video file using the VLC library in fullscreen mode.
 *              If the video size is bigger than the screen, it is shrunk
 *              to the screen size.  If the video size is smaller than the
 *              screen, the video is played as is surrounded by a black
 *              background.  Built with C++ Builder 10.3 and tested under
 *              Windows 7.
 *
 *              Accepted keyboard keys are:
 *                - 1            Jump to Round 1
 *                - 2            Jump to Round 2
 *                - 3            Jump to Round 3
 *                - 4            Jump to Round 4
 *                - 5            Jump to Round 5
 *                - 6            Jump to Round 6
 *                - 7            Jump to Extra Round 7
 *                - ENTER        I just came!  LOL!!!
 *                - ESC          Stop the video
 *                - ARROW RIGHT  Jump forward 10 seconds
 *                - ARROW LEFT   Jump backward 10 seconds
 *                - ARROW UP     Jump forward 1 minutes
 *                - ARROW DOWN   Jump backward 1 minutes
 *                - PAGE UP      Jump forward 10 minutes
 *                - PAGE DOWN    Jump backward 10 minutes
 *                - HOME         Jump to the beginning
 *                - END          Jump 10 seconds before the end
 *                - SPACE        Pause the video
 *
 *              Accepted keypad keys are:
 *                - +            Increase the volume
 *                - -            Decrease the volume
 *                - *            Change the audio track
 *
 * Parameters:  The-video-offset-millisec  The-video-file-to-play
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

 //---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "main.h"
#include <vlc/vlc.h>


/*
 *  Constants
 */

#define ERROR_CHVP_USAGE      1
#define ERROR_CHVP_MEM        2
#define ERROR_CHVP_VLC        3
#define ERROR_CHVP_LEN        4

#define CHVP_HSEC             500
#define CHVP_10SEC            10000
#define CHVP_1MIN             60000
#define CHVP_10MIN            600000

#define CHVP_STATE_PLAYING    1

#define LNSZ                  200


//---------------------------------------------------------------------------

#pragma package(smart_init)
#pragma resource "*.dfm"
TrfMain *rfMain;

/*
 *  Global variables
 */

static int                    giErr = 0,
                              giHero = 0,
                              giNumVlcAudioTracks = 0,
                              giOffset = 0,
                              giState = 0,
                              giVlcAudioTrack = 0,
                              *gpVlcAudioTrackId = NULL;
static char                   gszErr[LNSZ];
static libvlc_instance_t      *gpVlcInst = NULL;
static libvlc_media_player_t  *gpVlcPlayer = NULL;
static libvlc_time_t          giEndTimeMs = 0,
                              giPrevious = 0;

//---------------------------------------------------------------------------

int
Crossed(libvlc_time_t iPrevious, libvlc_time_t iRef, libvlc_time_t iCurrent)
{
   return (iPrevious < iRef && iRef <= iCurrent
           && (iRef-iPrevious < CHVP_HSEC || iCurrent-iRef <= CHVP_HSEC));
}

//---------------------------------------------------------------------------

libvlc_time_t
Offset(libvlc_time_t iTime)
{
   iTime += giOffset;
   if (iTime < 0)
      iTime = 0;
   else if (iTime > giEndTimeMs - CHVP_10SEC)
      iTime = giEndTimeMs - CHVP_10SEC;

   return (iTime);
}

//---------------------------------------------------------------------------

__fastcall TrfMain::TrfMain(TComponent* Owner)
   : TForm(Owner)
{
   *gszErr = 0;
}

//---------------------------------------------------------------------------

void
__fastcall TrfMain::FormCreate(TObject *Sender)
{
   int            i,
                  iScrX = 0,
                  iScrY = 0;
   unsigned int   iVidX,
                  iVidY;
   char           sz[LNSZ];
   libvlc_media_t *pVlcMedia;
   libvlc_track_description_t *pVlcAudioTrackDesc, *pVlcATD;


   // Init
   *sz = 0;
   iScrX = GetSystemMetrics(SM_CXSCREEN);
   iScrY = GetSystemMetrics(SM_CYSCREEN);

   for (i = 1 ; i <= ParamCount() ; i++)
      if (FileExists(ParamStr(i), true) && ParamStr(i).Length() < LNSZ)
         strcpy(sz, AnsiString(ParamStr(i)).c_str());
      else
         giOffset = atoi(AnsiString(ParamStr(i)).c_str());

   if (!(*sz))
      if (rfOpen->Execute())
         strcpy(sz, AnsiString(rfOpen->FileName).c_str());

   if (!(*sz))
      giErr = ERROR_CHVP_USAGE;

   if (!giErr)
   {
      // Load the VLC engine
      gpVlcInst = libvlc_new(0, NULL);
      if (!gpVlcInst)
      {
         giErr = ERROR_CHVP_VLC;
         strcat(gszErr, "libvlc_new() failed!");
      }
   }
   if (!giErr)
   {
      // Create a new item
      pVlcMedia = libvlc_media_new_path(gpVlcInst, sz);
      if (pVlcMedia)
      {
	      // Create a media player playing environement
         gpVlcPlayer = libvlc_media_player_new_from_media(pVlcMedia);
         if (!gpVlcPlayer)
         {
            giErr = ERROR_CHVP_VLC;
            strcat(gszErr, "libvlc_media_player_new_from_media() failed!");
         }

   	   // No need to keep the media now
         libvlc_media_release(pVlcMedia);
      }
      else
      {
         giErr = ERROR_CHVP_VLC;
         strcat(gszErr, "libvlc_media_new_path() failed!");
      }
   }
   if (!giErr)
   {
      libvlc_media_player_set_hwnd(gpVlcPlayer, rrView->Handle);
      libvlc_video_set_key_input(gpVlcPlayer, 0);
      libvlc_media_player_play(gpVlcPlayer);
      libvlc_media_player_set_pause(gpVlcPlayer, 1);
      Sleep(1000);  // Wait for the video to be loaded in memory
      libvlc_video_get_size(gpVlcPlayer, 0,     &iVidX, &iVidY);
      giEndTimeMs = libvlc_media_player_get_length(gpVlcPlayer);
      if (giEndTimeMs <= 0)
      {
         giErr = ERROR_CHVP_VLC;
         strcat(gszErr, "libvlc_media_player_get_length() failed!");
      }
      else if (giEndTimeMs < 4356064 - CHVP_HSEC + giOffset
               || giEndTimeMs > 4356064 + CHVP_HSEC + giOffset)
         giErr = ERROR_CHVP_LEN;
   }
   if (!giErr)
   {
      giNumVlcAudioTracks = libvlc_audio_get_track_count(gpVlcPlayer);
      if (giNumVlcAudioTracks > 1)
      {
         gpVlcAudioTrackId = (int *)malloc(giNumVlcAudioTracks * sizeof(int));
         if (gpVlcAudioTrackId)
         {
            memset(gpVlcAudioTrackId, 0,
                   sizeof(giNumVlcAudioTracks * sizeof(int)));
            pVlcAudioTrackDesc = pVlcATD =
               libvlc_audio_get_track_description(gpVlcPlayer);
            if (pVlcAudioTrackDesc)
            {
               for (i = 0 ; pVlcATD && i < giNumVlcAudioTracks ; i++)
               {
                  gpVlcAudioTrackId[i] = pVlcATD->i_id;
                  pVlcATD = pVlcATD->p_next;
               }
               libvlc_track_description_list_release(pVlcAudioTrackDesc);
            }
            else
               giNumVlcAudioTracks = 0;
         }
         else
            giErr = ERROR_CHVP_MEM;
      }
   }
   if (!giErr)
   {
      if ((int)iVidX >= iScrX || (int)iVidY >= iScrY)
      {
         rrView->Left = 0;
         rrView->Top = 0;
         rrView->Width = iScrX;
         rrView->Height = iScrY;
      }
      else
      {
         rrView->Left = (iScrX-(int)iVidX)/2;
         rrView->Top = (iScrY-(int)iVidY)/2;
         rrView->Width = (int)iVidX;
         rrView->Height = (int)iVidY;
      }

      libvlc_media_player_set_time(gpVlcPlayer, 0);
      libvlc_media_player_play(gpVlcPlayer);
      giState = CHVP_STATE_PLAYING;
   }

   if (giErr)
   {
      switch (giErr)
      {
         case ERROR_CHVP_USAGE:
            strcpy(sz, "USAGE: chvpinferno1 <filename>");
            break;

         case ERROR_CHVP_MEM:
            strcpy(sz, "ERROR: Out Of Memory!");
            break;

         case ERROR_CHVP_VLC:
       	   sprintf(sz, "VLC ERROR: %s", gszErr);
            break;

         case ERROR_CHVP_LEN:
            strcpy(sz, "ERROR: Video Length Not 1h12:36 !");
            break;

         default:
       	   sprintf(sz, "ERROR # %d", giErr);
            break;
      }

      giState = 0;
      Hide();
      Application->MessageBox(UnicodeString(sz).w_str(),
                              L"Cock Hero Inferno Canto 1", MB_OK);
      Application->Terminate();
      giErr = 0;
   }
}

//---------------------------------------------------------------------------

void
__fastcall TrfMain::rrTimerTimer(TObject *Sender)
{
   libvlc_state_t stateVlcPlayer;
   libvlc_time_t  iTimeMs;

   static int     iInTimer = 0;


   if (!iInTimer)
   {
      iInTimer = 1;

      switch (giState)
      {
         case CHVP_STATE_PLAYING:
            stateVlcPlayer = libvlc_media_player_get_state(gpVlcPlayer);
            if (stateVlcPlayer == libvlc_Stopped
                || stateVlcPlayer == libvlc_Ended
                || stateVlcPlayer == libvlc_Error)
            {
               giState = 0;
               Hide();
               Application->Terminate();
            }
            else
            {
               iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
               if (Crossed(giPrevious, Offset(3606148), iTimeMs))
               {
                  if (giHero == 1)
                  {
                     giHero++;
                     libvlc_media_player_set_time(gpVlcPlayer, Offset(4257883));
                  }
                  else
                     libvlc_media_player_pause(gpVlcPlayer);
               }
               else if (Crossed(giPrevious, Offset(2828036), iTimeMs)
                        || Crossed(giPrevious, Offset(3696989), iTimeMs)
                        || Crossed(giPrevious, Offset(3780113), iTimeMs)
                        || Crossed(giPrevious, Offset(3869995), iTimeMs)
                        || Crossed(giPrevious, Offset(3987237), iTimeMs)
                        || Crossed(giPrevious, Offset(4119453), iTimeMs)
                        || Crossed(giPrevious, Offset(4255589), iTimeMs))
                  libvlc_media_player_pause(gpVlcPlayer);

               giPrevious = iTimeMs;
            }
            break;
      }

      iInTimer = 0;
   }
}

//---------------------------------------------------------------------------

void
__fastcall TrfMain::FormKeyDown(TObject *Sender, WORD &Key,
                                     TShiftState Shift)
{
   int            i;
   libvlc_time_t  iTimeMs;


   switch(Key)
   {
      case vk1:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(113280));
         break;

      case vk2:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(423173));
         break;

      case vk3:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(800551));
         break;

      case vk4:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(1062187));
         break;

      case vk5:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(1538789));
         break;

      case vk6:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(2012137));
         break;

      case vk7:
         libvlc_media_player_set_time(gpVlcPlayer, Offset(2834084));
         break;

      case vkReturn:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
         if (iTimeMs > Offset(113280))
         {
            if (iTimeMs < Offset(423340))
               libvlc_media_player_set_time(gpVlcPlayer, Offset(3610152));
            else if (iTimeMs < Offset(798088))
               libvlc_media_player_set_time(gpVlcPlayer, Offset(3698156));
            else if (iTimeMs < Offset(1055806))
               libvlc_media_player_set_time(gpVlcPlayer, Offset(3783825));
            else if (iTimeMs < Offset(1526735))
               libvlc_media_player_set_time(gpVlcPlayer, Offset(3871538));
            else if (iTimeMs < Offset(2006965))
               libvlc_media_player_set_time(gpVlcPlayer, Offset(3991450));
            else if (iTimeMs < Offset(2755255))
               libvlc_media_player_set_time(gpVlcPlayer, Offset(4122456));
            else if (iTimeMs < Offset(3517309))
            {
               libvlc_media_player_pause(gpVlcPlayer);
               if (!giHero)
                  giHero--;
            }
            else if (iTimeMs < Offset(3532751))
            {
               libvlc_media_player_pause(gpVlcPlayer);
               if (!giHero)
                  giHero++;
            }
         }
         break;

      case vkDown:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
         if (iTimeMs <= CHVP_1MIN)
            iTimeMs = 0;
         else
            iTimeMs -= CHVP_1MIN;

         libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         break;

      case vkEnd:
         iTimeMs = giEndTimeMs - CHVP_10SEC;
         libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         break;

      case vkEscape:
         giState = 0;
         Hide();
         libvlc_media_player_stop(gpVlcPlayer);
         Application->Terminate();
         break;

      case vkHome:
         libvlc_media_player_set_time(gpVlcPlayer, 0);
         break;

      case vkSubtract:
         i = libvlc_audio_get_volume(gpVlcPlayer) - 10;
         if (i < 1)
            i = 1;

         libvlc_audio_set_volume(gpVlcPlayer, i);
         break;

      case vkAdd:
         i = libvlc_audio_get_volume(gpVlcPlayer) + 10;
         if (i > 100)
            i = 100;

         libvlc_audio_set_volume(gpVlcPlayer, i);
         break;

      case vkLeft:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
         if (iTimeMs <= CHVP_10SEC)
            iTimeMs = 0;
         else
            iTimeMs -= CHVP_10SEC;

         libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         break;

      case vkPrior:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
         if (iTimeMs < giEndTimeMs - CHVP_10SEC)
         {
            if (iTimeMs < giEndTimeMs - CHVP_10MIN)
               iTimeMs += CHVP_10MIN;
            else
               iTimeMs = giEndTimeMs - CHVP_10SEC;

            libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         }
         break;

      case vkMultiply:
         if (giNumVlcAudioTracks > 1)
         {
            giVlcAudioTrack++;
            if (giVlcAudioTrack >= giNumVlcAudioTracks)
               giVlcAudioTrack = 0;

            libvlc_audio_set_track(gpVlcPlayer,
                                   gpVlcAudioTrackId[giVlcAudioTrack]);
         }
         break;

      case vkNext:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
         if (iTimeMs <= CHVP_10MIN)
            iTimeMs = 0;
         else
            iTimeMs -= CHVP_10MIN;

         libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         break;

      case vkRight:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer) + CHVP_10SEC;
         if (iTimeMs < giEndTimeMs)
            libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         break;

      case vkSpace:
         libvlc_media_player_pause(gpVlcPlayer);
         break;

      case vkUp:
         iTimeMs = libvlc_media_player_get_time(gpVlcPlayer);
         if (iTimeMs < giEndTimeMs - CHVP_10SEC)
         {
            if (iTimeMs < giEndTimeMs - CHVP_1MIN)
               iTimeMs += CHVP_1MIN;
            else
               iTimeMs = giEndTimeMs - CHVP_10SEC;
            libvlc_media_player_set_time(gpVlcPlayer, iTimeMs);
         }
         break;
   }
}

//---------------------------------------------------------------------------

void
__fastcall TrfMain::FormClose(TObject *Sender, TCloseAction &Action)
{
   if (gpVlcPlayer)
      libvlc_media_player_stop(gpVlcPlayer);
}

//---------------------------------------------------------------------------

__fastcall TrfMain::~TrfMain(void)
{
   if (gpVlcPlayer)
   {
      libvlc_media_player_release(gpVlcPlayer);
      gpVlcPlayer = NULL;
   }

   if (gpVlcInst)
   {
	   libvlc_release(gpVlcInst);
      gpVlcInst = NULL;
   }
}

//---------------------------------------------------------------------------

