#include <registry.h>
#include <except.h>
#include <cstring>
#include <wine/winreg.h>
#include <wine/vfw.h>
#include <wine/driver.h>
#include <stdio.h>

#if defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/time.h>
#endif

#include "Module.h"

#define __MODULE__ "Win32 loader"

using namespace std;
extern "C" void setup_FS_Segment();

VideoCodecControl control;

Module::Module(string name, VideoCodecControl& parent)
    :_parent(parent), forgotten(0), _refcount(0), _name(name)
{
    _handle = LoadLibraryA(name.c_str());
    if (!_handle)
    {
	char e[150];
	strcpy(e, "Could not load Win32 dll library: ");
	strncat(e, name.c_str(), 100);
	throw FATAL(e);
    }
    try
    {
	if((name==string("divxc32.dll")) || (name==string("divxc32f.dll")))
	{
	    int newkey;
	    int bitrate;
	    int count = 4;
	    const char* keyname;
	    if (name == "divxc32.dll")
		keyname = "Software\\LinuxLoader\\div3";
	    else
		keyname = "Software\\LinuxLoader\\div4";

	    if(RegOpenKeyExA(HKEY_CURRENT_USER, keyname, 0, 0, &newkey)!=0)
	    {
		//		cerr<<"Could not open Divx key"<<endl;
		goto no_reg;
	    }
	    if(RegQueryValueExA(newkey, "BitRate", 0, 0, &bitrate, &count)==0)
		*(double*)((char*)_handle+0x14c0)=bitrate;
	    if(RegQueryValueExA(newkey, "KeyFrames", 0, 0, &bitrate, &count)==0)
		*(long*)((char*)_handle+0x28af)=bitrate;
	    if(RegQueryValueExA(newkey, "Crispness", 0, 0, &bitrate, &count)==0)
		*(long*)((char*)_handle+0x28a8)=bitrate;
	no_reg:
	    RegCloseKey(newkey);
	}

	DriverProc=(DRIVER_PROC) GetProcAddress(_handle, "DriverProc");
	if (!DriverProc)
	{
	    char e[150];
	    strcpy(e, "Not a valid Win32 dll library: ");
	    strncat(e, name.c_str(), 100);
	    throw FATAL(e);
	}

	driver.uDriverSignature=0;
	driver.hDriverModule=_handle;
	driver.DriverProc=(DRIVERPROC)DriverProc;
	driver.dwDriverID=0;

	setup_FS_Segment();

	STORE_ALL;
	DriverProc(0, (int)&driver, DRV_LOAD, 0, 0);
	REST_ALL;

	STORE_ALL;
	DriverProc(0, (int)&driver, DRV_ENABLE, 0, 0);
	REST_ALL;

	printf("Using Win32 dll library: %s\n", name.c_str());
    }
    catch(FatalError& e)
    {
	FreeLibrary(_handle);
	throw;
    }
}

Module::~Module()
{
    setup_FS_Segment();

    DriverProc(0, (int)&driver, DRV_FREE, 0, 0);
    FreeLibrary(_handle);
    if (!forgotten)
	_parent.Erase(this);
}


int Module::CreateHandle(int compressor, Mode mode)
{
    ICOPEN icopen;
    icopen.fccType    = mmioFOURCC('v', 'i', 'd', 'c');
    icopen.fccHandler = compressor;
    icopen.dwSize     = sizeof(ICOPEN);
    icopen.dwFlags    = (mode == Compress) ? ICMODE_COMPRESS : ICMODE_DECOMPRESS;

    driver.dwDriverID = ++_refcount;
    DRVR* hDriver = new DRVR(driver);
    // cerr<<"Creating new handle for _refcount "<<_refcount<<", driver "<<(int)hDriver<<endl;
    // cerr.setf(ios::hex, ios::basefield);
    // cerr<<"fcc: "<<icopen.fccType<<", handler: "<<icopen.fccHandler<<endl;
    // cerr.setf(ios::dec, ios::basefield);

    setup_FS_Segment();

    STORE_ALL;
    hDriver->dwDriverID=DriverProc((int)_refcount, (int)hDriver, DRV_OPEN,
				   0, (int) &icopen);
    REST_ALL;
    // cerr<<"New ID "<<hDriver->dwDriverID<<endl;
    if (!hDriver->dwDriverID)
	throw FATAL("Module::CreateHandle: DRV_OPEN failed");

    return (int) hDriver;
}

int Module::CloseHandle(int handle)
{
    setup_FS_Segment();

    DRVR* hDriver = (DRVR*) handle;
    DriverProc(hDriver->dwDriverID, (int)hDriver, DRV_CLOSE, 0, 0);
    _refcount--;
    if (_refcount==0)
	delete this;
    delete hDriver;

    return 0;
}

//FIXME - handle should be void*
int Module::Message(int handle, int message, int arg1, int arg2)
{
    DRVR* module= (DRVR*) handle;
    char qw[300];

    setup_FS_Segment();

#if defined(__FreeBSD__) || defined(__NetBSD__)
    /*   XXX
     *	disable Thread Context Switch in Windows Dll Calls.
     *	Because Thread Switched Win32 Dll Calls <-> another threads
     *      broken cpu registers ???
     */
    struct itimerval itoval;
    struct itimerval itval;
    memset(&itval,0,sizeof(struct itimerval));
    setitimer(ITIMER_PROF ,&itval, &itoval);
#endif

#ifndef __svr4__
    __asm__ __volatile__ ("fsave (%0)\n\t": :"r"(&qw));
#endif

    int result;
    STORE_ALL;
    result=DriverProc(module->dwDriverID, handle, message, arg1, arg2);
    REST_ALL;

#ifndef __svr4__
    __asm__ __volatile__ ("frstor (%0)\n\t": :"r"(&qw));
#endif

#if defined(__FreeBSD__) || defined(__NetBSD__)
    setitimer(ITIMER_PROF,&itoval,NULL);
#endif
    return result;
}
