/***************************************************************************
copyright            : (C) 2006 by David Nolden
email                : david.nolden.kdevelop@art-master.de
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include <tqtl.h>
#include <typeinfo>
#include "simpletypenamespace.h"
#include "simpletypecachebinder.h"

#include "safetycounter.h"

//#define PHYSICAL_IMPORT
//Necessary, because else nested members cannot search the correct scope
#define PHYSICALLY_IMPORT_NAMESPACES

extern SafetyCounter safetyCounter;

//SimpleTypeNamespace implementation

TypePointer SimpleTypeNamespace::clone() {
  return new SimpleTypeCachedNamespace( this );
}

SimpleTypeNamespace::SimpleTypeNamespace( const TQStringList& fakeScope, const TQStringList& realScope ) : SimpleTypeImpl( fakeScope ), m_currentSlaveId(0) {
  ifVerbose( dbg() << "\"" << str() << "\": created namespace-proxy with real scope \"" << realScope.join( "::" ) << "\"" << endl );
  SimpleType cm = SimpleType( realScope, HashedStringSet(), RepoCodeModel );
  SimpleType ct = SimpleType( realScope, HashedStringSet(), RepoCatalog );
  cm = SimpleType( cm->clone() );
  ct = SimpleType( ct->clone() );
  cm->setMasterProxy( this );
  ct->setMasterProxy( this );
  addImport( cm->desc() );
  addImport( ct->desc() );
}

SimpleTypeNamespace::SimpleTypeNamespace( const TQStringList& fakeScope ) : SimpleTypeImpl( fakeScope ), m_currentSlaveId(0) {
  ifVerbose( dbg() << "\"" << str() << "\": created namespace-proxy" << endl );
}

SimpleTypeNamespace::SimpleTypeNamespace( SimpleTypeNamespace* ns ) : SimpleTypeImpl( ns ), m_currentSlaveId(0) {
  ifVerbose( dbg() << "\"" << str() << "\": cloning namespace" << endl );
  m_aliases = ns->m_aliases;
  m_activeSlaves = ns->m_activeSlaves;
  m_activeSlaveGroups = ns->m_activeSlaveGroups;
}

void SimpleTypeNamespace::breakReferences() {
  m_aliases.clear();
  m_activeSlaves.clear();
  SimpleTypeImpl::breakReferences();
}


SimpleTypeImpl::MemberInfo SimpleTypeNamespace::findMember( TypeDesc name, MemberInfo::MemberType type ) {
  std::set<HashedString> ignore;
  SimpleTypeImpl::MemberInfo ret = findMember( name, type, ignore );
///chooseSpecialization( ret ); should not be necessary
  return ret;
}

TQValueList<TypePointer> SimpleTypeNamespace::getMemberClasses( const TypeDesc& name ) {
  std::set<HashedString> ignore;

  return getMemberClasses( name, ignore );
}

TQValueList<TypePointer>  SimpleTypeNamespace::getMemberClasses( const TypeDesc& name, std::set<HashedString>& ignore )  {
  HashedString myName = HashedString( scope().join( "::" ) +"%"+typeid( *this ).name() );
  if ( ignore.find( myName ) != ignore.end() || !safetyCounter )
    return TQValueList<TypePointer>();

  ignore.insert( myName );

  TQValueList<TypePointer> ret;

  SlaveList l = getSlaves( name.includeFiles() );
  for ( SlaveList::iterator it = l.begin(); it != l.end(); ++it ) {
    if (( *it ).first.first.resolved() ) {
      SimpleTypeNamespace* ns = dynamic_cast<SimpleTypeNamespace*>(( *it ).first.first.resolved().data() );
      if ( !ns ) {
        HashedString thatName = HashedString(( *it ).first.first.resolved()->scope().join( "::" ) +"%"+typeid( *( *it ).first.first.resolved() ).name() );
        if ( ignore.find( thatName ) != ignore.end() ) continue;
        ignore.insert( thatName );
        ret += ( *it ).first.first.resolved()->getMemberClasses( name );
      } else {
        ret += ns->getMemberClasses( name, ignore );

      }

    }
  }

  return ret;
}

SimpleTypeImpl::MemberInfo SimpleTypeNamespace::findMember( TypeDesc name, MemberInfo::MemberType type, std::set
      <HashedString>& ignore ) {
  MemberInfo mem;
  mem.name = "";
  mem.memberType = MemberInfo::NotFound;
  HashedString myName = HashedString( scope().join( "::" ) +"%"+typeid( *this ).name() );
  if ( ignore.find( myName ) != ignore.end() || !safetyCounter )
    return mem;
  ignore.insert( myName );

  SlaveList l = getSlaves( name.includeFiles() );

  ImportList m_aliasImports;

  AliasMap::iterator itt = m_aliases.find( name.name() );

  if ( itt != m_aliases.end() && !( *itt ).empty() ) {
    ifVerbose( dbg() << "\"" << str() << "\": namespace-sub-aliases \"" << name.name() << "\"" << "\" requested, locating targets" << endl );

    for ( ImportList::iterator it = ( *itt ).begin(); it != ( *itt ).end(); ++it ) {
      if ( !( /*name.includeFiles().size() < 1 ||*/ ( *it ).files <= name.includeFiles() ) ) continue; //filter the slave by the include-files

      ifVerbose( dbg() << "\"" << str() << "\": namespace-sub-aliases \"" << name.name() << "\": taking target \"" << ( *it ).import.fullNameChain() << "\"" << endl );
      /*TypeDesc d( (*it).import );
      d.setIncludeFiles( name.includeFiles() );*/
      m_aliasImports.insert( *it ); //@todo: what include-files should be used for searching the namespace?
      /*LocateResult l = locateDecType( d, SimpleTypeImpl::Normal, 0, SimpleTypeImpl::MemberInfo::Namespace );
      if ( !l || !l->resolved() || !dynamic_cast<SimpleTypeNamespace*>( l->resolved().data() ) ) {
      ifVerbose( dbg() << "\"" << str() << "\": namespace-sub-aliases \"" << name.name() << "\" -> \"" << ( *it ).import.fullNameChain() << "\" could not be resolved" << endl );
      } else {
      m_aliasImports.insert( Import(  d.includeFiles(), l, this ) );
      }*/
    }
  }

  for ( SlaveList::iterator it = l.begin(); it != l.end(); ++it ) {
    if ( !( *it ).first.first.resolved() )
      continue;
    if ( ignore.find( HashedString(( *it ).first.first.resolved()->scope().join( "::" ) +"%"+ typeid( *( *it ).first.first.resolved() ).name() ) ) != ignore.end() ) continue;

    ifVerbose( dbg() << "\"" << str() << "\": redirecting search for \"" << name.name() << "\" to \"" << ( *it ) .first.first.fullNameChain() << "\"" << endl );
    if ( !( *it ).first.first.resolved() ) {
      ifVerbose( dbg() << "\"" << str() << "\": while search for \"" << name.name() << "\": Imported namespace \"" << ( *it ) .first.first.fullNameChain() << "\" is not resolved(should have been resolved in updateAliases)" << endl );
      continue;
    }
    ifVerbose( dbg() << "\"Class-type: " << typeid( *( *it ).first.first.resolved().data() ).name() << ")" << endl );
    SimpleTypeNamespace* ns = dynamic_cast<SimpleTypeNamespace*>(( *it ).first.first.resolved().data() );

    if ( ns )
      mem = ns->findMember( name , type, ignore );
    else
      mem = ( *it ).first.first.resolved()->findMember( name, type );

    if ( mem ) {
      if ( mem.memberType != MemberInfo::Namespace ) {
#ifdef PHYSICAL_IMPORT
        TypePointer b = mem.build();
        if ( b && !( b->parent()->masterProxy().data() == this ) ) {
          b = b ->clone(); //expensive, cache is not shared
          b->setParent( this );

          mem.setBuilt( b );
        }
#else
        if( mem.memberType == MemberInfo::NestedType )
          chooseSpecialization( mem );
        TypePointer b = mem.build();
        if( b && b->parent() && b->parent()->masterProxy().data() == this )
          b->setParent( this );
#endif
        return mem;
      } else {
        TypePointer b = mem.build();

        if ( b )
          m_aliasImports.insert( Import( IncludeFiles(), b->desc(), TypePointer() ) );
        else
          ifVerbose( dbg() << "\"" << str() << "\": found namespace \"" << name.name() << "\", but it is not resolved" << endl );
      }
    }
  }

  if ( !m_aliasImports.empty() ) {
    return setupMemberInfo( name.fullNameList().join( "::" ), m_aliasImports );

  }

  return mem;
}

