/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  
*       For checking the validity of Smart messaging ringing tone and for 
*       ripping the ringing tone title from binary data. 
*
*/



// SYSTEM INCLUDES
#include <e32svr.h>

// USER INCLUDES
#include "ringbctoneconverter.h"
#include "NsmRingTone.h"
#include "debugtraces.h"


// LOCAL CONSTANTS AND MACROS
const TInt KCompMaxSongLength = 240; // maximum number of notes


// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------
// NewL()
//
// ---------------------------------------------------------
RingBCNSMConverter* RingBCNSMConverter::NewL()
    {
	RingBCNSMConverter* self = new (ELeave) RingBCNSMConverter();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();	// self
	return self;
    }

// ---------------------------------------------------------
// RingBCNSMConverter()
//
// ---------------------------------------------------------
RingBCNSMConverter::RingBCNSMConverter()
    {
	iScale = EScale2;
	iStyle = EStyleNatural;
    }

// ---------------------------------------------------------
// RingBCNSMConverter()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ConstructL()
    {
   
    
   
    }

// ---------------------------------------------------------
// ~RingBCNSMConverter()
//
// ---------------------------------------------------------
RingBCNSMConverter::~RingBCNSMConverter()
    {
	delete iCompData.iSongData; 	
    }


// ---------------------------------------------------------
// ConvertNsmToCompDataL()
//
// ---------------------------------------------------------
TCompData* RingBCNSMConverter::ConvertNsmToCompDataL( const TDesC8& aFileData )
    {
    QDEBUG_WRITE( "***BEGIN" );
    
    iReceivedNsmRTData.Set( aFileData );

    iReadOnly = EFalse;
	ResetForCompData();
	delete iCompData.iSongData;
	iCompData.iSongData = NULL;
	iCompData.iSongData = new(ELeave) CArrayFixFlat<TInstruction>( KCompMaxSongLength );

    QDEBUG_WRITE_FORMAT( "***NSM DATA LENGTH:", iReceivedNsmRTData.Length() );

    if( iReceivedNsmRTData.Length() == 0 )
        {
        QDEBUG_WRITE( "!!!ZERO LENGTH" );
        User::Leave( KErrCorrupt );
        }

	TInt commandlength( 1 );
	while( commandlength != KrtCommandEnd && 
        iNsmPosition < iReceivedNsmRTData.Length() )
        {
		commandlength = GetBits(KrtCommandLengthBits);
		

        QDEBUG_WRITE_FORMAT( "***NEXT CMD LENGTH :", commandlength );

		if (commandlength > 3)
            {
            QDEBUG_WRITE( "!!!CMD TOO LONG" );
            User::Leave( KErrCorrupt );
            }

		for (TInt i=0; i<commandlength; i++)
            {		

            QDEBUG_WRITE_FORMAT( "***CMD NUMBER ", i );

			ProcessNsmRingToneCommandPartL();
            }
        }

	return &iCompData;
    }

// ---------------------------------------------------------
// ProcessNsmRingToneCommandPartL()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ProcessNsmRingToneCommandPartL()
    {
	TInt commandpart( GetBits(KrtCommandPartBits) );

    QDEBUG_WRITE_FORMAT( "***CMD PART: ", commandpart );

	switch (commandpart)
	    {
	    case KrtCommandRTProgramming:
			{
			iNsmRTFlags |= KrtFlagRTProgramming;
		    break;
			}
	    case KrtCommandUnicode:
			{
			iNsmRTFlags |= KrtFlagUnicode;
		    break;
			}
	    case KrtCommandCancelCommand:
			{
		    if (GetBits(KrtCommandPartBits) == KrtCommandUnicode)
                {
			    iNsmRTFlags &= ~KrtFlagUnicode;
                }
		    break;
			}
	    case KrtCommandSound:
			{
			if ((iNsmRTFlags & KrtFlagRTProgramming) == 0)
                {
                QDEBUG_WRITE( "!!!CMD SOUND WITH NO RT PROGRAMMING" );
                User::Leave( KErrCorrupt );
                }
		    ProcessNsmRingToneSoundSpecL();
		    break;
			}
	    default:
			{
            // Ignore unknown commands
            QDEBUG_WRITE( "???CMD UNKNOWN" );
            iReadOnly = ETrue;
		    break;
			}
	    }

	GetBits(KrtFiller); // skip the filler bits
    }

