/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: svxmsbas.cxx,v $
 * $Revision: 1.24 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
#include <tools/debug.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/app.hxx>
#include <basic/basmgr.hxx>
#include <basic/sbmod.hxx>
#include <svxerr.hxx>
#include <svxmsbas.hxx>
#include <msvbasic.hxx>
#include <svx/msocximex.hxx>
#include <sot/storinfo.hxx>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/awt/XControlModel.hpp>
using namespace com::sun::star::beans;
using namespace com::sun::star::io;
using namespace com::sun::star::awt;
#include <comphelper/storagehelper.hxx>

#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/script/XLibraryContainer.hpp>
using namespace com::sun::star::container;
using namespace com::sun::star::script;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star;

using rtl::OUString;

int SvxImportMSVBasic::Import( const String& rStorageName,
								const String &rSubStorageName,
								BOOL bAsComment, BOOL bStripped )
{
	int nRet = 0;
	if( bImport && ImportCode_Impl( rStorageName, rSubStorageName,
									bAsComment, bStripped ))
		nRet |= 1;

    if (bImport)
        ImportForms_Impl(rStorageName, rSubStorageName);

	if( bCopy && CopyStorage_Impl( rStorageName, rSubStorageName ))
		nRet |= 2;

	return nRet;
}

bool SvxImportMSVBasic::ImportForms_Impl(const String& rStorageName,
    const String& rSubStorageName)
{
    SvStorageRef xVBAStg(xRoot->OpenSotStorage(rStorageName,
        STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL));
    if (!xVBAStg.Is() || xVBAStg->GetError())
        return false;

    std::vector<String> aUserForms;
    SvStorageInfoList aContents;
    xVBAStg->FillInfoList(&aContents);
    for (USHORT nI = 0; nI < aContents.Count(); ++nI)
    {
          SvStorageInfo& rInfo = aContents.GetObject(nI);
          if (!rInfo.IsStream() && rInfo.GetName() != rSubStorageName)
              aUserForms.push_back(rInfo.GetName());
    }

    if (aUserForms.empty())
        return false;

    bool bRet = true;
    SFX_APP()->EnterBasicCall();
    try
    {
        Reference<XMultiServiceFactory> xSF(comphelper::getProcessServiceFactory());

        Reference<XComponentContext> xContext;
        Reference<XPropertySet> xProps(xSF, UNO_QUERY);
        xProps->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("DefaultContext")) ) >>= xContext;


        Reference<XLibraryContainer> xLibContainer = rDocSh.GetDialogContainer();
        DBG_ASSERT( xLibContainer.is(), "No BasicContainer!" );

        String aLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) );
        Reference<XNameContainer> xLib;
        if (xLibContainer.is())
        {
            if( !xLibContainer->hasByName(aLibName))
                xLibContainer->createLibrary(aLibName);

            Any aLibAny = xLibContainer->getByName( aLibName );
            aLibAny >>= xLib;
        }

        if(xLib.is())
        {
            typedef std::vector<String>::iterator myIter;
            myIter aEnd = aUserForms.end();
            for (myIter aIter = aUserForms.begin(); aIter != aEnd; ++aIter)
            {
                SvStorageRef xForm (xVBAStg->OpenSotStorage(*aIter,
                    STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL));

                if (!xForm.Is() || xForm->GetError())
                    continue;

                SvStorageStreamRef xFrame = xForm->OpenSotStream(
                    String( RTL_CONSTASCII_USTRINGPARAM( "\3VBFrame" ) ),
                    STREAM_STD_READ | STREAM_NOCREATE);

                if (!xFrame.Is() || xFrame->GetError())
                    continue;

                SvStorageStreamRef xTypes = xForm->OpenSotStream(
                    String( 'f' ), STREAM_STD_READ | STREAM_NOCREATE);

                if (!xTypes.Is() || xTypes->GetError())
                    continue;

                //<UserForm Name=""><VBFrame></VBFrame>"
                String sData;
                String sLine;
                while(xFrame->ReadByteStringLine(sLine, RTL_TEXTENCODING_MS_1252))
                {
                    sData += sLine;
                    sData += '\n';
                }
                sData.ConvertLineEnd();

                Reference<container::XNameContainer> xDialog(
                    xSF->createInstance(
                       OUString(RTL_CONSTASCII_USTRINGPARAM(
                           "com.sun.star.awt.UnoControlDialogModel"))), uno::UNO_QUERY);

                OCX_UserForm aForm(xVBAStg, *aIter, *aIter, xDialog, xSF );
                aForm.pDocSh = &rDocSh;
                sal_Bool bOk = aForm.Read(xTypes);
                DBG_ASSERT(bOk, "Had unexpected content, not risking this module");
                if (bOk)
                    aForm.Import(xLib);
            }
        }
    }
    catch(...)
    {
        DBG_ERRORFILE( "SvxImportMSVBasic::ImportForms_Impl - any exception caught" );
        //bRet = false;
    }
    SFX_APP()->LeaveBasicCall();
    return bRet;
}


