/** -*- Mode: C++; tab-width: 4 -*-
 * vim: sw=4:ts=4:
 *
 * Gnome Apt file retrieval class
 *
 * 	(C) 1998 Havoc Pennington <hp@pobox.com>
 * 	    2002, 2003 Filip Van Raemdonck <mechanix@debian.org>
 *
 * 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.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 * 	$Id$
 *
 **/

using namespace std;

#include <stdio.h>
#include <unistd.h>
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/acquire-worker.h>
#include <apt-pkg/error.h>
#include <apt-pkg/strutl.h>
#include <gtk/gtk.h>

#include "acquirestatus.h"
#include "gnome-apt.h"

gboolean 
GAcqStatus::close_cb(GtkWidget* dialog, gpointer data)
{
  GAcqStatus* as = static_cast<GAcqStatus*>(data);

  as->cancelled_ = true;

  return TRUE; /* block the close - we'll close in the destructor after the next iteration of the acquire */
}

gboolean
GAcqStatus::response_cb (GtkWidget* w, gint response, gpointer data) {
	if (response == GTK_RESPONSE_CANCEL) {
		GAcqStatus* as = static_cast<GAcqStatus*> (data);
		as->cancelled_ = true;
	} else {
		g_warning ("Got response: %d", response);
	}

	return TRUE;
}

GAcqStatus::GAcqStatus() : cancelled_(false) {
	dialog_ = gtk_dialog_new_with_buttons (
		_("Download Progress"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
	gtk_window_set_default_size (GTK_WINDOW (dialog_), 450, 450);
	gtk_dialog_set_default_response (GTK_DIALOG (dialog_), GTK_RESPONSE_CANCEL);

  gnome_apt_setup_dialog(dialog_);  
	g_signal_connect (
		G_OBJECT (dialog_), "close", G_CALLBACK (close_cb), this);
	g_signal_connect (
		G_OBJECT (dialog_), "response", G_CALLBACK (response_cb), this);

  itembar_    = gtk_progress_bar_new();

	/*gtk_progress_bar_set_text (GTK_PROGRESS_BAR (itembar_), _("Active downloads(s) %P%%"));*/

  overallbar_ = gtk_progress_bar_new();

	/*gtk_progress_bar_set_text (GTK_PROGRESS_BAR (overallbar_), _("Overall %P%%"));*/
	gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (overallbar_), 0.1);

  label_ = gtk_label_new("");
  etalabel_ = gtk_label_new("");

  GtkWidget* frame = gtk_frame_new(_("Error Log"));

	errors_ = gtk_text_view_new ();
	gtk_widget_set_size_request (errors_, -1, 100);
	gtk_container_add (GTK_CONTAINER (frame), errors_);

	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog_)->vbox), label_,
	      TRUE, TRUE, GAPT_PAD);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog_)->vbox), etalabel_,
	      TRUE, TRUE, GAPT_PAD);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog_)->vbox), frame,
	      FALSE, FALSE, GAPT_PAD);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog_)->vbox), itembar_,
	      FALSE, FALSE, GAPT_PAD);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog_)->vbox), overallbar_,
	      FALSE, FALSE, GAPT_PAD);

   // flush
  while (gtk_events_pending())
    gtk_main_iteration();
}

void
GAcqStatus::Start() 
{
   pkgAcquireStatus::Start(); 
   ID = 1;
   cancelled_ = false;

	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (itembar_), 0.0);
	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (overallbar_), 0.0);

	gtk_text_buffer_set_text (
		GTK_TEXT_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (errors_))),
		"", -1);

   gtk_widget_show_all(dialog_);
};

bool 
GAcqStatus::MediaChange(string Media,string Drive)
{
	GtkWidget* subdialog = gtk_message_dialog_new (
		NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
		_("Please insert:\n'%s'\nin drive %s"), Media.c_str(), Drive.c_str());
	gtk_window_set_transient_for (GTK_WINDOW (subdialog), GTK_WINDOW (dialog_));
	gtk_dialog_run (GTK_DIALOG (subdialog));
	gtk_widget_destroy (subdialog);

  Update = true;
  return true;
}

