/**************************************************************
 * 
 * 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_sc.hxx"



// INCLUDE ---------------------------------------------------------------
#include <tools/list.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/bindings.hxx>
#include <svl/zforlist.hxx>

#include "ddelink.hxx"
#include "brdcst.hxx"
#include "document.hxx"
#include "scmatrix.hxx"
#include "patattr.hxx"
#include "rechead.hxx"
#include "rangeseq.hxx"
#include "sc.hrc"
#include "hints.hxx"

TYPEINIT2(ScDdeLink,::sfx2::SvBaseLink,SfxBroadcaster);

#define DDE_TXT_ENCODING 	gsl_getSystemTextEncoding()

sal_Bool ScDdeLink::bIsInUpdate = sal_False;

//------------------------------------------------------------------------

ScDdeLink::ScDdeLink( ScDocument* pD, const String& rA, const String& rT, const String& rI,
						sal_uInt8 nM ) :
    ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ALWAYS,FORMAT_STRING),
	pDoc( pD ),
	aAppl( rA ),
	aTopic( rT ),
	aItem( rI ),
	nMode( nM ),
	bNeedUpdate( sal_False ),
	pResult( NULL )
{
}

__EXPORT ScDdeLink::~ScDdeLink()
{
	// Verbindung aufheben

    // pResult is refcounted
}

ScDdeLink::ScDdeLink( ScDocument* pD, const ScDdeLink& rOther ) :
    ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ALWAYS,FORMAT_STRING),
	pDoc	( pD ),
	aAppl	( rOther.aAppl ),
	aTopic	( rOther.aTopic ),
	aItem	( rOther.aItem ),
	nMode	( rOther.nMode ),
	bNeedUpdate( sal_False ),
	pResult	( NULL )
{
	if (rOther.pResult)
		pResult = rOther.pResult->Clone();
}

ScDdeLink::ScDdeLink( ScDocument* pD, SvStream& rStream, ScMultipleReadHeader& rHdr ) :
    ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ALWAYS,FORMAT_STRING),
	pDoc( pD ),
	bNeedUpdate( sal_False ),
	pResult( NULL )
{
	rHdr.StartEntry();

	rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
	rStream.ReadByteString( aAppl, eCharSet );
	rStream.ReadByteString( aTopic, eCharSet );
	rStream.ReadByteString( aItem, eCharSet );

	sal_Bool bHasValue;
	rStream >> bHasValue;
	if ( bHasValue )
		pResult = new ScMatrix( rStream );

	if (rHdr.BytesLeft())		// neu in 388b und der 364w (RealTime-Client) Version
		rStream >> nMode;
	else
		nMode = SC_DDE_DEFAULT;

	rHdr.EndEntry();
}

void ScDdeLink::Store( SvStream& rStream, ScMultipleWriteHeader& rHdr ) const
{
	rHdr.StartEntry();

	rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
	rStream.WriteByteString( aAppl, eCharSet );
	rStream.WriteByteString( aTopic, eCharSet );
	rStream.WriteByteString( aItem, eCharSet );

	sal_Bool bHasValue = ( pResult != NULL );
	rStream << bHasValue;
	if (bHasValue)
		pResult->Store( rStream );

	if( rStream.GetVersion() > SOFFICE_FILEFORMAT_40 )		// nicht bei 4.0 Export
		rStream << nMode;									// seit 388b

	//	Links mit Mode != SC_DDE_DEFAULT werden bei 4.0 Export komplett weggelassen
	//	(aus ScDocument::SaveDdeLinks)

	rHdr.EndEntry();
}

void __EXPORT ScDdeLink::DataChanged( const String& rMimeType,
								const ::com::sun::star::uno::Any & rValue )
{
	//	wir koennen nur Strings...
	if ( FORMAT_STRING != SotExchange::GetFormatIdFromMimeType( rMimeType ))
		return;

	String aLinkStr;
	ScByteSequenceToString::GetString( aLinkStr, rValue, DDE_TXT_ENCODING );
	aLinkStr.ConvertLineEnd(LINEEND_LF);

	//	wenn String mit Zeilenende aufhoert, streichen:

	xub_StrLen nLen = aLinkStr.Len();
	if (nLen && aLinkStr.GetChar(nLen-1) == '\n')
		aLinkStr.Erase(nLen-1);

	String aLine;
	SCSIZE nCols = 1;		// Leerstring -> eine leere Zelle
	SCSIZE nRows = 1;
	if (aLinkStr.Len())
	{
		nRows = static_cast<SCSIZE>(aLinkStr.GetTokenCount( '\n' ));
		aLine = aLinkStr.GetToken( 0, '\n' );
		if (aLine.Len())
			nCols = static_cast<SCSIZE>(aLine.GetTokenCount( '\t' ));
	}

	if (!nRows || !nCols)				// keine Daten
	{
        pResult.Clear();
	}
	else								// Daten aufteilen
	{
		//	Matrix immer neu anlegen, damit bIsString nicht durcheinanderkommt
		pResult = new ScMatrix( nCols, nRows );

		SvNumberFormatter* pFormatter = pDoc->GetFormatTable();

		//	nMode bestimmt, wie der Text interpretiert wird (#44455#/#49783#):
		//	SC_DDE_DEFAULT - Zahlformat aus Zellvorlage "Standard"
		//	SC_DDE_ENGLISH - Standard-Zahlformat fuer English/US
		//	SC_DDE_TEXT    - ohne NumberFormatter direkt als String
		sal_uLong nStdFormat = 0;
		if ( nMode == SC_DDE_DEFAULT )
		{
			ScPatternAttr* pDefPattern = pDoc->GetDefPattern();		// enthaelt Standard-Vorlage
			if ( pDefPattern )
				nStdFormat = pDefPattern->GetNumberFormat( pFormatter );
		}
		else if ( nMode == SC_DDE_ENGLISH )
			nStdFormat = pFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US);

		String aEntry;
		for (SCSIZE nR=0; nR<nRows; nR++)
		{
			aLine = aLinkStr.GetToken( (xub_StrLen) nR, '\n' );
			for (SCSIZE nC=0; nC<nCols; nC++)
			{
				aEntry = aLine.GetToken( (xub_StrLen) nC, '\t' );
				sal_uInt32 nIndex = nStdFormat;
				double fVal;
				if ( nMode != SC_DDE_TEXT && pFormatter->IsNumberFormat( aEntry, nIndex, fVal ) )
					pResult->PutDouble( fVal, nC, nR );
				else
					pResult->PutString( aEntry, nC, nR );
			}
		}
	}

	//	Es hat sich was getan...

	if (HasListeners())
	{
		Broadcast( ScHint( SC_HINT_DATACHANGED, ScAddress(), NULL ) );
		pDoc->TrackFormulas();		// muss sofort passieren
		pDoc->StartTrackTimer();

		//	StartTrackTimer ruft asynchron TrackFormulas, Broadcast(FID_DATACHANGED),
		//	ResetChanged, SetModified und Invalidate(SID_SAVEDOC/SID_DOC_MODIFIED)
		//	TrackFormulas zusaetzlich nochmal sofort, damit nicht z.B. durch IdleCalc
		//	eine Formel berechnet wird, die noch im FormulaTrack steht (#61676#)

		//	notify Uno objects (for XRefreshListener)
		//	must be after TrackFormulas
		//!	do this asynchronously?
		ScLinkRefreshedHint aHint;
		aHint.SetDdeLink( aAppl, aTopic, aItem, nMode );
		pDoc->BroadcastUno( aHint );
	}
}

void ScDdeLink::ResetValue()
{
    pResult.Clear();

	//	Es hat sich was getan...
	//	Tracking, FID_DATACHANGED etc. passiert von aussen

	if (HasListeners())
		Broadcast( ScHint( SC_HINT_DATACHANGED, ScAddress(), NULL ) );
}

void __EXPORT ScDdeLink::ListenersGone()
{
	sal_Bool bWas = bIsInUpdate;
	bIsInUpdate = sal_True;				// Remove() kann Reschedule ausloesen??!?

	ScDocument* pStackDoc = pDoc;	// member pDoc can't be used after removing the link

	sfx2::LinkManager* pLinkMgr = pDoc->GetLinkManager();
	pLinkMgr->Remove( this);		// deletes this

	if ( !pLinkMgr->GetLinks().Count() )			// letzten geloescht ?
	{
		SfxBindings* pBindings = pStackDoc->GetViewBindings();		// don't use member pDoc!
		if (pBindings)
			pBindings->Invalidate( SID_LINKS );
	}

	bIsInUpdate = bWas;
}

void ScDdeLink::TryUpdate()
{
	if (bIsInUpdate)
		bNeedUpdate = sal_True;			// kann jetzt nicht ausgefuehrt werden
	else
	{
		bIsInUpdate = sal_True;
		//Application::Reschedule();	//! OS/2-Simulation
        pDoc->IncInDdeLinkUpdate();
		Update();
        pDoc->DecInDdeLinkUpdate();
		bIsInUpdate = sal_False;
		bNeedUpdate = sal_False;
	}
}


