//---------------------------------------------------------------------------
#include <vcl.h>
#include <windows.h>
#include <dir.h>
#pragma hdrstop
#include "live_VideoEditor.h"
#include "avfile.h"
#include "avifile.h"
#include "RTEditor.h"
//---------------------------------------------------------------------------
//   Important note about DLL memory management when your DLL uses the
//   static version of the RunTime Library:
//
//   If your DLL exports any functions that pass String objects (or structs/
//   classes containing nested Strings) as parameter or function results,
//   you will need to add the library MEMMGR.LIB to both the DLL project and
//   any other projects that use the DLL.  You will also need to use MEMMGR.LIB
//   if any other projects which use the DLL will be performing new or delete
//   operations on any non-TObject-derived classes which are exported from the
//   DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling
//   EXE's to use the BORLNDMM.DLL as their memory manager.  In these cases,
//   the file BORLNDMM.DLL should be deployed along with your DLL.
//
//   To avoid using BORLNDMM.DLL, pass string information using "char *" or
//   ShortString parameters.
//
//   If your DLL uses the dynamic version of the RTL, you do not need to
//   explicitly add MEMMGR.LIB as this will be done implicitly for you
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
static int VideoRatio = 1;
//static AnsiString DllPath;
static HMODULE Module = NULL;

#pragma argsused
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) {
  if (reason == DLL_PROCESS_ATTACH) {
    Module = hinst;
    /*
    char p[MAXPATH];
    if (GetModuleFileName(hinst, p, sizeof(p)) > 0) {
      DllPath = ExtractFilePath(p);
    }
    */
  }
  return 1;
}

//---------------------------------------------------------------------------
void log(const char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  FILE *f = fopen("VideoEditorJava.log", "a");
  if (f) {
  	vfprintf(f, fmt, ap);
    fclose(f);
  }
  va_end(ap);
}

#if 1
//---------------------------------------------------------------------------
TRTEditor* getPointer(JNIEnv *env, jobject obj) {
  jclass cls = env->GetObjectClass(obj);
  jfieldID fid = env->GetFieldID(cls, "videoEditor", "I");
  return (TRTEditor*)env->GetIntField(obj, fid);
}

//---------------------------------------------------------------------------
void setPointer(JNIEnv *env, jobject obj, TRTEditor *pointer) {
  jclass cls = env->GetObjectClass(obj);
  jfieldID fid = env->GetFieldID(cls, "videoEditor", "I");
  env->SetIntField(obj, fid, (int)pointer);
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_open(JNIEnv *env, jobject obj, jstring fileName) {
  log("%s\n", "open");

  TRTEditor *av = getPointer(env, obj);
  if (av) {
    delete av;
    setPointer(env, obj, NULL);
  }

  const char *str = env->GetStringUTFChars(fileName, 0);
  int ret;
  try {
    av = new TRTEditor();
//    av->SetPrologSources(DllPath);
    av->SetModule(Module);
    av->Load(str, NULL);
    av->Init(-1, 80, 0, true, true);
//    av->SetTime(4.5 * 60 * 1000);
    if (av->HasVideo() || av->HasAudio()) {
      setPointer(env, obj, av);
      ret = 0;
    } else {
      ret = -1;
      delete av;
    }
  }
  catch (Exception &e) {
    log("open: %s\n", e.Message.c_str());
    ret = -1;
  }
  catch (...) {
    ret = -1;
  }
  env->ReleaseStringUTFChars(fileName, str);
  return ret;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_close(JNIEnv *env, jobject obj) {
  log("%s\n", "close");

  TRTEditor *av = getPointer(env, obj);
  if (av) {
    delete av;
    setPointer(env, obj, NULL);
  }
  return 0;
}

//---------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_live_VideoEditor_eof(JNIEnv *env, jobject obj) {
	TRTEditor *av = getPointer(env, obj);
  return av == NULL || av->GetTime() >= av->Sources.GetLength();
}

//---------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_live_VideoEditor_hasVideo(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  return av != NULL && av->HasVideo();
}