// LocateResult SimpleTypeNamespace::locateSlave( const SlaveList::const_iterator& target, const IncludeFiles& includeFiles ) {
//     for( SlaveList::const_iterator it = m_activeSlaves.begin(); it != target; ++it ) {
//
//     }
// }

SimpleTypeImpl::MemberInfo SimpleTypeNamespace::setupMemberInfo( const TQStringList& subName, const ImportList& imports ) {
  MemberInfo mem;
  mem.name = subName.join( "::" );
  mem.memberType = MemberInfo::NotFound;
  TQStringList sc = scope();
  sc += subName;
  mem.type = sc.join( "::" );
  mem.memberType = MemberInfo::Namespace;
  mem.setBuildInfo( new NamespaceBuildInfo( sc, imports ) );
  return mem;
}

///This must be optimized
void SimpleTypeNamespace::addAliasMap( const TypeDesc& name, const TypeDesc& alias, const IncludeFiles& files, bool recurse, bool symmetric, const TypePointer& perspective ) {
  Debug db;
  if ( !db ) {
    kdDebug( 9007 ) << str() << " addAliasMap: cannot add alias \"" << name.fullNameChain() << "\" -> \"" << alias.fullNameChain() << "\", recursion too deep" << endl;
    return ;
  }
  if ( name.next() ) kdDebug( 9007 ) << "addAliasMap warning: type-alias-name has order higher than one: " << name.fullNameChain() << ", only " << name.name() << " will be used" <<  endl;
  if ( name == alias )
    return ;

  if ( symmetric )
    addAliasMap( alias, name, files, recurse, false );

  invalidateSecondaryCache();
  invalidatePrimaryCache( true ); //Only not-found items are cleared updated here for performance-reasons(found items will stay cached)

  AliasMap::iterator it = m_aliases.find( name.name() );
  if ( it == m_aliases.end() )
    it = m_aliases.insert( name.name(), ImportList() );

  Import a( files, alias, perspective );
  std::pair< ImportList::const_iterator, ImportList::const_iterator > rng = ( *it ).equal_range( a );
  while ( rng.first != rng.second ) {
    if ( rng.first->files == files )
      return ; //The same alias, with the same files, has already been added.
    ++rng.first;
  }

  ( *it ).insert( a );
  ifVerbose( dbg() << "\"" << str() << "\": adding namespace-alias \"" << name.name() << ( !symmetric ? "\" -> \"" : "\" = \"" ) << alias.name() << "\" files:\n[ " << files.print().c_str() << "]\n" << endl );
  ifVerbose( if ( alias.resolved() ) dbg() << "Resolved type of the imported namespace: " << typeid( *alias.resolved() ).name() );

  if ( name.name().isEmpty() ) {
    addImport( alias, files, perspective );
  }
}

