// XML7k.cpp : Defines the entry point for the console application.
//

#include "StdAfx.h"
#include "XML7k.h"

#include <stdio.h>
#include <memory>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using std::auto_ptr;

namespace                                                       // Begin unnamed namespace.
{

    class CXMLParser
    {
    public:

        ///////////////
        // Services.

                        CXMLParser          (   const char          *pcXMLMessage,
                                                const unsigned long &rulLength );

        virtual        ~CXMLParser          (   void );

        bool            IsValid             (   void )  const;

        CString         SystemText          (   void );

        unsigned long   DeviceId            (   void );
        unsigned short  SubsystemID         (   void );
        unsigned short  SystemEnumerator    (   void );

    private:

        ///////////////
        // Attributes.

        bool                    m_bValid;
        auto_ptr<CString>       m_psXML;

        ///////////////
        // Services.

        CString         AttributeText       (   LPCTSTR                 lpszTag,
                                                LPCTSTR                 lpszAttribute );

        CString         TagText             (   LPCTSTR                 lpszTagName );

        CString         ExtractAttributeData(   const int               &riStart );

        CString         ExtractTagText      (   const int               &riStart );

        bool            FindTag             (   LPCTSTR                 lpszTag,
                                                int                     &riStart,
                                                int                     &riStop );

        bool            FindAttribute       (   LPCTSTR                 lpszAttribute,
                                                int                     &riStart,
                                                int                     &riStop  );

                        CXMLParser          (   void );                                 // Not implemented thus private.

    };

}                                                               // End unnamed namespace.

/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	if ( AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0) )
	{
        FILE *pFile = fopen( "7k_0.xml", "r" );

        ASSERT( pFile != NULL );

        if ( pFile != NULL )
        {
            CString FileBuffer;

            int iChar;

            while ( ( iChar = fgetc( pFile ) ) != EOF )
            {
                FileBuffer += static_cast<char>( iChar );
            }

            fclose( pFile );

            if ( ! FileBuffer.IsEmpty() )
            {
                CXMLParser XML( static_cast<LPCTSTR>( FileBuffer ), static_cast<unsigned long>( FileBuffer.GetLength() ) );

                if ( XML.IsValid() )
                {
                    printf( "System text: %s, Device Id: %lu, Subsystem Id: %u, Enumerator: %u\n",  XML.SystemText(),
                                                                                                    XML.DeviceId(),
                                                                                                    XML.SubsystemID(),
                                                                                                    XML.SystemEnumerator() );
                }
            }
        }

        return 0;
	}

	return 1;
}

///////////////////////////////////////////////////////////////////////////////
// CXMLParser class implementation.

CXMLParser::CXMLParser( const char          *pcXMLMessage,
                        const unsigned long &rulLength )
{
    try
    {
        m_psXML  = auto_ptr<CString>( new CString( pcXMLMessage, rulLength ) );
        m_bValid = ( m_psXML.get() != NULL );
    }
    catch ( ... )
    {
        m_bValid = false;
    }

    ASSERT( m_bValid );
}

CXMLParser::~CXMLParser( void )
{
    m_bValid = false;

    if ( m_psXML.get() != NULL )
    {
        delete ( m_psXML.release() );
        m_psXML = auto_ptr<CString>( NULL );
    }
}

bool CXMLParser::IsValid( void ) const
{
    return m_bValid;
}

CString CXMLParser::SystemText ( void )
{
    return TagText( "Name" );
}

unsigned long  CXMLParser::DeviceId( void )
{
    CString sDeviceId = AttributeText( "Name", "deviceid" );

    if ( ! sDeviceId.IsEmpty() )
    {
        return static_cast<unsigned long>( atol( static_cast<LPCTSTR>( sDeviceId ) ) );
    }

    ASSERT( false );

    return 0UL;
}

unsigned short CXMLParser::SubsystemID( void )
{
    CString sSubsystemId = AttributeText( "Name", "subsystemid" );

    if ( ! sSubsystemId.IsEmpty() )
    {
        return static_cast<unsigned short>( atoi( static_cast<LPCTSTR>( sSubsystemId ) ) );
    }

    ASSERT( false );

    return 0;
}