// ---------------------------------------------------------
// ProcessNsmRingToneSoundSpecL()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ProcessNsmRingToneSoundSpecL()
    {
	TInt soundspec( GetBits(KrtSongTypeBits) );

    QDEBUG_WRITE_FORMAT( "***SONG TYPE :", soundspec );

	switch (soundspec)
	    {
	    case KrtSongTypeBasic:
			{
		    ProcessNsmRingToneSongTitleL();
		    ProcessNsmRingToneSongL();
		    break;
			}
	    case KrtSongTypeTemporary:
            {
		    ProcessNsmRingToneSongL();
		    break;
			}
	    default:
            {
		    iReadOnly = ETrue;
		    break;
			}
	    }

	iCompData.iSongLength = iCompDataPosition;
    }

// ---------------------------------------------------------
// ProcessNsmRingToneSongTitleL()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ProcessNsmRingToneSongTitleL()
{
	TInt titlelength( GetBits(KrtSongTitleTextLengthBits) );
    QDEBUG_WRITE_FORMAT( "***TITLE LENGTH ", titlelength );

    TInt charWidth( KrtDefaultCharBits );
	if( iNsmRTFlags & KrtFlagUnicode )
    	{
        charWidth = KrtUnicodeCharBits;
        }
    QDEBUG_WRITE_FORMAT( "***TITLE CHAR WIDTH: ", charWidth );

    TBuf<KrtSongTitleMaxLength> title;
	for( TInt i( 0 ); i < titlelength; i++ )
        {
        title.Append( GetBits(charWidth) );
        }
    QDEBUG_WRITE_FORMAT( "***TITLE: ", &title );

    SetTitle( title );
    }

// ---------------------------------------------------------
// ProcessNsmRingToneSong()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ProcessNsmRingToneSongL()
    {
	TInt songsequencelength( GetBits(KrtSongsequenceLengthBits) );
    QDEBUG_WRITE_FORMAT( "***SEQUENCE LENGTH ", songsequencelength );

	/*if( songsequencelength == 0 )
		{
		iReadOnly = ETrue;
		}*/

	for (TInt i( 0 ); i<songsequencelength; i++)
	    {
		TInt patternheaderid( GetBits(KrtInstructionIdLengthBit) );
        QDEBUG_WRITE_FORMAT( "***PATTERN HEADER ID:", patternheaderid );
		if (patternheaderid != EPatternHeaderId)
            {
            QDEBUG_WRITE( "!!!PATTERN HEADER ID INVALID" );
			User::Leave(KErrCorrupt);
            }

		TInt patternid( GetBits(KrtPatternIdLengthBit) );
        QDEBUG_WRITE_FORMAT( "***PATTERN ID:", patternid );
		TInt loopvalue( GetBits(KrtPatternLoopValueBits) );
        QDEBUG_WRITE_FORMAT( "***PATTERN LOOP VALUE:", loopvalue );
		TInt patternspec( GetBits(KrtPatternSpecBits) );
        QDEBUG_WRITE_FORMAT( "***PATTERN SPEC:", patternspec );
		
		if( patternspec != KrtPatternDefined )
		    {
            QDEBUG_WRITE( "***PATTERN NOT DEFINED" );
			iNsmRTPatterns[patternid].iStart = iCompData.iSongData->Count();
			
            for (TInt l( 0 ); l < patternspec && iCompDataPosition < KCompMaxSongLength; l++)
                {
				ProcessNsmRingTonePatternInstructionL();
                }
			iNsmRTPatterns[patternid].iEnd = iCompDataPosition - 1;
		    }
		// check if the pattern is really already defined before copying
		TInt start( iNsmRTPatterns[patternid].iStart );
		TInt end( iNsmRTPatterns[patternid].iEnd );
		if( start < end )
		    {
			if( loopvalue < KrtPatternRepeatInfinite )
			    {
				for( TInt loopNum( 0 ); loopNum < loopvalue; loopNum++ ) // handle repeat
				    {
					for (TInt k=start; (k<=end)&&(iCompDataPosition<KCompMaxSongLength); 
						k++, iCompDataPosition++)
                        {
						iCompData.iSongData->AppendL( iCompData.iSongData->At( k ) );
                        }
				    }
			    }
			else // handle infinite repeat
			    {
				while( iCompDataPosition<KCompMaxSongLength )
				    {
					for (TInt k=start; (k<=end)&&(iCompDataPosition<KCompMaxSongLength); 
						k++, iCompDataPosition++)
                        {
						iCompData.iSongData->AppendL( iCompData.iSongData->At( k ) );
                        }
				    }
			}
		}
	}
}