std::set<size_t> SimpleTypeNamespace::updateAliases( const IncludeFiles& files/*, bool isRecursion */) {
  std::set<size_t> possibleSlaves;
  if ( m_activeSlaves.empty() || !safetyCounter.ok() ) return possibleSlaves;
//   if( !isRecursion ) {
//     ///Test the cache
//     SlavesCache::const_iterator it = m_slavesCache.find( files );
//     if( it != m_slavesCache.end() && it->second.first == m_slavesCache.size() ) return; ///The cache already contains a valid entry, and the work is done
//   }

  m_activeSlaveGroups.findGroups( files, possibleSlaves );
  if( possibleSlaves.empty() ) return possibleSlaves;

  std::list<size_t> disabled;
  for( std::set<size_t>::const_reverse_iterator it = possibleSlaves.rbegin(); it != possibleSlaves.rend(); ++it ) {
    //Disable all slaves with higher ids
    SlaveMap::iterator current = m_activeSlaves.find( *it );
    if( current == m_activeSlaves.end() ) {
      kdDebug( 9007 ) << "ERROR" << endl;
    }

    SlaveDesc& d( current->second );

    if ( !d.first.first.resolved() ) {
      for( SlaveMap::const_iterator itr = current; itr != m_activeSlaves.end(); ++it ) {
        if( m_activeSlaveGroups.isDisabled( itr->first ) ) break; //stop searching when hitting the first disabled one(assuming that all behind are disabled too)
        disabled.push_back( itr->first );
        m_activeSlaveGroups.disableSet( itr->first );
      }

      TypeDesc descS = d.first.first;
      TypePointer p = d.second; //perspective

      HashedStringSet importIncludeFiles = d.first.second;

      if ( !p ) p = this;

      TypeDesc desc = p->locateDecType( descS, SimpleTypeImpl::Normal, 0, SimpleTypeImpl::MemberInfo::Namespace );
      if ( !desc.resolved() ) {
        ///If the namespace could not be found, help out by including the include-files of the current search
        descS.setIncludeFiles( descS.includeFiles() + files );
        desc = p->locateDecType( descS, SimpleTypeImpl::Normal, 0, SimpleTypeImpl::MemberInfo::Namespace );
      }
      if ( desc.resolved() ) {
        ///If exactly the same namespace was already imported use the earlier imported instance, so they can share a single cache
        ///@todo make more efficient.
        for ( SlaveMap::const_iterator it = m_activeSlaves.begin(); it != m_activeSlaves.end(); ++it ) {
          if (( *it ).second.first.first.resolved() && ( *it ).second.first.first.resolved()->scope() == desc.resolved()->scope() && typeid( *( *it ).second.first.first.resolved().data() ) == typeid( desc.resolved().data() ) ) {
            desc.setResolved(( *it ).second.first.first.resolved() );
            break;
          }
        }
#ifdef PHYSICALLY_IMPORT_NAMESPACES
        if ( desc.resolved()->masterProxy().data() != this ) {
          desc.setResolved( desc.resolved()->clone() ); //expensive, cache is not shared
          desc.resolved()->setMasterProxy( this ); //Possible solution: don't use this, simply set the parents of all found members correctly
        }
#endif
        d.first.first = desc;
      }
    }
  }

  for( std::list<size_t>::const_iterator it = disabled.begin(); it != disabled.end(); ++it ) {
    m_activeSlaveGroups.enableSet( *it );
  }

  return possibleSlaves;
}


