// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
/* ######################################################################

   Acquire Progress - Command line progress meter

   ##################################################################### */
									/*}}}*/
// Include files							/*{{{*/
#include<config.h>

#include <apt-pkg/acquire.h>
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/acquire-worker.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/error.h>

#include <apt-private/acqprogress.h>

#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <iostream>
#include <sstream>
#include <unistd.h>

#include <apti18n.h>
									/*}}}*/

// AcqTextStatus::AcqTextStatus - Constructor				/*{{{*/
// ---------------------------------------------------------------------
/* */
AcqTextStatus::AcqTextStatus(std::ostream &out, unsigned int &ScreenWidth,unsigned int const Quiet) :
    pkgAcquireStatus(), out(out), ScreenWidth(ScreenWidth), LastLineLength(0), ID(0), Quiet(Quiet)
{
   // testcases use it to disable pulses without disabling other user messages
   if (Quiet == 0 && _config->FindB("quiet::NoUpdate", false) == true)
      this->Quiet = 1;
}
									/*}}}*/
// AcqTextStatus::Start - Downloading has started			/*{{{*/
// ---------------------------------------------------------------------
/* */
void AcqTextStatus::Start()
{
   pkgAcquireStatus::Start();
   LastLineLength = 0;
   ID = 1;
}
									/*}}}*/
// AcqTextStatus::IMSHit - Called when an item got a HIT response	/*{{{*/
// ---------------------------------------------------------------------
/* */
void AcqTextStatus::IMSHit(pkgAcquire::ItemDesc &Itm)
{
   if (Quiet > 1)
      return;

   clearLastLine();

   out << _("Hit ") << Itm.Description;
   out << std::endl;
   Update = true;
}
									/*}}}*/
// AcqTextStatus::Fetch - An item has started to download		/*{{{*/
// ---------------------------------------------------------------------
/* This prints out the short description and the expected size */
void AcqTextStatus::Fetch(pkgAcquire::ItemDesc &Itm)
{
   Update = true;
   if (Itm.Owner->Complete == true)
      return;

   Itm.Owner->ID = ID++;

   if (Quiet > 1)
      return;

   clearLastLine();

   out << _("Get:") << Itm.Owner->ID << ' ' << Itm.Description;
   if (Itm.Owner->FileSize != 0)
      out << " [" << SizeToStr(Itm.Owner->FileSize) << "B]";
   out << std::endl;
}
									/*}}}*/
// AcqTextStatus::Done - Completed a download				/*{{{*/
// ---------------------------------------------------------------------
/* We don't display anything... */
void AcqTextStatus::Done(pkgAcquire::ItemDesc &/*Itm*/)
{
   Update = true;
}
									/*}}}*/
// AcqTextStatus::Fail - Called when an item fails to download		/*{{{*/
// ---------------------------------------------------------------------
/* We print out the error text  */
void AcqTextStatus::Fail(pkgAcquire::ItemDesc &Itm)
{
   if (Quiet > 1)
      return;

   // Ignore certain kinds of transient failures (bad code)
   if (Itm.Owner->Status == pkgAcquire::Item::StatIdle)
      return;

   clearLastLine();

   if (Itm.Owner->Status == pkgAcquire::Item::StatDone)
   {
      out << _("Ign ") << Itm.Description << std::endl;
      if (Itm.Owner->ErrorText.empty() == false &&
	    _config->FindB("Acquire::Progress::Ignore::ShowErrorText", false) == true)
	 out << "  " << Itm.Owner->ErrorText << std::endl;
   }
   else
   {
      out << _("Err ") << Itm.Description << std::endl;
      out << "  " << Itm.Owner->ErrorText << std::endl;
   }

   Update = true;
}
									/*}}}*/
// AcqTextStatus::Stop - Finished downloading				/*{{{*/
// ---------------------------------------------------------------------
/* This prints out the bytes downloaded and the overall average line
   speed */
void AcqTextStatus::Stop()
{
   pkgAcquireStatus::Stop();
   if (Quiet > 1)
      return;

   clearLastLine();

   if (_config->FindB("quiet::NoStatistic", false) == true)
      return;

   if (FetchedBytes != 0 && _error->PendingError() == false)
      ioprintf(out,_("Fetched %sB in %s (%sB/s)\n"),
	       SizeToStr(FetchedBytes).c_str(),
	       TimeToStr(ElapsedTime).c_str(),
	       SizeToStr(CurrentCPS).c_str());
}
									/*}}}*/
// AcqTextStatus::Pulse - Regular event pulse				/*{{{*/
// ---------------------------------------------------------------------
/* This draws the current progress. Each line has an overall percent
   meter and a per active item status meter along with an overall
   bandwidth and ETA indicator. */