// This means the file hasn't changed, If Modified Since.
void
GAcqStatus::IMSHit(pkgAcquire::ItemDesc &Itm)
{
  gchar buf[256];

  cerr << "Hit " << Itm.Description << endl;

  int n = g_snprintf(buf, 255, _("Already have %s "), Itm.Description.c_str());
	if (n > 0) {
		if (Itm.Owner->FileSize) {
			g_snprintf (&buf[n], 255 - n, "[%s]", SizeToStr (Itm.Owner->FileSize).c_str());
		}
	}
	gtk_label_set_text (GTK_LABEL (label_), buf);

  while (gtk_events_pending())
    gtk_main_iteration();

  // ??
  Update = true;
};

void
GAcqStatus::Fetch(pkgAcquire::ItemDesc &Itm)
{
  Update = true;
  
  cerr << "Fetch " << Itm.Description << endl;

  if (Itm.Owner->Complete == true)
    return;
  
  Itm.Owner->ID = ID++;
  
  gchar buf[256];

#if 0
  int n = g_snprintf(buf, 255, "%s    %%P%%", Itm.Description.c_str());
  if (n > 0) {
    if (Itm.Owner->FileSize != 0)
      g_snprintf(&buf[n], 255-n, "/%s", SizeToStr(Itm.Owner->FileSize).c_str());
  }

   gtk_progress_set_format_string(GTK_PROGRESS(itembar_), buf);
   gtk_progress_set_show_text(GTK_PROGRESS(itembar_), TRUE);
#endif

   g_snprintf(buf, 255, _("Get: %ld %s [%s]"),
              Itm.Owner->ID, Itm.Description.c_str(),
              SizeToStr(Itm.Owner->FileSize).c_str());
	gtk_label_set_text (GTK_LABEL (label_), buf);

   // flush
   while (gtk_events_pending())
     gtk_main_iteration();
};

void
GAcqStatus::Done(pkgAcquire::ItemDesc &Itm)
{
  cerr << "Done " << Itm.Description << endl;

  gchar buf[256];
  g_snprintf(buf, 255, _("Finished %s"), 
             Itm.Description.c_str());
	gtk_label_set_text (GTK_LABEL (label_), buf);

   // flush
  while (gtk_events_pending())
    gtk_main_iteration();

  Update = true;
};


void
GAcqStatus::Fail(pkgAcquire::ItemDesc &Itm)
{   
  gchar buf[256];
	GtkTextBuffer* buffer;
	GtkTextIter pend;

	gchar* errmsg = g_strdup_printf (_("Failed to fetch %s\n  %s\n"),
	      Itm.Owner->DescURI().c_str(), Itm.Owner->ErrorText.c_str());
	cerr << errmsg;
	_error->Warning (errmsg);

  g_snprintf(buf, 255, _("%s\n%s"), 
             Itm.Description.c_str(), 
             Itm.Owner->ErrorText.c_str());
	gtk_label_set_text (GTK_LABEL (label_), buf);

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (errors_));
	gtk_text_buffer_get_end_iter (buffer, &pend);
	gtk_text_buffer_insert (buffer, &pend, buf, -1);

#if 0
  gtk_progress_set_show_text(GTK_PROGRESS(itembar_), FALSE);
  gtk_progress_set_percentage(GTK_PROGRESS(itembar_), 0.0);
#endif

   // flush
  while (gtk_events_pending())
    gtk_main_iteration();

  Update = true;
};

void
GAcqStatus::Stop()
{
  pkgAcquireStatus::Stop();

  cerr << "Stop" << endl;

  gchar buf[256];

  if (FetchedBytes != 0)  
    g_snprintf(buf, 255, _("Fetched %s in %s  (%s/s)"),
               SizeToStr(FetchedBytes).c_str(),
               TimeToStr(ElapsedTime).c_str(), 
               SizeToStr(CurrentCPS).c_str());
  else
    g_snprintf(buf, 255, _("Fetched no data."));
	gtk_label_set_text (GTK_LABEL (label_), buf);

  // flush
  while (gtk_events_pending())
    gtk_main_iteration();

  // The beep is supposed to notify that the download finished,
  //  so if it finished via cancel we don't want it.
  if (!cancelled_)
    gdk_beep();
}

