// Filename: fltCopy.cxx
// Created by:  drose (01Nov00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "fltCopy.h"

#include "cvsSourceDirectory.h"
#include "fltHeader.h"
#include "fltFace.h"
#include "fltExternalReference.h"
#include "fltError.h"
#include "dcast.h"

////////////////////////////////////////////////////////////////////
//     Function: FltCopy::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
FltCopy::
FltCopy() {
  set_program_description
    ("fltcopy copies one or more MultiGen .flt files into a "
     "CVS source hierarchy.  "
     "Rather than copying the named files immediately into the current "
     "directory, it first scans the entire source hierarchy, identifying all "
     "the already-existing files.  If the named file to copy matches the "
     "name of an already-existing file in the current directory or elsewhere "
     "in the hierarchy, that file is overwritten.  Other .flt files, as "
     "well as texture files, that are externally referenced by the "
     "named .flt file(s) are similarly copied.");

  clear_runlines();
  add_runline("[opts] file.flt [file.flt ... ]");

  add_path_replace_options();
}

////////////////////////////////////////////////////////////////////
//     Function: FltCopy::run
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
void FltCopy::
run() {
  SourceFiles::iterator fi;
  for (fi = _source_files.begin(); fi != _source_files.end(); ++fi) {
    ExtraData ed;
    ed._type = FT_flt;

    CVSSourceTree::FilePath dest = import(*fi, &ed, _model_dir);
    if (!dest.is_valid()) {
      exit(1);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FltCopy::copy_file
//       Access: Protected, Virtual
//  Description: Called by import() if verify_file() indicates that a
//               file needs to be copied.  This does the actual copy
//               of a file from source to destination.  If new_file is
//               true, then dest does not already exist.
////////////////////////////////////////////////////////////////////
bool FltCopy::
copy_file(const Filename &source, const Filename &dest,
          CVSSourceDirectory *dir, void *extra_data, bool new_file) {
  ExtraData *ed = (ExtraData *)extra_data;
  switch (ed->_type) {
  case FT_flt:
    return copy_flt_file(source, dest, dir);

  case FT_texture:
    return copy_texture(source, dest, dir, ed->_texture, new_file);
  }

  nout << "Internal error: invalid type " << (int)ed->_type << "\n";
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: FltCopy::copy_flt_file
//       Access: Private
//  Description:
////////////////////////////////////////////////////////////////////
bool FltCopy::
copy_flt_file(const Filename &source, const Filename &dest,
              CVSSourceDirectory *dir) {
  PT(FltHeader) header = new FltHeader(_path_replace);

  // We don't want to automatically generate .attr files--we'd rather
  // write them out explicitly.
  header->set_auto_attr_update(FltHeader::AU_none);

  FltError result = header->read_flt(source);
  if (result != FE_ok) {
    nout << "Cannot read " << source << ": " << result << "\n";
    return false;
  }

  header->check_version();

  // Now scan the flt file for nested references.
  Refs refs;
  Textures textures;
  scan_flt(header, refs, textures);

  Refs::const_iterator ri;
  for (ri = refs.begin(); ri != refs.end(); ++ri) {
    FltExternalReference *ref = (*ri);
    Filename ref_filename = ref->get_ref_filename();

    if (!ref_filename.exists()) {
      nout << "*** Warning: external reference " << ref_filename
           << " does not exist.\n";
    } else {
      ExtraData ed;
      ed._type = FT_flt;

      CVSSourceTree::FilePath ref_path =
        import(ref_filename, &ed, _model_dir);
      if (!ref_path.is_valid()) {
        return false;
      }

      // Update the reference to point to the new flt filename, relative
      // to the base flt file.
      ref->set_ref_filename(ref_path.get_rel_from(dir));
    }
  }

  // Remove all the textures from the palette, and then add back only
  // those we found in use.  This way we don't copy a file that
  // references bogus textures.
  header->clear_textures();

  Textures::const_iterator ti;
  for (ti = textures.begin(); ti != textures.end(); ++ti) {
    FltTexture *tex = (*ti);
    Filename texture_filename = tex->get_texture_filename();

    if (!texture_filename.exists()) {
      nout << "*** Warning: texture " << texture_filename
           << " does not exist.\n";
    } else {
      ExtraData ed;
      ed._type = FT_texture;
      ed._texture = tex;

      CVSSourceTree::FilePath texture_path =
        import(texture_filename, &ed, _map_dir);
      if (!texture_path.is_valid()) {
        return false;
      }

      // Update the texture reference to point to the new texture
      // filename, relative to the flt file.
      tex->set_texture_filename(texture_path.get_rel_from(dir));
      header->add_texture(tex);
    }
  }

  // Finally, write the resulting file out.
  result = header->write_flt(dest);
  if (result != FE_ok) {
    nout << "Cannot write " << dest << "\n";
    return false;
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: FltCopy::copy_texture
//       Access: Private
//  Description:
////////////////////////////////////////////////////////////////////
bool FltCopy::
copy_texture(const Filename &source, const Filename &dest,
             CVSSourceDirectory *dir, FltTexture *tex, bool new_file) {
  if (!copy_binary_file(source, dest)) {
    return false;
  }

  // Also write out the .attr file.
  Filename attr_filename = dest.get_fullpath() + ".attr";
  if (!attr_filename.exists()) {
    new_file = true;
  }

  tex->write_attr_data(attr_filename);

  if (new_file) {
    cvs_add(attr_filename);
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: FltCopy::scan_flt
//       Access: Private
//  Description: Recursively walks through the flt file hierarchy,
//               looking for texture references and external flt file
//               references.
////////////////////////////////////////////////////////////////////
void FltCopy::
scan_flt(FltRecord *record, FltCopy::Refs &refs, FltCopy::Textures &textures) {
  if (record->is_of_type(FltFace::get_class_type())) {
    FltFace *face;
    DCAST_INTO_V(face, record);
    if (face->has_texture()) {
      textures.insert(face->get_texture());
    }

  } else if (record->is_of_type(FltExternalReference::get_class_type())) {
    FltExternalReference *ref;
    DCAST_INTO_V(ref, record);

    refs.insert(ref);
  }

  int i;
  int num_subfaces = record->get_num_subfaces();
  for (i = 0; i < num_subfaces; i++) {
    scan_flt(record->get_subface(i), refs, textures);
  }

  int num_children = record->get_num_children();
  for (i = 0; i < num_children; i++) {
    scan_flt(record->get_child(i), refs, textures);
  }
}


int main(int argc, char *argv[]) {
  FltCopy prog;
  prog.parse_command_line(argc, argv);
  prog.run();
  return 0;
}