void SimpleTypeNamespace::addAliases( TQString map, const IncludeFiles& files ) {
  while ( !map.isEmpty() ) {
    int mid = map.find( "=" );
    int mid2 = map.find( "<<" );
    int found = mid;
    int len = 1;
    if ( mid2 != -1 && ( mid2 < found || found == -1 ) ) {
      found = mid2;
      len = 2;
    }
    if ( found == -1 )
      break;

    int end = map.find( ";", found + len );
    if ( end == -1 ) {
      //break;
      end = map.length();
    }
    if ( end - ( found + len ) < 0 )
      break;

    addAliasMap( map.left( found ).stripWhiteSpace(), map.mid( found + len, end - found - len ).stripWhiteSpace(), files, true, found == mid );
    map = map.mid( end + 1 );
  }
}

void SimpleTypeNamespace::invalidatePrimaryCache( bool onlyNegative ) {
  //m_slavesCache.clear();
  SimpleTypeImpl::invalidatePrimaryCache( onlyNegative );
}

void SimpleTypeNamespace::addImport( const TypeDesc& import, const IncludeFiles& files, TypePointer perspective ) {
  //ifVerbose( dbg() << "
  if ( !perspective ) perspective = this;
  invalidateCache();
  TypeDesc d = import;
  if ( d.resolved() ) {
    #ifdef PHYSICALLY_IMPORT_NAMESPACES

    if( d.resolved()->masterProxy().data() != this ) {
      d.setResolved( d.resolved()->clone() ); //Expensive because of lost caching, think about how necessary this is
      d.resolved()->setMasterProxy( this );
    }
    #endif
  }

  m_activeSlaves[ ++m_currentSlaveId ] =  std::make_pair( std::make_pair( d, files ) , perspective );
  m_activeSlaveGroups.addSet( m_currentSlaveId, files );

  if( d.resolved() ) ///Must be called after the above, because it may insert new slaves, and the order in m_activeSlaves MUST be preserved
    d.resolved()->addAliasesTo( this );
}

