/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



#include <com/sun/star/xml/sax/SAXException.hpp>
#include <l10ntools/vosapp.hxx>

#include <osl/file.hxx>

#include "export.hxx"
#include "layoutparse.hxx"
#include "helpmerge.hxx"
#include "xmlparse.hxx"

// Convert a rtl::OUString to a byte string.
#define OUSTRING_CSTR( str ) \
    rtl::OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr()

#define STRING( str ) String( str, RTL_TEXTENCODING_UTF8 )
#define BSTRING( str ) ByteString( str, RTL_TEXTENCODING_UTF8 )

using ::rtl::OUString;

using namespace ::osl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;


class TranslateLayout : public Application
{
    ByteString mGid1;
    ByteString mLanguage;
    ByteString mLocalize; 
    ByteString mOutput;
    ByteString mProject;
    ByteString mRoot;
    bool mMergeMode;
    std::vector< ByteString > mLanguages;
    std::list< ByteString > mFiles;

public:
    TranslateLayout();
    virtual ~TranslateLayout();
    ByteString GetCommandLineParam( int i );
    ByteString GetOptionArgument( int const i );
    void ExceptionalMain();
    void Main();
    void Merge();
    void MergeLanguage( ByteString const& language );
    void ParseCommandLine();
    void CreateSDF();

    using Application::GetCommandLineParam;
};

static void usage()
{
    fprintf( stderr, "Usage: tralay [OPTION]... XML-FILE\n" );
    fprintf( stderr, "\nOptions:\n" );
    fprintf( stderr, "  -h,--help                  show this help\n" );
    fprintf( stderr, "  -l,--language=LANG         process this language\n" );
    fprintf( stderr, "  -m,--merge=DATABASE.SDF    translation database\n" );
    fprintf( stderr, "\nExamples:\n" );
    fprintf( stderr, "  tralay -l en-US -o localize.sdf zoom.xml   # Extract\n" );
    fprintf( stderr, "  tralay -m localize.sdf -l de -l nl -o out zoom.xml  # Merge/translate\n" );
    exit( 2 );
}

static ByteString ConvertSystemPath( const ByteString& rPath )
{
    if( rPath.CompareTo( ".", 1 ) == 0 )
    {
        OUString sPath( rPath.GetBuffer(), rPath.Len(), RTL_TEXTENCODING_UTF8 );

        ::rtl::OUString curDirPth, sURL;
        osl_getProcessWorkingDir( &curDirPth.pData );

        ::osl::FileBase::getAbsoluteFileURL( curDirPth, sPath, sURL );
        ::osl::FileBase::getSystemPathFromFileURL( sURL, sPath );

        return ByteString( rtl::OUStringToOString( sPath, RTL_TEXTENCODING_UTF8 ) );
    }
    else
    {
        return rPath;
    }
}

ByteString TranslateLayout::GetCommandLineParam( int i )
{
    return ByteString( OUSTRING_CSTR( Application::GetCommandLineParam( sal::static_int_cast< sal_uInt16 >( i ) ) ) );
}

ByteString TranslateLayout::GetOptionArgument( int const i )
{
    if ( i >= GetCommandLineParamCount() )
        usage();
    ByteString arg = GetCommandLineParam( i );
    if ( !arg.CompareTo( "-", 1 ) )
    {
        fprintf( stderr, "Option needs an argument: %s, found: %s\n",
                 GetCommandLineParam( i - 1 ).GetBuffer(),
                 arg.GetBuffer() );
        usage();
    }
    return arg;
 }

void TranslateLayout::ParseCommandLine()
{
    for ( int i = 0; i < GetCommandLineParamCount(); i++ )
    {
        ByteString aParam = GetCommandLineParam( i );
        if ( aParam.Equals( "-h" ) || aParam.Equals( "--help" ) )
            usage();
        else if ( aParam.Equals( "-l" ) || aParam.Equals( "--language" ) )
            mLanguages.push_back ( GetOptionArgument( ++i ) );
        else if ( aParam.Equals( "-m" ) || aParam.Equals( "--merge" ) )
        {
            mMergeMode = true;
            mLocalize = GetOptionArgument( ++i );
        }
        else if ( aParam.Equals( "-o" ) || aParam.Equals( "--output" ) )
            mOutput = ConvertSystemPath( GetOptionArgument( ++i ) );
        else if ( !aParam.CompareTo( "-", 1 ) )
        {
            fprintf( stderr, "error: No such option: %s\n", aParam.GetBuffer() );
            usage();
        }
        else
            mFiles.push_back( ConvertSystemPath( aParam ) );
    }
    if ( !mFiles.size() )
    {
        fprintf( stderr, "error: No XML-FILE found\n" );
        usage();
    }
}