BOOL SvxImportMSVBasic::CopyStorage_Impl( const String& rStorageName,
									 	const String& rSubStorageName)
{
	BOOL bValidStg = FALSE;
	{
		SvStorageRef xVBAStg( xRoot->OpenSotStorage( rStorageName,
									STREAM_READWRITE | STREAM_NOCREATE |
									STREAM_SHARE_DENYALL ));
		if( xVBAStg.Is() && !xVBAStg->GetError() )
		{
			SvStorageRef xVBASubStg( xVBAStg->OpenSotStorage( rSubStorageName,
								 	STREAM_READWRITE | STREAM_NOCREATE |
									STREAM_SHARE_DENYALL ));
			if( xVBASubStg.Is() && !xVBASubStg->GetError() )
			{
                // then we will copy these storages into the (temporary) storage of the document
				bValidStg = TRUE;
			}
		}
	}

	if( bValidStg )
	{
		String aDstStgName( GetMSBasicStorageName() );
        SotStorageRef xDst = SotStorage::OpenOLEStorage( rDocSh.GetStorage(), aDstStgName, STREAM_READWRITE | STREAM_TRUNC );
        SotStorageRef xSrc = xRoot->OpenSotStorage( rStorageName, STREAM_STD_READ );

        // TODO/LATER: should we commit the storage?
        xSrc->CopyTo( xDst );
        xDst->Commit();
        ErrCode nError = xDst->GetError();
        if ( nError == ERRCODE_NONE )
            nError = xSrc->GetError();
        if ( nError != ERRCODE_NONE )
            xRoot->SetError( nError );
        else
            bValidStg = TRUE;
	}

	return bValidStg;
}