bool
GAcqStatus::Pulse(pkgAcquire *Owner)
{   
   bool Ret = pkgAcquireStatus::Pulse(Owner);

   // This should never happen, but if it could we'd do this.
   if (Ret == false)
     return false;

   bool Shown = false;

   vector<string> workers;
   
   gulong workers_current = 0;
   gulong workers_total   = 0;
   
   pkgAcquire::Worker *i = Owner->WorkersBegin(); 
   while (i != 0) {
     // There is no item running 
     if (i->CurrentItem == 0)
       {
         if ( ! i->Status.empty())
           {
             workers.push_back(i->Status);
             Shown = true;
           }
         i = Owner->WorkerStep(i);
         continue;
       }

     Shown = true;
      
     string s;
      
     gchar buf[512];

		// Add in the short description
		if (i->CurrentItem->Owner->ID != 0) {
			g_snprintf (buf, 511, _("Item %ld, %s"),
				i->CurrentItem->Owner->ID,
				i->CurrentItem->ShortDesc.c_str());
		} else {
			g_snprintf (buf, 511, "%s", i->CurrentItem->ShortDesc.c_str());
		}

     s += buf;

     // Show the short mode string (What is this?)
     if (i->CurrentItem->Owner->Mode != 0)
       {
         g_snprintf(buf,511," %s",i->CurrentItem->Owner->Mode);
         s += buf;
       }
            
     // Add the current progress
     g_snprintf(buf,511," %s",SizeToStr(i->CurrentSize).c_str());

     s += buf;
      
      // Add the total size and percent
     if (i->TotalSize > 0 && i->CurrentItem->Owner->Complete == false)
       {
         g_snprintf(buf,511,
                    "/%s %lu%%",
                    SizeToStr(i->TotalSize).c_str(),
                    long(double(i->CurrentSize*100.0)/double(i->TotalSize)));
         s += buf;
       }      

     workers_current += i->CurrentSize;
     workers_total   += i->TotalSize;

     workers.push_back(s);
      
     i = Owner->WorkerStep(i);
   }

   if (!Shown) {
		gtk_progress_bar_pulse (GTK_PROGRESS_BAR (overallbar_));

		gtk_label_set_text (GTK_LABEL (label_), _("No active downloads"));
	} else {
		string list_of_workers = _("Active download(s):\n");
     vector<string>::iterator q = workers.begin();
     while (q != workers.end()) {
       list_of_workers += *q;
       list_of_workers += "\n";
       ++q;
     }
		gtk_label_set_text (GTK_LABEL (label_), list_of_workers.c_str());

     float workerpercent = 0.0;
     if (workers_total != 0) 
       workerpercent = float(workers_current)/float(workers_total);
     else 
       workerpercent = 0.0;

     // Put in the percent done, clamping to valid values.
		gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (itembar_),
		      CLAMP (workerpercent, 0.0, 1.0));
		gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (overallbar_), 
		      CLAMP (float (CurrentBytes + CurrentItems) / float (TotalBytes + TotalItems), 0.0, 1.0));
   }
      
   // Put in the ETA and cps meter
   if (CurrentCPS != 0)
     {      
       char Tmp[300];
       unsigned long ETA = (unsigned long)((TotalBytes - CurrentBytes)/CurrentCPS);
       g_snprintf(Tmp,299,_("%s remaining at %s/s"),
                  TimeToStr(ETA).c_str(), 
                  SizeToStr(CurrentCPS).c_str());
		gtk_label_set_text (GTK_LABEL (etalabel_), Tmp);
     }
   else 
		gtk_label_set_text (GTK_LABEL (etalabel_), _("Stalled"));
 
   // flush
   while (gtk_events_pending())
     gtk_main_iteration();
   
   Update = false;

   return !cancelled_;
}