static XMLAttribute*
findAttribute( XMLAttributeList* lst, String const& name )
{
    for ( sal_uLong i = 0; i < lst->Count(); i++ )
        if ( lst->GetObject( i )->Equals( name ) )
            return lst->GetObject( i );
    return 0;
}

static XMLAttribute*
translateAttribute( XMLAttributeList* lst,
                    String const& name, String const& translation )
{
    if ( XMLAttribute* a = findAttribute( lst, name ) )
        return lst->Replace ( new XMLAttribute( name.Copy( 1 ), translation ), a );
    return 0;
}

static void
translateElement( XMLElement* element, ByteString const& lang,
                  ResData* resData, MergeDataFile& mergeData )
{
    XMLAttributeList* attributes = element->GetAttributeList();
    std::vector<XMLAttribute*> interesting( interestingAttributes( attributes ) );


    if( !interesting.empty() )
    {
        std::vector<XMLAttribute*>::iterator i( interesting.begin() );
        ByteString id = BSTRING( (*i++)->GetValue() );
        for ( ; i != interesting.end(); ++i )
        {
            ByteString attributeId = id;
            attributeId += BSTRING ( **i );
            resData->sGId = attributeId;
            resData->sId = element->GetOldref();

            if ( PFormEntrys* entry = mergeData.GetPFormEntrys( resData ) )
            {
                ByteString translation;
                entry->GetText( translation, STRING_TYP_TEXT, lang, true );
    //            ByteString original = removeContent( element );
                if ( !translation.Len() )
#if 0
                    translation = original;
#else
                    translation = BSTRING( ( *i )->GetValue() );
#endif
                delete translateAttribute( attributes, **i , STRING( translation ) );
            }
        }
    }
}

static bool is_dir( ByteString const& name )
{
    DirectoryItem aItem;
    OUString sFileURL( name.GetBuffer(), name.Len(), RTL_TEXTENCODING_UTF8 );
    FileBase::getFileURLFromSystemPath( sFileURL, sFileURL );
    if( DirectoryItem::get( sFileURL, aItem ) == FileBase::E_None )
    {
        FileStatus aStatus(FileStatusMask_Type);
        if( aItem.getFileStatus( aStatus ) == FileBase::E_None )
        {
            if( aStatus.getFileType() == FileStatus::Directory )
                return true;
        }
    }
    return false;
}

static void make_directory( ByteString const& name )
{
    OUString sFileURL( name.GetBuffer(), name.Len(), RTL_TEXTENCODING_UTF8 );
    FileBase::getFileURLFromSystemPath( sFileURL, sFileURL );
    Directory::create( sFileURL );
}

static void insertMarker( XMLParentNode *p, ByteString const& file )
{
    if ( XMLChildNodeList* lst = p->GetChildList() )
        if ( lst->Count() )
        {
            sal_uLong i = 1;
            // Skip newline, if possible.
            if ( lst->Count() > 1
                 && lst->GetObject( 2 )->GetNodeType() == XML_NODE_TYPE_DEFAULT )
                i++;
            OUString marker = OUString::createFromAscii( "\n    NOTE: This file has been generated automagically by transex3/layout/tralay,\n          from source template: " )
                + STRING( file )
                + OUString::createFromAscii( ".\n          Do not edit, changes will be lost.\n" );
            lst->Insert( new XMLComment( marker, 0 ), i );
        }
}

