/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbCore module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

#include "hbngnormalnumber_p.h"
#include "hbnumbergrpxmlreader_p.h"

namespace NgNormalNumber {
	QHash<QLocale::Country, HbNgNormalNumber::CountryData> countryList;
}

QString HbNgNormalNumber::normalNumberGrouping( const QString &number, 
												const QLocale::Country &country )
{
	QString decimal;
	QString group;
	int firstNumber;
	int lastNumber;
	int dot;
	bool sign;
	QList<int> patternBlockSizes;
	
	// Checking received 'number' parameter...
	// Here we initialize variables...
	if ( HbNgNormalNumber::checkingNumber(number, firstNumber, lastNumber, dot, sign) == false ) {
	    return QString();
	}
	
	// Checking XML...
	// Here we initialize (more) variables...
	if ( HbNgNormalNumber::checkingXML(country, decimal, group, patternBlockSizes) == false ) {
	    return QString();
	}
		
	// Formating number group...
	// Here we use those variables...
	return HbNgNormalNumber::formatingGroup(number, firstNumber, lastNumber, dot, sign, decimal, group, patternBlockSizes);
}

bool HbNgNormalNumber::checkingNumber( const QString &number, 
									   int &firstNumber,
									   int &lastNumber, 
									   int &dot,
									   bool &sign ) 
{
	firstNumber = -1;
	lastNumber = -1;
	dot = -1;
	sign = false;
	int index = 0;
	int nrSize = number.size();
	
	for ( index = 0; index < nrSize; index++ ) {
		switch ( number.at(index).toAscii() ) {
    	case 43: // "+"
    	case 45: // "-"
		    if ( index < number.size()-1 ) {
		         if ( index > 0 ) {
		         	// If sign (+/-) present then it should be a first or last (for arabic) character 
		         	return false;
		         } else {
		         	// Sign (+/-) is the first character
		            sign=true;
		         }
		    } else {
		    	if ( sign ) {
		        	// Sign (+/-) already exists as the first character
		    		// Sign cannot be in both ends same time
		            return false;
		   		} else {
		        	// Sign (+/-) is the last character
		            sign=true;
		       	}
		    }
    		break;
    	case 46: // "."
			if ( firstNumber == -1 ) {
				// We don't support '.x' format
				// At least zero should be a first character, example 0.x
				return false;
			}
			if ( dot != -1 ) {
				// We don't support multiple dots
				// Only one dot is allowed
				return false;
			}
			if ( index == (nrSize-2) ) {
				if ( number.at(index+1).toAscii() == 43) {
					// We don't support 'x.+' format
					// At least zero should be a second last character, example x.0+
					return false;
				}
				if ( number.at(index+1).toAscii() == 45) {
					// We don't support 'x.-' format
					// At least zero should be a second last character, example x.0-
					return false;
				}
			}
			if ( index == (nrSize-1) ) {
				// We don't support 'x.' format
				// At least zero should be a last character, example x.0
				return false;
			}
			dot = index;
			break;
    	case 48: // "0"
    	case 49: // "1"
    	case 50: // "2"
    	case 51: // "3"
    	case 52: // "4"
    	case 53: // "5"
    	case 54: // "6"
    	case 55: // "7"
    	case 56: // "8"
    	case 57: // "9"
    		if ( firstNumber == -1 ) {
    			firstNumber = index;
    		}
    		lastNumber = index;
    		break;
    	default:
    		// String contains unknown or invalid character(s)
    		return false;
		}
	}
	
	if ( firstNumber == -1 ) {
		// String doesn't contain any number for formating
    	return false;
	}
	
    return true;
}