BOOL SvxImportMSVBasic::ImportCode_Impl( const String& rStorageName,
										const String &rSubStorageName,
										BOOL bAsComment, BOOL bStripped )
{
	BOOL bRet = FALSE;
	VBA_Impl aVBA( *xRoot, bAsComment );
	if( aVBA.Open(rStorageName,rSubStorageName) )
	{
		SFX_APP()->EnterBasicCall();
        Reference<XLibraryContainer> xLibContainer = rDocSh.GetBasicContainer();
		DBG_ASSERT( xLibContainer.is(), "No BasicContainer!" );

        UINT16 nStreamCount = aVBA.GetNoStreams();
		Reference<XNameContainer> xLib;
        if( xLibContainer.is() && nStreamCount )
        {
            String aLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) );
		    if( !xLibContainer->hasByName( aLibName ) )
			    xLibContainer->createLibrary( aLibName );

            Any aLibAny = xLibContainer->getByName( aLibName );
		    aLibAny >>= xLib;
        }
		if( xLib.is() )
		{
			for( UINT16 i=0; i<nStreamCount;i++)
			{
				StringArray aDecompressed = aVBA.Decompress(i);
#if 0
/*  DR 2005-08-11 #124850# Do not filter special characters from module name.
    Just put the original module name and let the Basic interpreter deal with
    it. Needed for roundtrip...
 */
				ByteString sByteBasic(aVBA.GetStreamName(i),
					RTL_TEXTENCODING_ASCII_US,
						(RTL_UNICODETOTEXT_FLAGS_UNDEFINED_UNDERLINE|
						RTL_UNICODETOTEXT_FLAGS_INVALID_UNDERLINE |
						RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 |
						RTL_UNICODETOTEXT_FLAGS_NOCOMPOSITE)
				);

				const String sBasicModule(sByteBasic,
					RTL_TEXTENCODING_ASCII_US);
#else
                const String &sBasicModule = aVBA.GetStreamName( i);
#endif
                /* #117718# expose information regarding type of Module
                * Class, Form or plain 'ould VBA module with a REM statment
                * at the top of the module. Mapping of Module Name
                * to type is performed in  VBA_Impl::Open() method,
                * ( msvbasic.cxx ) by examining the PROJECT stream.
                */

                // using name from aVBA.GetStreamName
                // because the encoding of the same returned
                // is the same as the encoding for the names
                // that are keys in the map used by GetModuleType method
                const String &sOrigVBAModName = aVBA.GetStreamName( i );
                ModuleType mType = aVBA.GetModuleType( sOrigVBAModName );

                rtl::OUString sClassRem( RTL_CONSTASCII_USTRINGPARAM( "Rem Attribute VBA_ModuleType=" ) );

                rtl::OUString modeTypeComment;

                switch( mType )
                {
                    case Class:
                        modeTypeComment = sClassRem +
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAClassModule\n" ) );
                        break;
                    case Form:
                        modeTypeComment = sClassRem +
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAFormModule\n" ) );
                        break;
                    case Document:
                        modeTypeComment = sClassRem +
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBADocumentModule\n" ) );
                        break;
                    case Normal:
                        modeTypeComment = sClassRem +
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAModule\n" ) );
                        break;
                    case Unknown:
                        modeTypeComment = sClassRem +
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAUnknown\n" ) );
                        break;
                    default:
                        DBG_ERRORFILE( "SvxImportMSVBasic::ImportCode_Impl - unknown module type" );
                        break;
                }
				static ::rtl::OUString sVBAOption( RTL_CONSTASCII_USTRINGPARAM( "Option VBASupport 1\n" ) ); 
				static ::rtl::OUString sClassOption( RTL_CONSTASCII_USTRINGPARAM( "Option ClassModule\n" ) ); 
				if ( !bAsComment )
				{
					modeTypeComment = modeTypeComment + sVBAOption;
					if ( mType == Class )
						modeTypeComment = modeTypeComment + sClassOption;

				}

				String sModule(sBasicModule); //#i52606# no need to split Macros in 64KB blocks any more!
				String sTemp;
				if (bAsComment)
				{
					sTemp+=String(RTL_CONSTASCII_USTRINGPARAM( "Sub " ));
					String sMunge(sModule);
					//Streams can have spaces in them, but modulenames
					//cannot !
					sMunge.SearchAndReplaceAll(' ','_');

					sTemp += sMunge;
					sTemp.AppendAscii("\n");
				};
	            ::rtl::OUString aSource(sTemp);

				for(ULONG j=0;j<aDecompressed.GetSize();j++)
				{
					if (bStripped)
					{
                        String *pStr = aDecompressed.Get(j);
                        bool bMac = true;
                        xub_StrLen nBegin = pStr->Search('\x0D');
                        if ((STRING_NOTFOUND != nBegin) && (pStr->Len() > 1) && (pStr->GetChar(nBegin+1) == '\x0A'))
                            bMac = false;

                        const char cLineEnd = bMac ? '\x0D' : '\x0A';
                        const String sAttribute(String::CreateFromAscii(
                            bAsComment ? "Rem Attribute" : "Attribute"));
						nBegin = 0;
						while (STRING_NOTFOUND != (nBegin =	pStr->Search(sAttribute, nBegin)))
						{
                            if ((nBegin) && pStr->GetChar(nBegin-1) != cLineEnd)
							{
                                // npower #i63766# Need to skip instances of Attribute
								// that are NOT Attribute statements
								nBegin = nBegin + sAttribute.Len();
                                continue;
							}
							xub_StrLen nEnd = pStr->Search(cLineEnd ,nBegin);
                            // DR #i26521# catch STRING_NOTFOUND, will loop endless otherwise
                            if( nEnd == STRING_NOTFOUND )
                                pStr->Erase();
                            else
                                pStr->Erase(nBegin, (nEnd-nBegin)+1);
						}
					}
					if( aDecompressed.Get(j)->Len() )
					{
			            aSource+=::rtl::OUString( *aDecompressed.Get(j) );
					}

				}
				if (bAsComment)
				{
						aSource += rtl::OUString::createFromAscii("\nEnd Sub");
				}
				::rtl::OUString aModName( sModule );
				if ( aSource.getLength() )
				{
					aSource = modeTypeComment + aSource;

					Any aSourceAny;
					aSourceAny <<= aSource;
					if( xLib->hasByName( aModName ) )
						xLib->replaceByName( aModName, aSourceAny );
					else
						xLib->insertByName( aModName, aSourceAny );
				}                                

				bRet = true;
			}
		}
		SFX_APP()->LeaveBasicCall();
	}
	return bRet;
}

