/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_i18npool.hxx"

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

#include "sal/main.h"

#include <com/sun/star/lang/XComponent.hpp>

#include <com/sun/star/xml/sax/SAXParseException.hpp>
#include <com/sun/star/xml/sax/XParser.hpp>
#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>

#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/io/XActiveDataSource.hpp>

#include <cppuhelper/servicefactory.hxx>
#include <cppuhelper/implbase1.hxx>
#include <cppuhelper/implbase3.hxx>

#include <vos/diagnose.hxx>

#include "LocaleNode.hxx"

using namespace ::rtl;
using namespace ::std;
using namespace ::cppu;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::registry;
using namespace ::com::sun::star::xml::sax;
using namespace ::com::sun::star::io;






/************
 * Sequence of bytes -> InputStream
 ************/
class OInputStream : public WeakImplHelper1 < XInputStream >
{
public:
	OInputStream( const Sequence< sal_Int8 >&seq ) :
		nPos( 0 ),
		m_seq( seq )
		{}

public:
    virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
		throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
		{
			nBytesToRead = (nBytesToRead > m_seq.getLength() - nPos ) ?
				m_seq.getLength() - nPos :
				nBytesToRead;
			aData = Sequence< sal_Int8 > ( &(m_seq.getConstArray()[nPos]) , nBytesToRead );
			nPos += nBytesToRead;
			return nBytesToRead;
		}
    virtual sal_Int32 SAL_CALL readSomeBytes(
		::com::sun::star::uno::Sequence< sal_Int8 >& aData,
		sal_Int32 nMaxBytesToRead )
		throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
		{
			return readBytes( aData, nMaxBytesToRead );
		}
    virtual void SAL_CALL skipBytes( sal_Int32 /*nBytesToSkip*/ )
		throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
		{
			// not implemented
		}
    virtual sal_Int32 SAL_CALL available(  )
		throw(NotConnectedException, IOException, RuntimeException)
		{
			return m_seq.getLength() - nPos;
		}
    virtual void SAL_CALL closeInput(  )
		throw(NotConnectedException, IOException, RuntimeException)
		{
			// not needed
		}
	sal_Int32 nPos;
	Sequence< sal_Int8> m_seq;
};

//-------------------------------
// Helper : create an input stream from a file
//------------------------------
Reference< XInputStream > createStreamFromFile(
	const char *pcFile )
{
	FILE *f = fopen( pcFile , "rb" );
	Reference<  XInputStream >  r;

	if( f ) {
		fseek( f , 0 , SEEK_END );
		size_t nLength = ftell( f );
		fseek( f , 0 , SEEK_SET );

		Sequence<sal_Int8> seqIn(nLength);
		if (fread( seqIn.getArray() , nLength , 1 , f ) == 1)
			r = Reference< XInputStream > ( new OInputStream( seqIn ) );
		else
			fprintf(stderr, "failure reading %s\n", pcFile);
		fclose( f );
	}
	return r;
}


