#include <iostream>
#include <sstream>
#include "zypp/base/Logger.h"
#include "zypp/repo/ServiceRepos.h"
#include "zypp/repo/RepoException.h"
#include "zypp/media/MediaException.h"
#include "zypp/parser/RepoFileReader.h"
#include "zypp/media/MediaManager.h"
#include "zypp/parser/RepoindexFileReader.h"
#include "zypp/ExternalProgram.h"

using std::stringstream;
using std::endl;

///////////////////////////////////////////////////////////////////
namespace zypp
{
  ///////////////////////////////////////////////////////////////////
  namespace repo
  {
    struct ServiceRepos::Impl
    { virtual ~Impl() {} };

    ///////////////////////////////////////////////////////////////////

    struct RIMServiceRepos : public ServiceRepos::Impl
    {
      RIMServiceRepos( const Pathname & /*root_r*/,
		       const ServiceInfo & service,
		       const ServiceRepos::ProcessRepo & callback,
		       const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() )
      {
	// repoindex.xml must be fetched always without using cookies (bnc #573897)
	Url serviceUrl( service.url() );
	serviceUrl.setQueryParam( "cookies", "0" );

	// download the repo index file
	media::MediaManager mediamanager;
	media::MediaAccessId mid = mediamanager.open( serviceUrl );
	mediamanager.attach( mid );
	mediamanager.provideFile( mid, "repo/repoindex.xml" );
	Pathname path = mediamanager.localPath(mid, "repo/repoindex.xml" );
	parser::RepoindexFileReader reader(path, callback);
	service.setProbedTtl( reader.ttl() );	// hack! Modifying the const Service to set parsed TTL
	mediamanager.release( mid );
	mediamanager.close( mid );
      }
    };

    ///////////////////////////////////////////////////////////////////

    struct PluginServiceRepos : public ServiceRepos::Impl
    {
      PluginServiceRepos( const Pathname & root_r,
			  const ServiceInfo & service,
			  const ServiceRepos::ProcessRepo & callback,
			  const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() )
      {
	// bsc#1080693: Service script needs to be executed chrooted to the RepoManagers rootDir.
	// The service is not aware of the rootDir, so it's explicitly passed and needs to be
	// stripped from the URLs path.
	stringstream buffer;

	ExternalProgram::Arguments args;
	args.reserve( 3 );
	args.push_back( "/bin/sh" );
	args.push_back( "-c" );
	args.push_back( Pathname::stripprefix( root_r, service.url().getPathName() ).asString() );
	ExternalProgramWithStderr prog( args, root_r );
	prog >> buffer;

	if ( prog.close() != 0 )
	{
	  // ServicePluginInformalException:
	  // Ignore this error but we'd like to report it somehow...
	  std::string errbuffer;
	  prog.stderrGetUpTo( errbuffer, '\0' );
	  ERR << "Capture plugin error:[" << endl << errbuffer << endl << ']' << endl;
	  ZYPP_THROW( repo::ServicePluginInformalException( service, errbuffer ) );
	}
	parser::RepoFileReader parser( buffer, callback );
      }
    };

    ///////////////////////////////////////////////////////////////////

    ServiceRepos::ServiceRepos( const Pathname & root_r,
				const ServiceInfo & service,
				const ServiceRepos::ProcessRepo & callback,
				const ProgressData::ReceiverFnc &progress )
    : _impl( ( service.type() == ServiceType::PLUGIN )
    ? static_cast<ServiceRepos::Impl*>( new PluginServiceRepos( root_r, service, callback, progress ) )
    : static_cast<ServiceRepos::Impl*>( new RIMServiceRepos( root_r, service, callback, progress ) ) )
    {}

    ServiceRepos::~ServiceRepos()
    {}

  } // namespace repo
  ///////////////////////////////////////////////////////////////////
} //namespace zypp
///////////////////////////////////////////////////////////////////