ULONG SvxImportMSVBasic::SaveOrDelMSVBAStorage( BOOL bSaveInto,
												const String& rStorageName )
{
	ULONG nRet = ERRCODE_NONE;
    uno::Reference < embed::XStorage > xSrcRoot( rDocSh.GetStorage() );
	String aDstStgName( GetMSBasicStorageName() );
    SotStorageRef xVBAStg( SotStorage::OpenOLEStorage( xSrcRoot, aDstStgName,
                                STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL ) );
	if( xVBAStg.Is() && !xVBAStg->GetError() )
	{
		xVBAStg = 0;
		if( bSaveInto )
		{
			BasicManager *pBasicMan = rDocSh.GetBasicManager();
			if( pBasicMan && pBasicMan->IsBasicModified() )
				nRet = ERRCODE_SVX_MODIFIED_VBASIC_STORAGE;

            SotStorageRef xSrc = SotStorage::OpenOLEStorage( xSrcRoot, aDstStgName, STREAM_STD_READ );
            SotStorageRef xDst = xRoot->OpenSotStorage( rStorageName, STREAM_READWRITE | STREAM_TRUNC );
            xSrc->CopyTo( xDst );
            xDst->Commit();
            ErrCode nError = xDst->GetError();
            if ( nError == ERRCODE_NONE )
                nError = xSrc->GetError();
            if ( nError != ERRCODE_NONE )
                xRoot->SetError( nError );
		}
	}

	return nRet;
}

// check if the MS-VBA-Storage exists in the RootStorage of the DocShell.
// If it exists, then return the WarningId for losing the information.
ULONG SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( SfxObjectShell &rDocSh)
{
    uno::Reference < embed::XStorage > xSrcRoot( rDocSh.GetStorage() );
    SvStorageRef xVBAStg( SotStorage::OpenOLEStorage( xSrcRoot, GetMSBasicStorageName(),
					STREAM_READ | STREAM_NOCREATE | STREAM_SHARE_DENYALL ));
	return ( xVBAStg.Is() && !xVBAStg->GetError() )
					? ERRCODE_SVX_VBASIC_STORAGE_EXIST
					: ERRCODE_NONE;
}

String SvxImportMSVBasic::GetMSBasicStorageName()
{
    return String( RTL_CONSTASCII_USTRINGPARAM( "_MS_VBA_Macros" ) );
}

/* vi:set tabstop=4 shiftwidth=4 expandtab: */