// ---------------------------------------------------------
// ProcessNsmRingTonePatternInstruction()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ProcessNsmRingTonePatternInstructionL()
    {
	TInt instructionid( GetBits(KrtInstructionIdLengthBit) );
    QDEBUG_WRITE_FORMAT( "***INSTRUCTION ID:", instructionid );
	switch (instructionid)
	    {
	    case ENoteInstructionId:
			{
            QDEBUG_WRITE_FORMAT( "***INSTRUCTION ID: NOTE", instructionid );
		    ProcessNsmRingToneNoteInstructionL(iScale, iStyle);
		    break;
			}
	    case EScaleInstructionId:
			{
			iScale = GetBits(KrtNoteScaleBits);
            QDEBUG_WRITE_FORMAT( "***INSTRUCTION SCALE:", iScale );
		    break;
			}
	    case EStyleInstructionId:
			{
			iStyle = GetBits(KrtNoteStyleBits);
            QDEBUG_WRITE_FORMAT( "***INSTRUCTION STYLE:", iStyle );
            if( iStyle == EStyleReserved )
                {
                QDEBUG_WRITE( "???INSTRUCTION STYLE NOT SUPPORTED" );
                iReadOnly = ETrue;
                }
		    break;
			}
	    case ETempoInstructionId:
			{
			iCompData.iTempo = GetBits(KrtTempoBits);
            QDEBUG_WRITE_FORMAT( "***INSTRUCTION TEMPO:", iCompData.iTempo );
            if( iCompData.iTempo > ETempo250 )
                {
                QDEBUG_WRITE( "???TEMPO TOO HIGH" );
                iReadOnly = ETrue;
                }
		    break;
			}
	    case EVolumeInstructionId:
            {
			QDEBUG_WRITE( "***INSTRUCTION VOLUME" );
		    GetBits(KrtVolumebits);
		    break;
			}
	    default:
            {
			QDEBUG_WRITE( "???INSTRUCTION NOT SUPPORTED" );
            iReadOnly = ETrue;
		    break;
			}
	    }
    }

// ---------------------------------------------------------
// ProcessNsmRingToneNoteInstruction()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ProcessNsmRingToneNoteInstructionL(TInt aScale, TInt aStyle)
    {
	TInt notevalue( GetBits(KrtNoteValueBits) );
    QDEBUG_WRITE_FORMAT( "***NOTE VALUE:", notevalue );
    if( notevalue != ENotePause && (notevalue > ENoteB 
        || iScale == EScale4 && notevalue > ENoteGis
        || iScale == EScale1 && notevalue < ENoteG ) )
        {
        QDEBUG_WRITE( "???NOTE VALUE OUT OF RANGE" );
        iReadOnly = ETrue;
        }

	TInt noteduration( GetBits(KrtNoteDurationBits) );
    QDEBUG_WRITE_FORMAT( "***NOTE DURATION:", noteduration );
    if( noteduration > EDurationThirtysecond )
        {
        QDEBUG_WRITE( "???NOTE DURATION OUT OF RANGE" );
        iReadOnly = ETrue;
        }

	TInt notedurspecifier( GetBits(KrtNoteDurSpecifierBits) );
    QDEBUG_WRITE_FORMAT( "***NOTE DURATION SPECIFIER:", notedurspecifier);
    if( notedurspecifier == EDurSpecifireDoubleDotted
        || notedurspecifier > EDurSpecifierTriplet )
        {
        QDEBUG_WRITE( "???NOTE DURATION SPECIFIER OUT OF RANGE" );
        iReadOnly = ETrue;
        }

	TInstruction symbol;
	symbol.iValue = notevalue;
	symbol.iDuration = noteduration;
	symbol.iDurspecifier = notedurspecifier;
	symbol.iScale = aScale;
	symbol.iStyle = aStyle;

	iCompData.iSongData->AppendL( symbol );
	iCompDataPosition ++;
    }

