// ------------------------------------------------------------------------
/*
	\author: Jaap Suter
   
	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_COMMENT_PARSER_H
#define REVEAL_COMMENT_PARSER_H

#include <string.h>
#include <assert.h>
#include <stdio.h>

#include "reveal/impl/option_control.h"
#include "reveal/impl/widget.h"

namespace Reveal {

// -----------------------------------------------------------------
class CommentParser
{
public:

	enum
	{
		MAX_NUM_OPTIONS = 64
	};

	typedef enum
    {
        INVALID,
        RANGE_CONTROL,
        CHECKBOX_CONTROL,
        OPTION_CONTROL

    } ControlType;

    CommentParser() : mType( INVALID ), mNumOptions( 0 ) {}

    float Min() const { assert( mType == RANGE_CONTROL ); return mMin; }
    float Max() const { assert( mType == RANGE_CONTROL ); return mMax; }
	ControlType Type() const { return mType; }
	const char* Name() { return mName; }
	const char* Path() { return mPath; }

	size_t		  NumOptions() const { assert( mType == OPTION_CONTROL ); return mNumOptions; }
	const char*	  OptionDesc( size_t i ) const { assert( mType == OPTION_CONTROL ); assert( i < mNumOptions ); return mOptions[ i ].mDesc; }
	int			  OptionValue( size_t i ) const { assert( mType == OPTION_CONTROL ); assert( i < mNumOptions ); return mOptions[ i ].mValue; }

	void Reset() 
	{
		mType = INVALID;
		mNumOptions = 0;
	}

    void Parse( const char* s )
    {
		#define COMMENT_PARSER_CHECK_AND_RETURN if ( !s || !*s ) return
        
		ControlType type = INVALID;

        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
        while ( *s == '/' ) ++s; COMMENT_PARSER_CHECK_AND_RETURN;
        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;

        if ( strstr( s, "Reveal" ) != s ) return;
        else s += strlen( "Reveal" ); 
        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
        while ( *s == ':' ) ++s; COMMENT_PARSER_CHECK_AND_RETURN;
        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;

        if ( strstr( s, "Tweaker" ) == 0 ) return; 
        else s += strlen( "Tweaker" ); 
        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
        while ( *s == ':' ) ++s; COMMENT_PARSER_CHECK_AND_RETURN;
        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
        
        if ( strstr( s, "AddRangeControl"    ) == s ) type = RANGE_CONTROL; else
        if ( strstr( s, "AddOptionControl"   ) == s ) type = OPTION_CONTROL; else
        if ( strstr( s, "AddCheckBoxControl" ) == s ) type = CHECKBOX_CONTROL; else return;

        s = strchr( s, '(' );
        if ( s == 0 ) return; else ++s; COMMENT_PARSER_CHECK_AND_RETURN;

        s = SkipWhiteSpace( s ); 
        if ( *s == '\"' ) ++s; else return;

        strncpy( mPath, s, Impl::Widget::MAX_PATH_STRING_LENGTH );
        mPath[ Impl::Widget::MAX_PATH_STRING_LENGTH - 1 ] = 0;

        const char* closingQuote = strchr( s, '\"' );
        if ( closingQuote == 0 ) return;
        else
		{
			if ( closingQuote-s < Impl::Widget::MAX_PATH_STRING_LENGTH )
				mPath[closingQuote-s] = 0;
		}

		s = SkipWhiteSpace( ++closingQuote ); COMMENT_PARSER_CHECK_AND_RETURN;
        
        if ( *s == ',' ) ++s; else return;
        s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
        if ( *s == '\"' ) ++s; else return;

        strncpy( mName, s, Impl::Widget::MAX_NAME_STRING_LENGTH );
        mName[ Impl::Widget::MAX_NAME_STRING_LENGTH - 1 ] = 0;

        closingQuote = strchr( s, '\"' );
        if ( closingQuote == 0 ) return;
		else
		{
			if ( closingQuote-s < Impl::Widget::MAX_NAME_STRING_LENGTH )
				mName[closingQuote-s] = 0;
		}

        s = SkipWhiteSpace( ++closingQuote ); COMMENT_PARSER_CHECK_AND_RETURN;

        if ( type == CHECKBOX_CONTROL )
        {
            mType = type;
            return;
        }
        else
        if ( type == RANGE_CONTROL )
        {
            if ( *s == ',' ) ++s; else return;
            s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
            float min = 0; sscanf( s, "%f", &min );
            while ( *s != ',' ) ++s; COMMENT_PARSER_CHECK_AND_RETURN;
            ++s; 
            s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
            float max = 1; sscanf( s, "%f", &max );

            mMin = min;
            mMax = max;
            mType = type;
        }
        else
        if ( type == OPTION_CONTROL )
        {
            if ( *s == ',' ) ++s; else return;
            s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
            if ( *s == '\"' ) ++s; else return;            
            while ( *s != '\"' )
            {
                s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
                size_t idx = 0;
                while ( (*s != 0) && (*s != ' ') && (*s != ',' ) && (*s != '\"') && (idx < (Option::MAX_OPTION_DESC_LENGTH - 1)) ) mOptions[ mNumOptions ].mDesc[ idx++ ] = *s++;
                mOptions[ mNumOptions ].mDesc[ idx ] = 0;
                s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
                if ( *s == '=' )
                {
                    ++s;
                    s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
                    sscanf( s, "%i", &mOptions[ mNumOptions ].mValue );
                }
                else
                {
                    if ( mNumOptions == 0 ) mOptions[ mNumOptions ].mValue = 0;
                    else                    mOptions[ mNumOptions ].mValue = mOptions[ mNumOptions - 1 ].mValue + 1;
                }
                ++mNumOptions; if ( mNumOptions >= MAX_NUM_OPTIONS ) break;
				s = SkipWhiteSpace( s ); COMMENT_PARSER_CHECK_AND_RETURN;
				if ( (*s == ')') || (*s == '\"') || (*s == 0) ) break;
                s = strchr( s, ',' );
                if ( s != 0 ) ++s; else break;
            }

            mType = type;
        }
		#undef COMMENT_PARSER_CHECK_AND_RETURN
    }

private:

    static const char* SkipWhiteSpace( const char* s )
    {
        char* whitespace = " /t/n/r";
        while ( strchr( whitespace, *s ) ) ++s;
        return s;
    }

    static char* SkipWhiteSpace( char* s )
    {
        return const_cast< char* >( SkipWhiteSpace( const_cast< const char* >( s ) ) );
    }

	struct Option
	{ 
		enum {  MAX_OPTION_DESC_LENGTH = 32 };

		int	 mValue; 
		char mDesc[ MAX_OPTION_DESC_LENGTH ]; 
	};

	char		  mName[ Impl::Widget::MAX_NAME_STRING_LENGTH ];
	char		  mPath[ Impl::Widget::MAX_PATH_STRING_LENGTH ];
    ControlType   mType;
    float         mMin;
    float         mMax;
    Option		  mOptions[ MAX_NUM_OPTIONS ];
    size_t	      mNumOptions;
};

} // Namespace Reveal;

#endif
