// packages.cc - class for working with packages, pkg_list.
//    Copyright (C) 2000 Laurynas Biveinis <lauras@softhome.net>
//
//    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
//

#include "packages.h"

#include "kite.h"
#include "scroldlg.h"

#include <dir.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libzippo/archive.h>
#include <libzippo/dsm.h>
#include <libzippo/packdep.h>
#include <libzippo/packlist.h>
#include <libzippo/util.h>

#define Uses_TButton
#define Uses_TDeskTop
#define Uses_TDialog
#define Uses_TProgram
#define Uses_TRect
#define Uses_TStaticText
#include <rhtvision/tv.h>

pkg_list::pkg_list(const char * dj_dir, const char * zip_dir, 
                   const log_file & where_to_log) :
   log(&where_to_log),
   packages(NULL)
{
   // Possible directories to find DJGPP packages. Includes current
   // directory and SimtelNet directory structure.
   char * search_paths[] = {
      "./", "v2/", "v2apps/", "v2gnu/", "v2misc/", "v2tk/", "../v2apps/",
      "../v2gnu/", "../v2misc/", "../v2tk/",
      NULL
   };
   struct ffblk file_info;
   char current_path[FILENAME_MAX + 1];
   char archive_name[FILENAME_MAX + 1];
   PACKAGE_INFO * package = NULL;
   DSM_ERROR * dsm_errors = NULL;
   DSM_FILE_ERROR * dsm_file_errors = NULL;
   wait_box * wait_dialog = new wait_box("Scanning packages...");
   // Add platform DSMs
   char *dsm_path = new char[FILENAME_MAX + 1];
   strcpy(dsm_path, dj_dir);
   strcat(dsm_path, "share/zippo/db");
   packages = dsm_load_dir(dsm_path, packages, &dsm_file_errors);
   char ** path_suffix = &search_paths[0];
   while (*path_suffix)
   {
      strcpy(current_path, zip_dir);
      strcat(current_path, *path_suffix);
      strcpy(dsm_path, current_path);
      packages = dsm_load_dir(dsm_path, packages, &dsm_file_errors);
      strcat(current_path, "*.zip");
      log->print_log("Searching for packages in %s\n", current_path);
      int done = findfirst(current_path, &file_info, FA_RDONLY | FA_ARCH);
      while (!done)
      {
         strcpy(archive_name, zip_dir);
         strcat(archive_name, *path_suffix);
         strcat(archive_name, file_info.ff_name);
         log->print_log("Package found: %s\n", archive_name);
         if (dsm_get_and_parse(archive_name, package, NULL, &dsm_errors))
         {
            log->print_log("Warning! DSM extraction from %s failed.\n",
                           archive_name);
            dsm_free_error_list(dsm_errors);
            package_free(package);
         }
         else
         {
            package->q_forw = packages;
            package->q_back = NULL;
            if (packages)
               packages->q_back = package;
            packages = package;
         }
         done = findnext(&file_info);
      }
      path_suffix++;
   }
   packlist_xref(packages);
   packlist_dedupe(packages);
   delete wait_dialog;
}

pkg_list::~pkg_list(void)
{
   packlist_free(packages);
}

void pkg_list::install(void)
{
   pkg_dialog * dialog = new pkg_dialog(packages);
   TProgram::deskTop->execView(dialog);
   destroy(dialog);
}

pkg_list::pkg_checkbox::pkg_checkbox(PACKAGE_INFO * package) :
   TCheckBoxes(TRect(0, 0, 0, 0), // Bounds will be set later
               new TSItem(package->short_description, NULL)),
   pkg(package)
{
   // Link package information and its checkbox. It is used to update
   // status of other checkboxes according to dependencies of packages.
   pkg->info = this;
}

void pkg_list::pkg_checkbox::press(int item)
{
   TCheckBoxes::press(item);
   // When user checks a checkbox of current package, also mark for 
   // installation all its 'required' dependencies.
   // If user unchecks it, uncheck it only if all deps are still met.
}

// Titles for each package group
static char * titles[] = {"",                      // TYPE_NONE
                          " Binaries ---------- ", // TYPE_BINARIES
                          " Sources ----------- ", // TYPE_SOURCES
                          " Documentation ----- ", // TYPE_DOCUMENTATION
                          "",                      // TYPE_VIRTUAL
                          " Task selections --- "};// TYPE_GROUP

pkg_list::pkg_dialog::pkg_dialog(PACKAGE_INFO * list) :
   scroll_dialog(TProgram::deskTop->getExtent(), "Packages selection"),
   TWindowInit(&TDialog::initFrame),
   packages(list)
{
   insert_scroll_group(new scrollable_group(
                            TRect(2, 2, size.x / 4 * 3, size.y / 4 * 3)));
   // Create a checkbox list of available packages.

   // Group DSM go first
   insert_packages(TYPE_GROUP);
   insert_packages(TYPE_BINARIES);
   insert_packages(TYPE_DOCUMENTATION);
   insert_packages(TYPE_SOURCES);
   insert(new TButton(TRect(2, size.y / 4 * 3 + 2, 12, size.y / 4 * 3 + 4),
                      "~O~K", cmOK, bfDefault));
   selectNext(False);
}

void pkg_list::pkg_dialog::handleEvent(TEvent & event)
{
   scroll_dialog::handleEvent(event);
}

int pkg_list::pkg_dialog::list_browser(PACKAGE_INFO * pkg, void * info)
{
   struct info * inf = (struct info *)info;
   if (pkg->version.type == inf->type) 
   {
      if (!inf->encountered)
      {
         int i;
         switch (inf->type)
         {
            case TYPE_NONE:          i = 0; break;
            case TYPE_BINARIES:      i = 1; break;
            case TYPE_SOURCES:       i = 2; break;
            case TYPE_DOCUMENTATION: i = 3; break;
            case TYPE_VIRTUAL:       i = 4; break;
            case TYPE_GROUP:         i = 5; break;
            default: /* Huh? */      i = 0; break;
         }
         inf->dialog->fill_next_row(new TStaticText(TRect(0, 0, 0, 0), titles[i]));
         inf->encountered = 1;
      }; 
      inf->dialog->fill_next_row(new pkg_list::pkg_checkbox(pkg));
   }
   return 0;
}