// ---------------------------------------------------------
// GetBits()
//
// ---------------------------------------------------------
TInt RingBCNSMConverter::GetBits(TInt aNumBits)
    {
	TUint32 buf( 0 );   
	if( aNumBits == 0 ) // handle byte alignment
	    {
		if (iNsmPositionBit != 0)
		    {
			iNsmPositionBit = 0;	// skip filler bits
			iNsmPosition ++;			
		    }
		return buf;
	    }

	for (TInt n=0; n<4; n++)
	    {
		buf <<= 8;
		if ( iNsmPosition+n < iReceivedNsmRTData.Length() )
            {
			buf |= iReceivedNsmRTData[iNsmPosition+n];
            }
	    }

	TUint32 filter = 0;
	buf = (buf >> (32 - iNsmPositionBit - aNumBits)) & ~(~filter << aNumBits);

	iNsmPositionBit += aNumBits;
	while (iNsmPositionBit > 7)
	    {
		iNsmPositionBit -= 8;
		iNsmPosition ++;
	    }
	return buf;
    }

// ---------------------------------------------------------
// ResetForCompData()
//
// ---------------------------------------------------------
void RingBCNSMConverter::ResetForCompData()
    {
	iNsmRTPatterns[EPatternIdA].iStart = 0;
	iNsmRTPatterns[EPatternIdA].iEnd = 0;
	iNsmRTPatterns[EPatternIdB].iStart = 0;
	iNsmRTPatterns[EPatternIdB].iEnd = 0;
	iNsmRTPatterns[EPatternIdC].iStart = 0;
	iNsmRTPatterns[EPatternIdC].iEnd = 0;
	iNsmRTPatterns[EPatternIdD].iStart = 0;
	iNsmRTPatterns[EPatternIdD].iEnd = 0;
	iNsmRTFlags = 0;
    iNsmPosition = 0;
	iNsmPositionBit = 0;
	iCompDataPosition = 0;

	iCompData.iTempo = KrtBpmDefault;
	iCompData.iSongLength = 0;
    }

// ---------------------------------------------------------
// SetTitle()
//
// ---------------------------------------------------------
void RingBCNSMConverter::SetTitle( const TDesC& aFileName )
    {
    iCompData.iSongTitle = aFileName.Left( KrtSongTitleTextLength );
    }


// ---------------------------------------------------------
// TitleLC()
//
// ---------------------------------------------------------
HBufC* RingBCNSMConverter::TitleLC(TPtr8& aFileData)
{
	iReceivedCompData = ConvertNsmToCompDataL(aFileData);

	HBufC* titlePtr = iReceivedCompData->iSongTitle.AllocLC();
	return titlePtr;
}


// ---------------------------------------------------------
// IsRingToneMimeTypeL()
//
// ---------------------------------------------------------
TBool  RingBCNSMConverter::IsRingToneMimeTypeL(TPtr8& aFileData)
    {
	TRAPD( returnError, 
        iReceivedCompData = ConvertNsmToCompDataL(aFileData) );
    if( returnError != KErrCorrupt 
        && returnError != KErrNotSupported )
        {
        User::LeaveIfError( returnError );
        }

    return returnError != KErrCorrupt;
    }

// end of file
