/*
  Copyright (c) 2006 Gábor Lehel <illissius@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include <config.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <tqdeepcopy.h>
#include <tqstring.h>
#include <pthread.h>

#include "atomicstring.h"

class AtomicString::Data: public TQString
{
public:
    uint refcount;
    Data(): refcount( 0 ) { }
    Data( const TQString &s ): TQString( s ), refcount( 0 ) { }
};

AtomicString::AtomicString(): m_string( 0 ) { }

AtomicString::AtomicString( const AtomicString &other )
{
    s_storeMutex.lock();
    m_string = other.m_string;
    ref( m_string );
    s_storeMutex.unlock();
}

AtomicString::AtomicString( const TQString &string ): m_string( 0 )
{
    if( string.isEmpty() )
        return;

    Data *s = new Data( string );  // note: s is a shallow copy
    s_storeMutex.lock();
    m_string = static_cast<Data*>( *( s_store.insert( s ).first ) );
    ref( m_string );
    uint rc = s->refcount;
    if( rc && !isMainThread()) {
	// Inserted, and we are not in the main thread -- we need to make s a deep copy,
	// as this copy may be refcounted by the main thread outside our locks
	(TQString &) (*s) = TQDeepCopy<TQString>( string );
    }
    s_storeMutex.unlock();
    if ( !rc ) delete( s );	// already present
}

AtomicString::~AtomicString()
{
    s_storeMutex.lock();
    deref( m_string );
    s_storeMutex.unlock();
}

TQString AtomicString::string() const
{
    if ( !m_string ) return TQString();
    // References to the stored string are only allowed to circulate in the main thread
    if ( isMainThread() ) return *m_string;
    else return deepCopy();
}

TQString AtomicString::deepCopy() const
{
    if (m_string)
	return TQString( m_string->unicode(), m_string->length() );
    return TQString();
}

bool AtomicString::isEmpty() const
{
    return !m_string;
}

const TQString *AtomicString::ptr() const
{
    if( m_string )
        return m_string;
    return &null_tqstring;
}

uint AtomicString::refcount() const
{
    if ( m_string ) {
	s_storeMutex.lock();
	uint rc = m_string->refcount;
	s_storeMutex.unlock();
	return rc;
    }
    return 0;
}

AtomicString &AtomicString::operator=( const AtomicString &other )
{
    if( m_string == other.m_string )
        return *this;
    s_storeMutex.lock();
    deref( m_string );
    m_string = other.m_string;
    ref( m_string );
    s_storeMutex.unlock();
    return *this;
}

// needs to be called holding the lock
inline void AtomicString::deref( Data *s )
{
    checkLazyDeletes();         // a good time to do this
    if( !s )
        return;
    if( !( --s->refcount ) )
    {
        s_store.erase( s );
        // only the main thread is allowed to delete stored strings
        if ( isMainThread() )
            delete s;
        else
            s_lazyDeletes.append(s);
    }
}

// needs to be called holding the lock
inline void AtomicString::ref( Data *s )
{
    checkLazyDeletes();         // a good time to do this
    if( s )
        s->refcount++;
}

// It is not necessary to hold the store mutex here.
bool AtomicString::isMainThread()
{
    // For isMainThread(), we could use TQThread::currentThread(), except the
    // docs say it's unreliable. And in general TQThreads don't like to be called from
    // app destructors. Good old pthreads will serve us well. As for Windows, these
    // two calls surely have equivalents; better yet we'll have QT4 and thread safe
    // TQStrings by then.
    // Note that the the static local init is thread safe.
    static pthread_t main_thread = pthread_self();
    return pthread_equal(pthread_self(), main_thread);
}

// call holding the store mutex
inline void AtomicString::checkLazyDeletes()
{
    // only the main thread is allowed to delete
    if ( isMainThread() )
    {
        s_lazyDeletes.setAutoDelete(true);
        s_lazyDeletes.clear();
    }
}

AtomicString::set_type AtomicString::s_store;
TQPtrList<TQString> AtomicString::s_lazyDeletes;
TQMutex AtomicString::s_storeMutex;