bool HbNgNormalNumber::checkingXML( const QLocale::Country &country, 
									QString &decimal, 
									QString &group, 
									QList<int> &patternBlockSizes )
{
	decimal = "";
	group = "";
	QString pattern = "";
	
	// Firstly we try to find needed data from the list...
	QHash<QLocale::Country, HbNgNormalNumber::CountryData>::iterator iter = NgNormalNumber::countryList.find(country);
	if ( iter == NgNormalNumber::countryList.end() ) { 
		// Data not found. So now we read XML file...
	    HbNumberGrpXmlReader numberGrpReader(country);
	    pattern = numberGrpReader.getPattern();
	    decimal = numberGrpReader.getDecimal();  
	    group = numberGrpReader.getGroup();
	    
	    if ( (pattern == "") || (decimal == "") ) {
	    	// XML doesn't contain all needed information or reading has failed
	    	return false;
	    }
	    	    
		// Checking pattern...
	    patternBlockSizes = HbNgNormalNumber::checkingPattern(pattern);
	    if ( patternBlockSizes.isEmpty() ) {
	    	// Pattern was invalid or corrupted
	    	return false;
	    }
	    
	    // Updating the list with new information... 
	    HbNgNormalNumber::CountryData tempCountryData;
	    tempCountryData.pattern = pattern;
	    tempCountryData.decimal = decimal;
	    tempCountryData.group = group;
	    tempCountryData.patternBlockSizes = patternBlockSizes;
	    NgNormalNumber::countryList.insert(country, tempCountryData);  
	} else { 
		// Data found
		HbNgNormalNumber::CountryData tempCountryData2 = iter.value();
	    pattern = tempCountryData2.pattern;
	    decimal = tempCountryData2.decimal;
	    group = tempCountryData2.group;
	    patternBlockSizes = tempCountryData2.patternBlockSizes;
	}

    return true;
}

QList<int> HbNgNormalNumber::checkingPattern( const QString &pattern )
{
	int index = 0;
	int comma = -1;
	int patternBlockSize = 0;
	QList<int> blockSizes;
	
	for ( index = 0; index < pattern.size(); index++ ) {
		if ( pattern.at(index).toAscii() == 44 ) { // Comma
			if ( comma == -1 ) {
				comma = index;		
			} else {
				// Calculating size of block...
				patternBlockSize = index - comma - 1;
			    if ( patternBlockSize < 1 ) {
			    	// Pattern was invalid or corrupted
			    	return QList<int>();
			    }
				blockSizes.append(patternBlockSize);
				comma = index;
			}
		} else if ( pattern.at(index).toAscii() == 46 ) { // Dot
			if ( comma == -1 ) {
				patternBlockSize = 99999;
			} else {
				// Calculating size of block...
				patternBlockSize = index - comma - 1;
			    if ( patternBlockSize < 1 ) {
			    	// Pattern was invalid or corrupted
			    	return QList<int>();
			    }		
			}
			blockSizes.append(patternBlockSize);
			break;
		}
	}
	
	return blockSizes;
}

QString HbNgNormalNumber::formatingGroup( const QString &number, 
										  const int &firstNumber, 
										  const int &lastNumber, 
										  const int &dot, 
										  const bool &sign, 
										  const QString &decimal, 
										  const QString &group, 
										  const QList<int> &patternBlockSizes )
{
	QString formated = "";
	int index = 0;
	int patternBlockSize = 0;
	int blockCounter = 0;
	int numberOfBlocks = 0;
	int nrSize = number.size();
	int tempDot = dot;

	if ( tempDot == -1 ) {
		if ( (sign == true) && (lastNumber < nrSize-1) ) {
			tempDot = nrSize - 1;
			
			// Inserting sign (xxx-/+)...
			formated.insert(0, number.at(nrSize-1)); 
		} else {
			tempDot = nrSize;
		}
	} else {
		// Inserting decimals (and sign x.xxx-/+)...
		formated.append(decimal);
		formated.append(number.right(nrSize-tempDot-1));
	}
	
	// Checking amount of blocks and size of first (last in the list) block for using...
	numberOfBlocks = patternBlockSizes.length();
	patternBlockSize = patternBlockSizes.last(); 
	
	for ( index = tempDot-1; index > firstNumber-1; index-- ) {
		if ( blockCounter < patternBlockSize ) {
			// Inserting number...
			formated.insert(0, number.at(index));
			blockCounter++;
		} else {
			// Inserting number and group separator...
			formated.insert(0, group);
			formated.insert(0, number.at(index));
			blockCounter = 1;
			
			// Take next size of block in use
			numberOfBlocks--;
			// Updating the size of block if needed...
			if ( numberOfBlocks > 0 ) {
				patternBlockSize = patternBlockSizes.at(numberOfBlocks-1);
			}
		}
	}
	
	if ( firstNumber > 0 ) {
		// Inserting sign (+/-xxx)...
		formated.insert(0, number.at(0)); 
	}
	
	return formated;
}
