//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
//

#include "main.h"

#include "DDrawUty.h"
#include "DDTestKit_Iterators.h"

using namespace com_utilities;
using namespace DebugStreamUty;
using namespace DDrawUty;

namespace TestKit_Surface_Iterators
{
    ////////////////////////////////////////////////////////////////////////////////
    // CIterateSurfaces::TestIDirectDraw
    //  
    //
    eTestResult CIterateTKSurfaces::TestIDirectDraw()
    {
        eTestResult tr = trPass;
        vectSurfPixPair_itr itr;
        TCHAR szDescSurf[128];
        HRESULT hr;

        // Call the Pre-Test configuration method
        tr |= PreSurfaceTest();
        if (trPass != tr)
            return tr;

        // Handle the case that the test wants to be called with a
        // NULL surface pointer.  This is usually used by the test
        // to create and test unusual surfaces, such as large flipping
        // chains. 
        if (m_dwSurfCategories & scNull)
        {
            m_piDDS = NULL;
            dbgout << "Testing Surface : NULL" << indent;
            tr |= RunSurfaceTest();
            dbgout << unindent << "Done" << endl;
        }

        // Iterate all desired surfaces
        for (itr = m_vectPairs.begin(); itr != m_vectPairs.end(); itr++)
        {
            hr = g_DDConfig.GetSurfaceDesc((*itr).first, (*itr).second, m_cddsd, szDescSurf, countof(szDescSurf));
            // Skip any unsupported surface types
            if (S_OK != hr)
            {
                dbgout << "Surface Unsupported" << endl;
                continue;
            }    

            // See if this is a primary surface
            if (m_cddsd.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
            {
                // See if the test wants to be called for primary surfaces
                if ((m_dwSurfCategories & scPrimary) || (m_dwSurfCategories & scBackbuffer))
                {
                    dbgout << "Testing Source Surface : " << szDescSurf << indent;
                    
                    // Handle this as a primary surface
                    g_DDSPrimary.m_cddsd = m_cddsd;
                    m_piDDS = g_DDSPrimary.GetObject();
                    
                    if (m_piDDS != NULL)
                    {
                        // Get the actual surface description
                        m_piDDS->GetSurfaceDesc(&m_cddsd);
                        
                        // Run the test on the primary, if requested
                        if (m_dwSurfCategories & scPrimary)
                        {
                            dbgout << "Testing Source Surface : Primary" << indent;
                            tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDS.AsInParam());
                            if(tr==trPass)
                            {
                                tr |= RunSurfaceTest();
                            }
                            m_piDDS.ReleaseInterface();
                            dbgout << unindent << "Done" << endl;
                        }

                        // If there is a backbuffer and the test wants to
                        // be called for backbuffers
                        if ((m_cddsd.dwBackBufferCount > 0) && (m_dwSurfCategories & scBackbuffer))
                        {
                            dbgout << "Testing Source Surface : BackBuffer 1" << indent;
                            m_piDDS = g_DDSBackbuffer.GetObject();

                            if (m_piDDS != NULL)
                            {
                                m_piDDS->GetSurfaceDesc(&m_cddsd);
                                tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDS.AsInParam());
                                if(!(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_FLIP))
                                {
                                    dbgout << "Creation of a surface with BackBuffers succeded, but flipping unsupported" << endl;
                                    tr |= trFail;
                                }
                                if(tr==trPass)
                                {
                                    tr |= RunSurfaceTest();
                                }
                                m_piDDS.ReleaseInterface();
                            }
                            else
                                tr |= trAbort;
                            g_DDSBackbuffer.ReleaseObject();
                            dbgout << unindent << "Done" << endl;
                        }
                    }
                    else if((m_cddsd.dwBackBufferCount > 0) && ((g_DDSPrimary.GetLastError() == DDERR_OUTOFMEMORY) || (g_DDSPrimary.GetLastError() == DDERR_OUTOFVIDEOMEMORY)))
                    {
                        // if there is a back buffer and an out of memory error, skip test
                        dbgout << "Out of memory creating primary surface with back buffer(s)" << indent;
                    }
                    else if((DDERR_NOFLIPHW == g_DDSPrimary.GetLastError() || DDERR_INVALIDCAPS == g_DDSPrimary.GetLastError()) && 
                        (m_cddsd.ddsCaps.dwCaps & DDSCAPS_FLIP) &&
                        (!(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_FLIP) ||
                        !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)))
                    {
                        dbgout << "Flipping unsupported, unable to create surface with backBuffers";
                    }
                    else
                        tr |= trAbort;
                    
                    g_DDSPrimary.ReleaseObject();
                    dbgout << unindent << "Done" << endl;
                }
            }
            else
            {
                // See if the surface matches the test's desired criteria
                if (((m_cddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) && !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_OWNDC) && (m_dwSurfCategories & scOffScrSys)) ||
                    (((m_cddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) && (m_cddsd.ddsCaps.dwCaps & DDSCAPS_OWNDC)) && (m_dwSurfCategories & scOffScrSysOwnDc)) ||
                    ((m_cddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)  && (m_dwSurfCategories & scOffScrVid)) ||
                    ((m_cddsd.ddsCaps.dwCaps & DDSCAPS_OVERLAY)      && (m_dwSurfCategories & scOverlay)))
                {
                    dbgout << "Testing Source Surface : " << szDescSurf << indent;
                    
                    bool fRetrySmaller;
                    do
                    {
                        fRetrySmaller = false;
                        // Handle this as a simple surface
                        hr = m_piDD->CreateSurface(&m_cddsd, m_piDDS.AsTestedOutParam(), NULL);
                        if (DDERR_GENERIC == hr)
                        {
                            dbgout << "Surface not supported" << endl;
                        } else
                        if (DDERR_OUTOFVIDEOMEMORY == hr || DDERR_OUTOFMEMORY == hr)
                        {
                            tr |= OutOfMemory_Helpers::VerifyOOMCondition(hr, m_piDD.AsInParam(), m_cddsd);
                        }
                        else if ((DDERR_NOOVERLAYHW == hr || DDERR_INVALIDCAPS == hr) &&
                                 ((m_cddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) ||
                                  !(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_OVERLAY)))
                        {
                            // If we got a no overlay hardware error, the test still passes as
                            // long as either the surface is in system memory or if the HAL
                            // doesn't support overlays.
                            dbgout << "Overlay not supported ... Skipping" << endl;
                        }
                        else if ((DDERR_INVALIDPIXELFORMAT == hr || DDERR_UNSUPPORTEDFORMAT == hr) && (m_cddsd.ddsCaps.dwCaps & DDSCAPS_OVERLAY))
                        {
                            // Unsupported formats are ok for overlay surfaces, since the
                            // support is driver dependent.
                            dbgout << "Overlay of this pixel format not supported, please verify that this should not be supported. Skipping" << endl;
                        }
                        else if ((DDERR_UNSUPPORTEDFORMAT == hr) && 
                            !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_OVERLAY) && 
                            (m_cddsd.ddpfPixelFormat.dwFlags & DDPF_FOURCC))
                        {
                            // FourCC formats can be only supported for overlays
                            dbgout << "FourCC format not supported on non-overlay surface ... Skipping" << endl;
                        }
                        else if ((DDERR_TOOBIGHEIGHT == hr || DDERR_TOOBIGWIDTH == hr || DDERR_TOOBIGSIZE == hr) && 
                            !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
                        {
                            dbgout << "Surface too large (received " << hr << "). Will attempt smaller surface" << endl;
                            m_cddsd.dwWidth = (m_cddsd.dwWidth * 3 / 4) & (~0x3f);
                            m_cddsd.dwHeight = (m_cddsd.dwHeight * 3 / 4) & (~0x3f);
                            if (0 == m_cddsd.dwWidth || 0 == m_cddsd.dwHeight)
                            {
                                dbgout(LOG_FAIL) << "Unable to find small enough surface." << endl;
                                tr |= trFail;
                            }
                            else
                            {
                                dbgout << "Trying with surface of size " << m_cddsd.dwWidth << "x" << m_cddsd.dwHeight << endl;
                                fRetrySmaller = true;
                            }
                        }
                        else if (FAILED(hr))
                        {
                            QueryHRESULT(hr, "CreateSurface", tr |= trFail);
                            dbgout(LOG_FAIL)<< "NOTE: DirectDraw driver must now handle creation of system memory surfaces" << endl;
                        }
                        else QueryCondition(m_piDDS.InvalidOutParam(), "CreateSurface succeeded but failed to set the OUT parameter", tr |= trFail)
                        else
                        {
                            tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDS.AsInParam());
                            if(tr==trPass)
                            {    
                                // Run the test on the surface
                                tr |= RunSurfaceTest();
                            }
                            m_piDDS.ReleaseInterface();
                        }
                    } while (fRetrySmaller);
                    dbgout << unindent << "Done" << endl;
                }
            }
        }

        // Call the post-test method
        tr |= PostSurfaceTest();
        
        return tr;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // CIterateSurfaces_TWO::TestIDirectDrawSurface
    // 
    //
    eTestResult CIterateTKSurfaces_TWO::TestIDirectDrawSurface()
    {
        eTestResult tr = trPass;
        vectSurfPixPair_itr itr;
        TCHAR szDescSurf[128];
        HRESULT hr;

        for (itr = m_vectPairsDst.begin(); itr != m_vectPairsDst.end(); itr++)
        {
            hr = g_DDConfig.GetSurfaceDesc((*itr).first, (*itr).second, m_cddsdDst, szDescSurf, countof(szDescSurf));
            // Skip any unsupported surface types
            if (S_OK != hr)
            {
                dbgout << "Surface Unsupported" << endl;
                continue;
            }

            // If our current destination is a primary
            if (m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
            {
                // If the source is not a primary and not a backbuffer, we can
                // create primary surfaces as the destination, if the
                // test has requested it
                if (!(m_cddsd.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE) &&
                    !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) &&
                    ((m_dwSurfCategoriesDst & scPrimary) || (m_dwSurfCategoriesDst & scBackbuffer)))
                {
                    dbgout << "Testing Destination Surface : " << szDescSurf << indent;
                    
                    g_DDSPrimary.m_cddsd = m_cddsdDst;
                    m_piDDSDst = g_DDSPrimary.GetObject();
                    
                    if (m_piDDSDst != NULL)
                    {
                        // Get the actual surface description
                        m_piDDSDst->GetSurfaceDesc(&m_cddsdDst);
                        
                        // Run the test on the surface, if requested
                        if (m_dwSurfCategoriesDst & scPrimary)
                        {
                            dbgout << "Testing Destination Surface : Primary" << indent;
                            tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDSDst.AsInParam());
                            if(tr==trPass)
                            {
                                tr |= RunSurfaceTest_TWO();
                            }
                            m_piDDSDst.ReleaseInterface();
                            dbgout << unindent << "Done" << endl;
                        }
                        
                        // If there is a backbuffer and the test is to be run
                        // on the backbuffer
                        if ((m_cddsdDst.dwBackBufferCount > 0) && (m_dwSurfCategoriesDst & scBackbuffer))
                        {
                            dbgout << "Testing Destination Surface : BackBuffer 1" << indent;
                            m_piDDSDst = g_DDSBackbuffer.GetObject();
                            if (m_piDDSDst != NULL)
                            {
                                m_piDDSDst->GetSurfaceDesc(&m_cddsdDst);
                                tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDSDst.AsInParam());

                                if(!(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_FLIP))
                                {
                                    dbgout << "Creation of a surface with BackBuffers succeded, but flipping unsupported" << endl;
                                    tr |= trFail;
                                }

                                if(tr==trPass)
                                {
                                    tr |= RunSurfaceTest_TWO();
                                }
                                m_piDDSDst.ReleaseInterface();
                            }
                            else
                                tr |= trAbort;
                            g_DDSBackbuffer.ReleaseObject();
                            dbgout << unindent << "Done" << endl;
                        }
                    }
                    else if((m_cddsd.dwBackBufferCount > 0) && ((g_DDSPrimary.GetLastError() == DDERR_OUTOFMEMORY) || (g_DDSPrimary.GetLastError() == DDERR_OUTOFVIDEOMEMORY)))
                    {
                        // if there is a back buffer and an out of memory error, skip test
                        dbgout << "Out of memory creating primary surface with back buffer(s)" << indent;
                    }
                    else if((DDERR_NOFLIPHW == g_DDSPrimary.GetLastError() || DDERR_INVALIDCAPS == g_DDSPrimary.GetLastError()) && 
                            (m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_FLIP) &&
                            (!(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_FLIP) ||
                            !(m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)))
                    {
                        dbgout << "Flipping unsupported, unable to create surface with backBuffers";
                    }
                    else
                        tr |= trAbort;
                        
                    g_DDSPrimary.ReleaseObject();
                    dbgout << unindent << "Done Destination" << endl;
                }
            }
            else
            {
                // See if the surface matches the test's desired criteria
                if (((m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) && !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_OWNDC) && (m_dwSurfCategoriesDst & scOffScrSys)) ||
                    (((m_cddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) && (m_cddsd.ddsCaps.dwCaps & DDSCAPS_OWNDC)) && (m_dwSurfCategories & scOffScrSysOwnDc)) ||
                    ((m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)  && (m_dwSurfCategoriesDst & scOffScrVid)) ||
                    ((m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_OVERLAY)      && (m_dwSurfCategoriesDst & scOverlay)))
                {
                    dbgout << "Testing Destination Surface : " << szDescSurf << indent;
                    
                    bool fRetrySmaller;
                    do
                    {
                        fRetrySmaller = false;
                        // Handle this as a simple surface
                        hr = m_piDD->CreateSurface(&m_cddsdDst, m_piDDSDst.AsTestedOutParam(), NULL);

                        // Give it another try with a smaller overlay if we didn't have enough memory the first time.
                        if (m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_OVERLAY && 
                            (DDERR_OUTOFVIDEOMEMORY == hr || DDERR_OUTOFMEMORY == hr))
                        {
                            m_cddsdDst.dwHeight /= 2;
                            m_cddsdDst.dwWidth /= 2;
                            hr = m_piDD->CreateSurface(&m_cddsdDst, m_piDDSDst.AsTestedOutParam(), NULL);
                        }
                        
                        if (DDERR_GENERIC == hr)
                        {
                            dbgout << "Surface not supported" << endl;
                        } else
                        if (DDERR_OUTOFVIDEOMEMORY == hr || DDERR_OUTOFMEMORY == hr)
                        {
                            tr |= OutOfMemory_Helpers::VerifyOOMCondition(hr, m_piDD.AsInParam(), m_cddsdDst);
                        }
                        else if ((DDERR_NOOVERLAYHW == hr || DDERR_INVALIDCAPS == hr) &&
                                 ((m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) ||
                                  !(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_OVERLAY)))
                        {
                            // If we got a no overlay hardware error, the test still passes as
                            // long as either the surface is in system memory or if the HAL
                            // doesn't support overlays.
                            dbgout << "Overlay not supported ... Skipping" << endl;
                        }
                        else if (DDERR_UNSUPPORTEDFORMAT == hr && (m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_OVERLAY))
                        {
                            // Unsupported formats are ok for overlay surfaces, since the
                            // support is driver dependent.
                            dbgout << "Overlay not supported ... Skipping" << endl;
                        }
                        else if((DDERR_NOFLIPHW == g_DDSPrimary.GetLastError() || DDERR_NOFLIPHW == hr) && 
                            (m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_FLIP) &&
                            (!(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_FLIP) ||
                            !(m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)))
                        {
                            dbgout << "Flipping unsupported, unable to create surface with backBuffers";
                        }
                        else if ((DDERR_TOOBIGHEIGHT == hr || DDERR_TOOBIGWIDTH == hr || DDERR_TOOBIGSIZE == hr) && 
                            !(m_cddsdDst.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
                        {
                            dbgout << "Surface too large (received " << hr << "). Will attempt smaller surface" << endl;
                            m_cddsdDst.dwWidth = (m_cddsdDst.dwWidth * 3 / 4) & (~0x3f);
                            m_cddsdDst.dwHeight = (m_cddsdDst.dwHeight * 3 / 4) & (~0x3f);
                            if (0 == m_cddsdDst.dwWidth || 0 == m_cddsdDst.dwHeight)
                            {
                                dbgout(LOG_FAIL) << "Unable to find small enough surface." << endl;
                                tr |= trFail;
                            }
                            else
                            {
                                dbgout << "Trying with surface of size " << m_cddsdDst.dwWidth << "x" << m_cddsdDst.dwHeight << endl;
                                fRetrySmaller = true;
                            }
                        }
                        else if (FAILED(hr))
                        {
                            QueryHRESULT(hr, "CreateSurface", tr |= trFail);
                            dbgout(LOG_FAIL)<< "NOTE: DirectDraw driver must now handle creation of system memory surfaces" << endl;
                        }
                        else QueryCondition(m_piDDSDst.InvalidOutParam(), "CreateSurface succeeded but failed to set the OUT parameter", tr |= trFail)
                        else
                        {
                            tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDSDst.AsInParam());
                            if(tr==trPass)
                            {
                                // Run the test on the surface
                                tr |= RunSurfaceTest_TWO();
                            }
                            m_piDDSDst.ReleaseInterface();
                        }
                    }while(fRetrySmaller);
                    dbgout << unindent << "Done Destination" << endl;
                }
            }
           
        }

        // Some special, not-in-the-loop cases
        //====================================
        
        // If the source is a primary, we can get its backbuffer
        if (m_cddsd.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
        {
            // If there is a backbuffer, and the test wants to be
            // run with a backbuffer destination
            if ((m_cddsd.dwBackBufferCount > 0) && (m_dwSurfCategoriesDst & scBackbuffer))
            {
                dbgout << "Testing Destination Surface : BackBuffer 1" << indent;
                m_piDDSDst = g_DDSBackbuffer.GetObject();
                m_cddsdDst = g_DDSBackbuffer.m_cddsd;
                if (m_piDDSDst != NULL)
                {
                    tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDSDst.AsInParam());
                    if(tr==trPass)
                    {
                        tr |= RunSurfaceTest_TWO();
                    }
                    m_piDDSDst.ReleaseInterface();
                }
                else if((DDERR_NOFLIPHW == g_DDSPrimary.GetLastError() || DDERR_INVALIDCAPS == g_DDSPrimary.GetLastError()) && 
                        (m_cddsd.ddsCaps.dwCaps & DDSCAPS_FLIP) &&
                        (!(g_DDConfig.HALCaps().ddsCaps.dwCaps & DDSCAPS_FLIP) ||
                        !(m_cddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)))
                    {
                        dbgout << "Flipping unsupported, unable to create surface with backBuffers" << endl;
                    }
                else
                    tr |= trAbort;
                g_DDSBackbuffer.ReleaseObject();
                dbgout << unindent << "Done" << endl;
            }
        }
        else if ((m_cddsd.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER) && (m_dwSurfCategoriesDst & scPrimary))
        {
            // If the source is a backbuffer, and the test wants to test
            // the primary as a destination, we can get it and run the,
            // test, but DON'T destroy the primary!
            dbgout << "Testing Destination Surface : Primary" << indent;
            m_piDDSDst = g_DDSPrimary.GetObject();
            m_cddsdDst = g_DDSPrimary.m_cddsd;
            if (m_piDDSDst != NULL)
            {
                tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDSDst.AsInParam());
                if(tr==trPass)
                {
                    tr |= RunSurfaceTest_TWO();
                }
                m_piDDSDst.ReleaseInterface();
            }
            else
                tr |= trAbort;
            dbgout << unindent << "Done" << endl;
        }
        
        // Last, test the case that we're using the same surface as
        // both source and destination.  This must be last, since
        // it's destructive for the source surface.
        m_cddsdDst = m_cddsd;
        m_piDDSDst = m_piDDS;
        dbgout << "Testing Destination == Source" << indent;

        tr |=Surface_Helpers::AttachPaletteIfNeeded(m_piDD.AsInParam(), m_piDDSDst.AsInParam());
        if(tr==trPass)
        {
            tr |= RunSurfaceTest_TWO();
        }
        dbgout << unindent << "Done" << endl;
        m_piDDSDst.ReleaseInterface();

        if (tr & trAbort)
        {
            dbgout << "### Test Aborted" << endl;
        }
        
        return tr;
    }
};