bool SimpleTypeNamespace::hasNode() const {
  return true;
}

SimpleTypeNamespace::SlaveList SimpleTypeNamespace::getSlaves( const IncludeFiles& files ) {
  /*  ///Test the cache
  SlavesCache::const_iterator it = m_slavesCache.find( files );
  if( it != m_slavesCache.end() && it->second.first == m_activeSlaves.size() ) return it->second.second; ///The cache already contains a valid entry, and the work is done*/

  std::set<size_t> allSlaves = updateAliases( files );
  SlaveList ret;
#ifdef IMPORT_DEBUG
  for ( SlaveList::const_iterator it = m_activeSlaves.begin(); it != m_activeSlaves.end(); ++it ) {
#ifdef IMPORT_DEBUG
    ifVerbose( dbg() << "\"" << str() << "\": Checking whether \"" << (*it).second.first.first.fullNameChain() << "\" should be imported, current include-files: " << files.print().c_str() << "\nNeeded include-files: " << (*it).second.first.second.print().c_str() << "\n"; )
#endif
    if ( !(( *it ).second.first.second <= files ) ) {
#ifdef IMPORT_DEBUG
      ifVerbose( dbg() << "not imported." );
#endif
      continue;
    }
#ifdef IMPORT_DEBUG
    ifVerbose( dbg() << "imported." << endl );
#endif
    ret.push_back( *it.second );
  }
#else
  ifVerbose( dbg() << str() << " getSlaves() called for \n[ " << files.print().c_str() << endl );
  
  for( std::set<size_t>::const_iterator it = allSlaves.begin(); it != allSlaves.end(); ++it ) {
    SlaveMap::const_iterator itr = m_activeSlaves.find( *it );
    if( itr != m_activeSlaves.end() ) {
      ifVerbose( dbg() << str() << "getSlaves() returning " << (*itr).second.first.first.fullNameChain() << endl );
      ret.push_back( (*itr).second );
    } else {
      kdDebug( 9007 ) << "ERROR in getSlaves()";
    }
  }
#endif
  /*if( it == m_slavesCache.end() || it->second.first < m_activeSlaves.size()
    ) {
    m_slavesCache.insert( std::make_pair( files, std::make_pair( m_activeSlaves.size(), ret ) ) );
  }*/
  return ret;
}

//SimpleTypeNamespace::NamespaceBuildInfo implementation

TypePointer SimpleTypeNamespace::NamespaceBuildInfo::build() {
  if ( m_built )
    return m_built;
  m_built = new SimpleTypeCachedNamespace( m_fakeScope );
  for ( ImportList::iterator it = m_imports.begin(); it != m_imports.end(); ++it )  {
    TypeDesc i = ( *it ).import;
    if ( i.resolved() ) {
      //            i.setResolved( i.resolved()->clone() );
    }

    (( SimpleTypeCachedNamespace* ) m_built.data() ) ->addAliasMap( TypeDesc(), i, ( *it ).files, true, false, ( *it ).perspective );
  }
  return m_built;
}