class TestDocumentHandler :
	public WeakImplHelper3< XExtendedDocumentHandler , XEntityResolver , XErrorHandler >
{
public:
	TestDocumentHandler(const char* locale, const char* outFile ) :
      rootNode(0), nError(0), nbOfCurrencies(0), nbOfCalendars(0), nbOfFormatElements(0), 
      nbOfTransliterations(0), nbOfCollations(0), nbOfDays(50), nbOfMonths(50), nbOfEras(10),
      flag(-1), of(outFile, locale), isStartDayOfWeek(false), foundDefaultName(false),
      foundVariant(false), openElement(false)
	{
        strncpy( theLocale, locale, sizeof(theLocale) );
        theLocale[sizeof(theLocale)-1] = 0;
	}

	~TestDocumentHandler(  )
	{
		of.closeOutput();
	}


public: // Error handler
    virtual void SAL_CALL error(const Any& aSAXParseException) throw (SAXException, RuntimeException)
    {
        ++nError;
    	printf( "Error !\n" );
    	throw  SAXException(
			OUString( RTL_CONSTASCII_USTRINGPARAM("error from error handler")) ,
			Reference < XInterface >() ,
			aSAXParseException );
    }
    virtual void SAL_CALL fatalError(const Any& /*aSAXParseException*/) throw (SAXException, RuntimeException)
    {
        ++nError;
    	printf( "Fatal Error !\n" );
    }
    virtual void SAL_CALL warning(const Any& /*aSAXParseException*/) throw (SAXException, RuntimeException)
    {
    	printf( "Warning !\n" );
    }


public: // ExtendedDocumentHandler



	stack<LocaleNode *> currentNode ;
	sal_Bool  fElement ;
	LocaleNode * rootNode;

	virtual void SAL_CALL startDocument(void) throw (SAXException, RuntimeException)
    {
	printf( "parsing document %s started\n", theLocale);
	of.writeAsciiString("#include <sal/types.h>\n\n\n");
	of.writeAsciiString("#include <stdio.h> // debug printfs\n\n");
	of.writeAsciiString("extern \"C\" {\n\n");
	}

    virtual void SAL_CALL endDocument(void) throw (SAXException, RuntimeException)
    {
		if (rootNode)
        {
			rootNode->generateCode(of);
            int err = rootNode->getError();
            if (err)
            {
                printf( "Error: in data for %s: %d\n", theLocale, err);
                nError += err;
            }
        }
        else
        {
            ++nError;
            printf( "Error: no data for %s\n", theLocale);
        }
		printf( "parsing document %s finished\n", theLocale);

		of.writeAsciiString("} // extern \"C\"\n\n");
		of.closeOutput();
    }

    virtual void SAL_CALL startElement(const OUString& aName,
							  const Reference< XAttributeList > & xAttribs)
		throw (SAXException,RuntimeException)
	{

		LocaleNode * l =  LocaleNode::createNode (aName, xAttribs);
		if (!currentNode.empty() ) {
			LocaleNode * ln = (LocaleNode *) currentNode . top();
			ln->addChild(l);
		} else {
			rootNode = l;
		}
		currentNode . push (l);
	}


	virtual void SAL_CALL endElement(const OUString& /*aName*/) throw (SAXException,RuntimeException)
    {
    	currentNode . pop();
    }

	virtual void SAL_CALL characters(const OUString& aChars) throw (SAXException,RuntimeException)
    {

    	LocaleNode * l = currentNode . top();
    	l->setValue (aChars);
		::rtl::OUString str(aChars);
		sal_Unicode nonBreakSPace[2]= {0xa, 0x0};
		if(!openElement || str.equals(nonBreakSPace))
		  return;
    }

	virtual void SAL_CALL ignorableWhitespace(const OUString& /*aWhitespaces*/) throw (SAXException,RuntimeException)
    {
    }

    virtual void SAL_CALL processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/) throw (SAXException,RuntimeException)
    {
		// ignored
    }

    virtual void SAL_CALL setDocumentLocator(const Reference< XLocator> & /*xLocator*/)
		throw (SAXException,RuntimeException)
    {
		// ignored
    }

    virtual InputSource SAL_CALL resolveEntity(
		const OUString& sPublicId,
		const OUString& sSystemId)
		throw (RuntimeException)
	{
		InputSource source;
		source.sSystemId = sSystemId;
		source.sPublicId = sPublicId;

		source.aInputStream = createStreamFromFile(
			OUStringToOString( sSystemId , RTL_TEXTENCODING_ASCII_US) );

		return source;
	}

    virtual void SAL_CALL startCDATA(void) throw (SAXException,RuntimeException)
    {
    }
    virtual void SAL_CALL endCDATA(void) throw (RuntimeException)
    {
    }
    virtual void SAL_CALL comment(const OUString& /*sComment*/) throw (SAXException,RuntimeException)
    {
    }
    virtual void SAL_CALL unknown(const OUString& /*sString*/) throw (SAXException,RuntimeException)
    {
    }

	virtual void SAL_CALL allowLineBreak( void) throw (SAXException, RuntimeException )
	{

	}

public:
    int nError;
	::rtl::OUString currentElement;
	sal_Int16 nbOfCurrencies;
	sal_Int16 nbOfCalendars;
	sal_Int16 nbOfFormatElements;
	sal_Int16 nbOfTransliterations;
	sal_Int16 nbOfCollations;
	Sequence<sal_Int16> nbOfDays;
	Sequence<sal_Int16> nbOfMonths;
	Sequence<sal_Int16> nbOfEras;
	sal_Char *elementTag;
	sal_Char theLocale[50];
	sal_Int16 flag;
	OFileWriter of;
	sal_Bool isStartDayOfWeek;
	sal_Bool foundDefaultName;
	sal_Bool foundVariant;
        sal_Bool openElement;
};





SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{


	if( argc < 6) {
		printf( "usage : %s <locaLe> <XML inputfile> <destination file> <services.rdb location> <types.rdb location>\n", argv[0] );
		exit( 1 );
	}

	// create service manager
	Reference< XMultiServiceFactory > xSMgr;
	try
	{
		xSMgr = createRegistryServiceFactory(
			::rtl::OUString::createFromAscii(argv[4]),
			::rtl::OUString::createFromAscii(argv[5]), true );
	}
	catch ( Exception& )
	{
		printf( "Exception on createRegistryServiceFactory\n" );
		exit(1);
	}

	//--------------------------------
	// parser demo
	// read xml from a file and count elements
	//--------------------------------
	Reference< XInterface > x = xSMgr->createInstance(
		OUString::createFromAscii( "com.sun.star.xml.sax.Parser" ) );
    int nError = 0;
	if( x.is() )
	{
		Reference< XParser > rParser( x , UNO_QUERY );

		// create and connect the document handler to the parser
		TestDocumentHandler *pDocHandler = new TestDocumentHandler( argv[1], argv[3]);

		Reference < XDocumentHandler >	rDocHandler( (XDocumentHandler *) pDocHandler );
		Reference< XEntityResolver > rEntityResolver( (XEntityResolver *) pDocHandler );

		rParser->setDocumentHandler( rDocHandler );
		rParser->setEntityResolver( rEntityResolver );

		// create the input stream
		InputSource source;
		source.aInputStream = createStreamFromFile( argv[2] );
		source.sSystemId 	= OUString::createFromAscii( argv[2] );

		try
		{
			// start parsing
			rParser->parseStream( source );
		}

		catch( Exception & e )
		{
			OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 );
			printf( "Exception during parsing : %s\n" ,  o1.getStr() );
			exit(1);
		}
        nError = pDocHandler->nError;
	}
	else
	{
		printf( "couln't create sax-parser component\n" );
		exit(1);
	}

	return nError;
}