bool AcqTextStatus::Pulse(pkgAcquire *Owner)
{
   pkgAcquireStatus::Pulse(Owner);

   if (Quiet > 0)
      return true;

   enum {Long = 0,Medium,Short} Mode = Medium;

   std::string Line;
   {
      std::stringstream S;
      for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
	    I = Owner->WorkerStep(I))
      {
	 // There is no item running
	 if (I->CurrentItem == 0)
	 {
	    if (I->Status.empty() == false)
	       S << " [" << I->Status << "]";

	    continue;
	 }

	 // Add in the short description
	 S << " [";
	 if (I->CurrentItem->Owner->ID != 0)
	    S << I->CurrentItem->Owner->ID << " ";
	 S << I->CurrentItem->ShortDesc;

	 // Show the short mode string
	 if (I->CurrentItem->Owner->ActiveSubprocess.empty() == false)
	    S << " " << I->CurrentItem->Owner->ActiveSubprocess;

	 // Add the current progress
	 if (Mode == Long)
	    S << " " << I->CurrentSize;
	 else
	 {
	    if (Mode == Medium || I->TotalSize == 0)
	       S << " " << SizeToStr(I->CurrentSize) << "B";
	 }

	 // Add the total size and percent
	 if (I->TotalSize > 0 && I->CurrentItem->Owner->Complete == false)
	 {
	    if (Mode == Short)
	       ioprintf(S, " %.0f%%", (I->CurrentSize*100.0)/I->TotalSize);
	    else
	       ioprintf(S, "/%sB %.0f%%", SizeToStr(I->TotalSize).c_str(),
		     (I->CurrentSize*100.0)/I->TotalSize);
	 }
	 S << "]";
      }

      // Show at least something
      Line = S.str();
      S.clear();
      if (Line.empty() == true)
	 Line = _(" [Working]");
   }
   // Put in the percent done
   {
      std::stringstream S;
      ioprintf(S, "%.0f%%", Percent);
      S << Line;
      Line = S.str();
      S.clear();
   }

   /* Put in the ETA and cps meter, block off signals to prevent strangeness
      during resizing */
   sigset_t Sigs,OldSigs;
   sigemptyset(&Sigs);
   sigaddset(&Sigs,SIGWINCH);
   sigprocmask(SIG_BLOCK,&Sigs,&OldSigs);

   if (CurrentCPS != 0)
   {
      unsigned long long ETA = (TotalBytes - CurrentBytes)/CurrentCPS;
      std::string Tmp = " " + SizeToStr(CurrentCPS) + "B/s " + TimeToStr(ETA);
      size_t alignment = Line.length() + Tmp.length();
      if (alignment < ScreenWidth)
      {
	 alignment = ScreenWidth - alignment;
	 for (size_t i = 0; i < alignment; ++i)
	    Line.append(" ");
	 Line.append(Tmp);
      }
   }
   if (Line.length() > ScreenWidth)
      Line.erase(ScreenWidth);
   sigprocmask(SIG_SETMASK,&OldSigs,0);

   // Draw the current status
   if (_config->FindB("Apt::Color", false) == true)
      out << _config->Find("APT::Color::Yellow");
   if (LastLineLength > Line.length())
      clearLastLine();
   else
      out << '\r';
   out << Line << std::flush;
   if (_config->FindB("Apt::Color", false) == true)
      out << _config->Find("APT::Color::Neutral") << std::flush;

   LastLineLength = Line.length();
   Update = false;

   return true;
}
									/*}}}*/
// AcqTextStatus::MediaChange - Media need to be swapped		/*{{{*/
// ---------------------------------------------------------------------
/* Prompt for a media swap */
bool AcqTextStatus::MediaChange(std::string Media, std::string Drive)
{
   // If we do not output on a terminal and one of the options to avoid user
   // interaction is given, we assume that no user is present who could react
   // on your media change request
   if (isatty(STDOUT_FILENO) != 1 && Quiet >= 2 &&
       (_config->FindB("APT::Get::Assume-Yes",false) == true ||
	_config->FindB("APT::Get::Force-Yes",false) == true ||
	_config->FindB("APT::Get::Trivial-Only",false) == true))

      return false;

   clearLastLine();
   ioprintf(out,_("Media change: please insert the disc labeled\n"
		   " '%s'\n"
		   "in the drive '%s' and press enter\n"),
	    Media.c_str(),Drive.c_str());

   char C = 0;
   bool bStatus = true;
   while (C != '\n' && C != '\r')
   {
      int len = read(STDIN_FILENO,&C,1);
      if(C == 'c' || len <= 0)
	 bStatus = false;
   }

   if(bStatus)
      Update = true;
   return bStatus;
}
									/*}}}*/
void AcqTextStatus::clearLastLine() {					/*{{{*/
   if (Quiet > 0 || LastLineLength == 0)
      return;

   // do not try to clear more than the (now smaller) screen
   if (LastLineLength > ScreenWidth)
      LastLineLength = ScreenWidth;

   out << '\r';
   for (size_t i = 0; i < LastLineLength; ++i)
      out << ' ';
   out << '\r' << std::flush;
}
									/*}}}*/