void TranslateLayout::MergeLanguage( ByteString const& language )
{
    ByteString xmlFile = mFiles.front();

    MergeDataFile mergeData( mLocalize, xmlFile,
                             sal_False, RTL_TEXTENCODING_MS_1252 );

    DirEntry aFile( xmlFile );
    SimpleXMLParser aParser;
    LayoutXMLFile* layoutXml = new LayoutXMLFile( mMergeMode );
    if ( !aParser.Execute( aFile.GetFull() , STRING( xmlFile ), layoutXml ) )
    {
        fprintf(stderr, "error: parsing: %s\n", xmlFile.GetBuffer() );
        return;
    }
    
    layoutXml->Extract();
    insertMarker( layoutXml, xmlFile );

    ResData resData( "", "", "" );
    resData.sResTyp = mProject; /* mGid1 ?? */
    resData.sFilename = xmlFile;

    XMLHashMap* xmlStrings = layoutXml->GetStrings();
    for ( XMLHashMap::iterator i = xmlStrings->begin(); i != xmlStrings->end();
          ++i )
    {
        if ( LangHashMap* languageMap = i->second )
            if ( XMLElement* element = ( *languageMap )[ "en-US" ] )
                translateElement( element, language, &resData, mergeData );
    }
    
#ifndef WNT
    ByteString outFile = "/dev/stdout";
#else
    ByteString outFile = "\\\\.\\CON";
#endif
    if ( mOutput.Len() )
    {
        outFile = mOutput;
        if ( is_dir( outFile ) )
        {
            ByteString outDir = mOutput;
            outDir.Append( "/" ).Append( language );
            if ( !is_dir( outDir ) )
                make_directory( outDir );
            outFile = outDir;
            outFile.Append( "/" ).Append( xmlFile );
        }
    }
    layoutXml->Write( outFile );
    delete layoutXml;
}

void TranslateLayout::Merge()
{
    if ( mLanguages.size() )
        for ( std::vector<ByteString>::iterator i = mLanguages.begin();
              i != mLanguages.end(); ++i)
            MergeLanguage( *i );
    else
        MergeLanguage( mLanguage );
}

void TranslateLayout::CreateSDF()
{
    ByteString xmlFile = mFiles.front();
#ifndef WNT
    ByteString sdf = "/dev/stdout";
#else
    ByteString sdf = "\\\\.\\CON";
#endif
    if ( mOutput.Len() )
        sdf = mOutput;
    Export::SetLanguages( mLanguages );
    HelpParser::CreateSDF( sdf, mProject, mRoot, xmlFile,
                           new LayoutXMLFile( mMergeMode ), mGid1 );
}

void TranslateLayout::ExceptionalMain()
{
    ParseCommandLine();
    if ( mLanguages.size() )
        mLanguage = mLanguages.front();
    if ( mMergeMode )
        Merge();
    else
        CreateSDF();
}

void TranslateLayout::Main()
{
    try
    {
        ExceptionalMain();
    }
    catch ( xml::sax::SAXException& rExc )
    {
        OString aStr( OUStringToOString( rExc.Message,
                                         RTL_TEXTENCODING_ASCII_US ) );
        uno::Exception exc;
        if (rExc.WrappedException >>= exc)
        {
            aStr += OString( " >>> " );
            aStr += OUStringToOString( exc.Message, RTL_TEXTENCODING_ASCII_US );
        }
        fprintf( stderr, "error: parsing: '%s'\n", aStr.getStr() );
        OSL_ENSURE( 0, aStr.getStr() );
    }
    catch ( uno::Exception& rExc )
    {
        OString aStr( OUStringToOString( rExc.Message,
                                         RTL_TEXTENCODING_ASCII_US ) );
        fprintf( stderr, "error: UNO: '%s'\n", aStr.getStr() );
        OSL_ENSURE( 0, aStr.getStr() );
    }
}

TranslateLayout::TranslateLayout()
    : Application()
    , mGid1( "layout" )
    , mLanguage( "en-US" )
    , mLocalize( "localize.sdf" )
    , mOutput()
    , mProject( "layout" )
    , mRoot()
    , mMergeMode( false )
    , mLanguages()
    , mFiles()
{
}

TranslateLayout::~TranslateLayout()
{
}

SAL_IMPLEMENT_MAIN()
{
    TranslateLayout t;
    t.Main();
    return 0;
}
