// ================================================================================================
// Given an HBITMAP (dibsection) and hardware dc, screen-capture
// ================================================================================================
int CaptureDC(HBITMAP hRawBitmap, HDC srcDC)
{
    // a compatible dc for the screen and offscreen
    HDC hSrcDC       = GetDC(NULL);
    HDC hMemDC	= CreateCompatibleDC(hSrcDC);

    if (!hMemDC)
        return 1;

    HBITMAP	hOldBitmap	= (HBITMAP) ::SelectObject(hMemDC, hRawBitmap);

    int result = BitBlt(hMemDC,				// destination
                        0, 0, kWidth, kHeight,		// destination position
                        srcDC,						// source
                        0, 0,						// source position
                        SRCCOPY);

    hRawBitmap = (HBITMAP)::SelectObject(hMemDC, hOldBitmap);
    // and now new stuff, actually get the data

    //BITMAP bitmap;
    //GetObject(hRawBitmap, sizeof(BITMAP), &bitmap);
    BITMAPINFO info;
    info.bmiHeader.biBitCount = GetTrueScreenDepth(hSrcDC);
    info.bmiHeader.biHeight = kHeight;
    info.bmiHeader.biWidth = kWidth;
    info.bmiHeader.biPlanes = 1;
    info.bmiHeader.biSize = kHeight*(kWidth)*info.bmiHeader.biBitCount/8; // NB this simplistic calculation assumes your width is divisible by 4
    char * pData = (char *) malloc(info.bmiHeader.biSize);
    GetDIBits(hSrcDC, hRawBitmap, 0, kHeight, pData, (BITMAPINFO *) &info.bmiHeader, DIB_RGB_COLORS);
    ::ReleaseDC(NULL, hSrcDC);

    free(pData);


    ::DeleteDC(hMemDC);
    ::DeleteDC(hSrcDC);
    ::GdiFlush();
    return 0;
}
// default child constructor...
CPushPinDesktop::CPushPinDesktop(HRESULT *phr, CPushSourceDesktop *pFilter)
    : CSourceStream(NAME("Push Source CPushPinDesktop child"), phr, pFilter, L"Capture"),
      m_FramesWritten(0),
      // m_bZeroMemory(0),
      m_iFrameNumber(0),
      //m_nCurrentBitDepth(32), // negotiated...
      m_pParent(pFilter),
      formatAlreadySet(false)
{

    // The main point of this sample is to demonstrate how to take a DIB
    // in host memory and insert it into a video stream.

    // To keep this sample as simple as possible, we just read the desktop image
    // from a file and copy it into every frame that we send downstream.
    //
    // In the filter graph, we connect this filter to the AVI Mux, which creates
    // the AVI file with the video frames we pass to it. In this case,
    // the end result is a screen capture video (GDI images only, with no
    // support for overlay surfaces).

    // Get the device context of the main display, just to get some metrics for it...
    globalStart = GetTickCount();

    hScrDc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL); // SLOW for aero desktop ...
    ASSERT(hScrDc != 0);
    // Get the dimensions of the main desktop window
    m_rScreen.left   = m_rScreen.top = 0;
    m_rScreen.right  = GetDeviceCaps(hScrDc, HORZRES);
    m_rScreen.bottom = GetDeviceCaps(hScrDc, VERTRES);

    m_iScreenBitRate = GetTrueScreenDepth(hScrDc);// no 15/16 diff here -> GetDeviceCaps(hScrDc, BITSPIXEL); // http://us.generation-nt.com/answer/get-desktop-format-help-26384242.html

    // my custom config settings...

    WarmupCounter();
    // assume 0 means not set...negative ignore :)
    // TODO no overflows, that's a bad value too... they crash it, I think! [position youtube too far bottom right, run it...]
    int config_start_x = read_config_setting(TEXT("start_x"));
    if(config_start_x != 0) { // negatives allowed...
        m_rScreen.left = config_start_x;
    }

    // is there a better way to do this registry stuff?
    int config_start_y = read_config_setting(TEXT("start_y"));
    if(config_start_y != 0) {
        m_rScreen.top = config_start_y;
    }

    int config_width = read_config_setting(TEXT("width"));
    ASSERT(config_width >= 0); // negatives not allowed...
    if(config_width > 0) {
        int desired = m_rScreen.left + config_width; // using DWORD here makes the math wrong to allow for negative values [dual monitor...]
        int max_possible = m_rScreen.right;
        if(desired < max_possible)
            m_rScreen.right = desired;
        else
            m_rScreen.right = max_possible;
    }

    int config_height = read_config_setting(TEXT("height"));
    ASSERT(config_width >= 0);
    if(config_height > 0) {
        int desired = m_rScreen.top + config_height;
        int max_possible = m_rScreen.bottom;
        if(desired < max_possible)
            m_rScreen.bottom = desired;
        else
            m_rScreen.bottom = max_possible;
    }

    // Save dimensions for later use in FillBuffer() et al
    m_iImageWidth  = m_rScreen.right  - m_rScreen.left;
    m_iImageHeight = m_rScreen.bottom - m_rScreen.top;

    int config_max_fps = read_config_setting(TEXT("default_max_fps")); // LODO allow floats in here, too!
    ASSERT(config_max_fps >= 0);
    if(config_max_fps == 0) {
        // TODO my force_max_fps logic is "off" by one frame, assuming it ends up getting used at all :P
        config_max_fps = 30; // set a high default so that the "caller" application knows that we can serve 'em up fast if desired...of course, if they just never set it then we'll probably be flooding them but who's problem is that, eh?
        // LODO only do this on some special pin or something [?] this way seems ok...
    }
    m_fFps = config_max_fps;
    m_rtFrameLength = UNITS / config_max_fps;
    wchar_t out[1000];
    swprintf(out, 1000, L"default from reg: %d %d %d %d -> %d %d %d %d %dfps\n", config_start_x, config_start_y, config_height, config_width,
             m_rScreen.top, m_rScreen.bottom, m_rScreen.left, m_rScreen.right, config_max_fps);
    LocalOutput(out);
    // does this work with flash?
    // set_config_string_setting(L"last_set_it_to", out);
}
// the default child constructor...
CPushPinDesktop::CPushPinDesktop(HRESULT *phr, CPushSourceDesktop *pFilter)
        : CSourceStream(NAME("Push Source CPushPinDesktop child/pin"), phr, pFilter, L"Capture"),
        m_FramesWritten(0),
		m_bReReadRegistry(0),
		m_bDeDupe(0),
        m_iFrameNumber(0),
		pOldData(NULL),
		m_bConvertToI420(false),
        //m_nCurrentBitDepth(32), // negotiated later...
		m_pParent(pFilter),
		m_bFormatAlreadySet(false),
		hRawBitmap(NULL)
{

    // Get the device context of the main display, just to get some metrics for it...
	globalStart = GetTickCount();

	m_iHwndToTrack = (HWND) read_config_setting(TEXT("hwnd_to_track"), NULL);
    hScrDc = GetDC(m_iHwndToTrack);
	m_iScreenBitDepth = GetTrueScreenDepth(hScrDc);
	ASSERT(hScrDc != 0);
	
	GdiSetBatchLimit(1); // disable any GDI...just in case this helps anybody...

    // Get the dimensions of the main desktop window as the default
    m_rScreen.left   = m_rScreen.top = 0;
    m_rScreen.right  = GetDeviceCaps(hScrDc, HORZRES); // NB this *fails* for dual monitor support currently... but we just get the wrong width by default, at least with aero windows 7 both can capture both monitors
    m_rScreen.bottom = GetDeviceCaps(hScrDc, VERTRES);

	// now read some custom settings...
	WarmupCounter();
    reReadCurrentPosition(0);

	int config_width = read_config_setting(TEXT("capture_width"), 0);
	ASSERT(config_width >= 0); // negatives not allowed...
	int config_height = read_config_setting(TEXT("capture_height"), 0);
	ASSERT(config_height >= 0); // negatives not allowed, if it's set :)

	if(config_width > 0) {
		int desired = m_rScreen.left + config_width;
		//int max_possible = m_rScreen.right; // disabled check until I get dual monitor working. or should I allow off screen captures anyway?
		//if(desired < max_possible)
			m_rScreen.right = desired;
		//else
		//	m_rScreen.right = max_possible;
	} else {
		// leave full screen
	}

	m_iCaptureConfigWidth = m_rScreen.right - m_rScreen.left;
	ASSERT(m_iCaptureConfigWidth  > 0);

	if(config_height > 0) {
		int desired = m_rScreen.top + config_height;
		//int max_possible = m_rScreen.bottom; // disabled, see above.
		//if(desired < max_possible)
			m_rScreen.bottom = desired;
		//else
		//	m_rScreen.bottom = max_possible;
	} else {
		// leave full screen
	}
	m_iCaptureConfigHeight = m_rScreen.bottom - m_rScreen.top;
	ASSERT(m_iCaptureConfigHeight > 0);	

	m_iStretchToThisConfigWidth = read_config_setting(TEXT("stretch_to_width"), 0);
	m_iStretchToThisConfigHeight = read_config_setting(TEXT("stretch_to_height"), 0);
	m_iStretchMode = read_config_setting(TEXT("stretch_mode_high_quality_if_1"), 0);
	ASSERT(m_iStretchToThisConfigWidth >= 0 && m_iStretchToThisConfigHeight >= 0 && m_iStretchMode >= 0); // sanity checks

	// default 30 fps...hmm...
	int config_max_fps = read_config_setting(TEXT("default_max_fps"), 30); // TODO allow floats [?] when ever requested
	ASSERT(config_max_fps >= 0);	

	// m_rtFrameLength is also re-negotiated later...
  	m_rtFrameLength = UNITS / config_max_fps; 

	if(is_config_set_to_1(TEXT("track_new_x_y_coords_each_frame_if_1"))) {
		m_bReReadRegistry = 1; // takes 0.416880ms, but I thought it took more when I made it off by default :P
	}
	if(is_config_set_to_1(TEXT("dedup_if_1"))) {
		m_bDeDupe = 1; // takes 10 or 20ms...but useful to me! :)
	}
	m_millisToSleepBeforePollForChanges = read_config_setting(TEXT("millis_to_sleep_between_poll_for_dedupe_changes"), 10);


    wchar_t out[1000];
	swprintf(out, 1000, L"default/from reg read config as: %dx%d -> %dx%d (%dtop %db %dl %dr) %dfps, dedupe? %d, millis between dedupe polling %d, m_bReReadRegistry? %d \n", 
	  m_iCaptureConfigHeight, m_iCaptureConfigWidth, getCaptureDesiredFinalHeight(), getCaptureDesiredFinalWidth(), m_rScreen.top, m_rScreen.bottom, m_rScreen.left, m_rScreen.right, config_max_fps, m_bDeDupe, m_millisToSleepBeforePollForChanges, m_bReReadRegistry);

	LocalOutput(out); // warmup for the below debug :)
	__int64 measureDebugOutputSpeed = StartCounter();
	LocalOutput(out);
	LocalOutput("writing a large-ish debug itself took: %.0Lf ms", GetCounterSinceStartMillis(measureDebugOutputSpeed));
	// does this work with flash?
	set_config_string_setting(L"last_init_config_was", out);
}