//---------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_live_VideoEditor_hasAudio(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  return av != NULL && av->HasAudio();
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getWidth(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    TAVFile::TFrameInfo fi = av->GetFrameInfo();
    return fi.Width / VideoRatio;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getHeight(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    TAVFile::TFrameInfo fi = av->GetFrameInfo();
    return fi.Height / VideoRatio;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jfloat JNICALL Java_live_VideoEditor_getFrameRate(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    TAVFile::TFrameInfo fi = av->GetFrameInfo();
    return (float)fi.FrameRate.Rate / (float)fi.FrameRate.Scale;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getChannels(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    TAVFile::TAudioInfo ai = av->GetAudioInfo();
    return ai.Channels;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getSampleRate(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    TAVFile::TAudioInfo ai = av->GetAudioInfo();
    return ai.SamplesPerSec;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getVideo(JNIEnv *env, jobject obj, jbyteArray frame) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    try {
      TImg *Img = av->GetVideo();
      if (Img) {
        if (VideoRatio > 1) {
          BYTE *tmp = new BYTE[(Img->Width / VideoRatio) * (Img->Height / VideoRatio) * 4];
          int *dst = (int*)tmp;
          int *src = (int*)Img->Bitmap;

          for (int y = 0; y < (Img->Height / VideoRatio); y++) {
            for (int x = 0; x < (Img->Width / VideoRatio); x++) {
              *dst = *src;
              dst++;
              src += VideoRatio;
            }
            src += Img->Width * (VideoRatio - 1);
          }

          env->SetByteArrayRegion(frame, 0, (Img->Width / VideoRatio) * (Img->Height / VideoRatio) * 4, tmp);
          delete[] tmp;
        } else
          env->SetByteArrayRegion(frame, 0, Img->Width * Img->Height * 4, Img->Bitmap);
				try {
        	delete Img;
        }
        catch (Exception &e) {
     		 log("delete\n");
    		}
        return 0;
      } else
        log("null video\n");
    }
    catch (Exception &e) {
      log("getVideo: %s\n", e.Message.c_str());
      log("time: %d\n", av->GetTime());
    }
    catch (...) {}
  }
  return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jbyteArray JNICALL Java_live_VideoEditor_getAudio(JNIEnv *env, jobject obj) {
  TRTEditor *av = getPointer(env, obj);
  if (av) {
    try {
      TWave *Wave = av->GetAudio();
      if (Wave) {
        jbyteArray audio = env->NewByteArray(Wave->Size);
        env->SetByteArrayRegion(audio, 0, Wave->Size, Wave->Wave);
        delete Wave;
        return audio;
      }
    }
    catch (Exception &e) {
      log("getAudio: %s\n", e.Message.c_str());
      log("time: %d\n", av->GetTime());
    }
    catch (...) {
    }
  }
  jbyteArray audio = env->NewByteArray(0);
  return audio;
}

#else
//---------------------------------------------------------------------------
TAVFile* getPointer(JNIEnv *env, jobject obj) {
  jclass cls = env->GetObjectClass(obj);
  jfieldID fid = env->GetFieldID(cls, "videoEditor", "I");
  return (TAVFile*)env->GetIntField(obj, fid);
}

//---------------------------------------------------------------------------
void setPointer(JNIEnv *env, jobject obj, TAVFile *pointer) {
  jclass cls = env->GetObjectClass(obj);
  jfieldID fid = env->GetFieldID(cls, "videoEditor", "I");
  env->SetIntField(obj, fid, (int)pointer);
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_open(JNIEnv *env, jobject obj, jstring fileName) {
  log("%s\n", "open");

  TAVFile *av = getPointer(env, obj);
  if (av) {
    delete av;
    setPointer(env, obj, NULL);
  }

  const char *str = env->GetStringUTFChars(fileName, 0);
  int ret;
  try {
    av = new TAVIFile();
    av->Open(str, TAVFile::omRead, TAVFile::otVideoAudio, false);
    if (av->GetOpenType() != TAVFile::otNone) {
      setPointer(env, obj, av);
      ret = 0;
    } else {
      delete av;
      ret -1;
    }
  }
  catch (...) {
    ret = -1;
  }
  env->ReleaseStringUTFChars(fileName, str);
  return ret;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_close(JNIEnv *env, jobject obj) {
  log("%s\n", "close");

  TAVFile *av = getPointer(env, obj);
  if (av) {
    delete av;
    setPointer(env, obj, NULL);
  }
  return 0;
}

//---------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_live_VideoEditor_hasVideo(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  return av != NULL && av->GetOpenMode() != TAVFile::omClosed && (av->GetOpenType() & TAVFile::otVideo);
}

//---------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_live_VideoEditor_hasAudio(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  return av != NULL && av->GetOpenMode() != TAVFile::omClosed && (av->GetOpenType() & TAVFile::otAudio);
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getWidth(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    TAVFile::TFrameInfo fi = av->GetFrameInfo();
    return fi.Width;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getHeight(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    TAVFile::TFrameInfo fi = av->GetFrameInfo();
    return fi.Height;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jfloat JNICALL Java_live_VideoEditor_getFrameRate(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    TAVFile::TFrameInfo fi = av->GetFrameInfo();
    return (float)fi.FrameRate.Rate / (float)fi.FrameRate.Scale;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getChannels(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    TAVFile::TAudioInfo ai = av->GetAudioInfo();
    return ai.Channels;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getSampleRate(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    TAVFile::TAudioInfo ai = av->GetAudioInfo();
    return ai.SamplesPerSec;
  } else
    return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_live_VideoEditor_getVideo(JNIEnv *env, jobject obj, jbyteArray frame) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    try {
      BYTE *f = av->GetFrame();
      TAVFile::TFrameInfo fi = av->GetFrameInfo();
      env->SetByteArrayRegion(frame, 0, fi.Width * fi.Height * 4, f);
      if (av->GetVideoPos() >= av->GetVideoLength())
        av->SeekVideo(0);
      return 0;
    }
    catch (...) {}
  }
  return -1;
}

//---------------------------------------------------------------------------
JNIEXPORT jbyteArray JNICALL Java_live_VideoEditor_getAudio(JNIEnv *env, jobject obj) {
  TAVFile *av = getPointer(env, obj);
  if (av) {
    try {
      unsigned int Size;
      BYTE *a = av->GetAudio(Size);
      jbyteArray audio = env->NewByteArray(Size);
      env->SetByteArrayRegion(audio, 0, Size, a);
      if (av->GetAudioPos() >= av->GetAudioLength())
        av->SeekAudio(0);
      return audio;
    }
    catch (...) {
    }
  }
  jbyteArray audio = env->NewByteArray(0);
  return audio;
}
#endif