unsigned short CXMLParser::SystemEnumerator( void )
{
    CString sEnumerator = AttributeText( "Name", "enumerator" );

    if ( ! sEnumerator.IsEmpty() )
    {
        return static_cast<unsigned short>( atoi( static_cast<LPCTSTR>( sEnumerator ) ) );
    }

    ASSERT( false );

    return 0;
}

CString CXMLParser::AttributeText( LPCTSTR lpszTag, LPCTSTR lpszAttribute )
{
    CString sAttribute;
    int     iStart = 0;
    int     iStop  = 0;

    if ( FindTag( lpszTag, iStart, iStop ) )
    {
        iStart = iStop;

        if ( FindAttribute( lpszAttribute, iStart, iStop ) )
        {
            iStart = iStop;

            sAttribute = ExtractAttributeData( iStart );
        }
    }

    ASSERT( ! sAttribute.IsEmpty() );

    return sAttribute;
}

CString CXMLParser::TagText( LPCTSTR lpszTag )
{
    CString sTagData;
    int     iStart = 0;
    int     iStop  = 0;

    if ( FindTag( lpszTag, iStart, iStop ) )
    {
        iStart = iStop;

        sTagData = ExtractTagText( iStart );
    }

    ASSERT( ! sTagData.IsEmpty() );

    return sTagData;
}

bool CXMLParser::FindTag(   LPCTSTR     lpszTag,
                            int        &riStart,
                            int        &riStop )
{
    bool bTagFound = false;

    try
    {
        riStart = max( 0, riStart );
        riStop  = riStart;

        if ( lpszTag != NULL )
        {
            int     iTagStart;
            CString SearchPattern;

            SearchPattern.Format( "<%s ", lpszTag );

            ASSERT( m_psXML.get() != NULL );

            if ( ( iTagStart = m_psXML->Find( SearchPattern, riStart ) ) != -1 )
            {
                riStart   = iTagStart;
                riStop    = iTagStart + SearchPattern.GetLength();
                bTagFound = true;
            }
        }
    }
    catch ( ... )
    {
        riStart   = 0;
        riStop    = 0;
        bTagFound = false;
    }

    return bTagFound;
}

bool CXMLParser::FindAttribute( LPCTSTR     lpszAttribute,
                                int        &riStart,
                                int        &riStop )
{
    bool bAttributeFound = false;

    try
    {
        riStart = max( 0, riStart );
        riStop  = max( riStart, riStop );

        if ( lpszAttribute != NULL )
        {
            int     iAttributeStart;
            CString SearchPattern;

            SearchPattern.Format( "%s=\"", lpszAttribute );

            ASSERT( m_psXML.get() != NULL );

            if ( ( iAttributeStart = m_psXML->Find( SearchPattern, riStart ) ) != -1 )
            {
                riStart   = iAttributeStart;
                riStop    = iAttributeStart + SearchPattern.GetLength();
                bAttributeFound = true;
            }
        }
    }
    catch ( ... )
    {
        riStart = 0;
        riStop  = 0;
        bAttributeFound = false;
    }

    return bAttributeFound;
}

CString CXMLParser::ExtractAttributeData( const int &riStart )
{
    CString  sAttributeData;

    ASSERT( m_psXML.get() != NULL );

    const CString &rXML = *(m_psXML.get());

    for ( int iChar = riStart; iChar < ( rXML.GetLength() - riStart ); iChar++ )
    {
        if ( ( rXML[ iChar ] == '\"' ) || ( rXML[ iChar ] == '>' ) )
        {
            break;
        }
        else
        {
            sAttributeData += rXML[ iChar ];
        }
    }

    ASSERT( ! sAttributeData.IsEmpty() );

    return sAttributeData;
}

CString CXMLParser::ExtractTagText( const int &riStart )
{
    CString sTagText;

    ASSERT( m_psXML.get() != NULL );

    const CString &rXML = *(m_psXML.get());

    int iTagTextStart = rXML.Find( ">",  riStart );
    int iTagTextStop  = rXML.Find( "</", riStart );

    // Extract the text between these two pointers, if valid.

    if ( ( iTagTextStart != -1 ) && ( iTagTextStop != -1 ) )
    {
        for ( int iChar = iTagTextStart + 1; iChar < iTagTextStop; iChar++ )
        {
            sTagText += rXML[ iChar ];
        }
    }

    ASSERT( ! sTagText.IsEmpty() );

    return sTagText;
}

