package com.dbxml.db.client.tools.command;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * 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: ContentImport.java,v 1.15 2004/08/22 04:18:41 bradford Exp $
 */

import java.io.*;

import com.dbxml.db.client.CollectionClient;
import com.dbxml.db.client.tools.CommandLine;
import com.dbxml.util.Stopwatch;
import com.dbxml.util.dbXMLException;
import java.util.ArrayList;
import java.util.List;

/**
 * ContentImport (IMPORT) imports content into the current context
 * Collection.
 */

public final class ContentImport extends CommandBase {
   private static final int SAMPLE_SET = 100;
   private static final DirectoryFilter dirFilter = new DirectoryFilter();
   private static final String EOL_STRING;

   static {
      StringBuffer sb = new StringBuffer(20);
      for ( int i = 0; i < 10; i++ )
         sb.append(' ');
      for ( int i = 0; i < 10; i++ )
         sb.append('\u0008');
      EOL_STRING = sb.toString();
   }

   public void process() throws dbXMLException {
      CollectionClient col = (CollectionClient)cl.getProperty(CommandLine.COLLECTION);
      if ( col == null )
         throw new dbXMLException("Collection context required");

      boolean binary = col.getCollectionType() == CollectionClient.TYPE_VALUES;
      String type;
      if ( binary )
         type = "Record";
      else
         type = "Document";

      String filename = cl.getNextToken("Filename");
      File file = new File(filename);
      String docName = file.getName();
      boolean asUsed = false;
      boolean recursive = false;
      while ( cl.hasMoreTokens() ) {
         String adj = cl.getNextToken("As");
         if ( adj.equalsIgnoreCase("AS") ) {
            asUsed = true;
            if ( cl.hasMoreTokens() )
               docName = cl.getNextToken(type+" Name");
            else
               throw new dbXMLException(type + " name required");
         }
         else if ( adj.equalsIgnoreCase("RECURSIVE") )
            recursive = true;
      }

      File dir = file.getParentFile();
      if ( dir == null )
         dir = new File("./");

      String fName = file.getName();
      if ( fName.indexOf('?') != -1 || fName.indexOf('*') != -1 ) {
         // Wildcard specified
         if ( asUsed )
            throw new dbXMLException("Can't assign a " + type + " name to a wildcard path");
         List files = new ArrayList();
         PrintWriter pw = cl.getWriter();

         long size = addFiles(files, dir, fName, recursive);
         int fileCount = files.size();

         if ( fileCount == 0 ) {
            pw.println("No " + type + "s to import");
            return;
         }

         pw.println("Importing " + fileCount + " " + type + "s");

         boolean estimate = fileCount > SAMPLE_SET * 10;
         pw.println("Average " + type + " size is " + (size / fileCount) + " bytes");
         Stopwatch sw = new Stopwatch("Estimated remaining load time");
         for ( int i = 0; i < fileCount; i++ ) {
            if ( estimate && i == 0 )
               sw.start();
            File f = (File)files.get(i);
            if ( binary )
               importBin(col, f, f.getName(), estimate);
            else
               importDoc(col, f, f.getName(), estimate);

            if ( estimate && i > 0 && i % SAMPLE_SET == 0 ) {
               long t = ((fileCount - i) * sw.elapsed()) / i;
               pw.print("\r" + sw.toString(t) + EOL_STRING);
               pw.flush();
            }
         }
         if ( estimate )
            pw.println();
      }
      else {
         if ( binary )
            importBin(col, file, docName, false);
         else
            importDoc(col, file, docName, false);
      }
   }

   private void importBin(CollectionClient col, File file, String docName, boolean estimates) throws dbXMLException {
      try {
         FileInputStream fis = new FileInputStream(file);
         byte[] b = new byte[(int)file.length()];
         fis.read(b);
         fis.close();
         col.setValue(docName, b);

         if ( !estimates ) {
            PrintWriter pw = cl.getWriter();
            pw.println("Imported '" + docName + "'");
         }
      }
      catch ( Exception e ) {
         PrintWriter pw = cl.getWriter();
         pw.println();
         pw.println("Couldn't import '" + docName + "': "+e.getMessage());
      }
   }

   private void importDoc(CollectionClient col, File file, String docName, boolean estimates) throws dbXMLException {
      try {
         FileInputStream fis = new FileInputStream(file);
         BufferedInputStream bis = new BufferedInputStream(fis, 4096);
         InputStreamReader isr = new InputStreamReader(bis, "UTF8");
         char[] c = new char[(int)file.length()];
         int size = isr.read(c);
         isr.close();
         col.setDocumentAsText(docName, new String(c, 0, size));

         if ( !estimates ) {
            PrintWriter pw = cl.getWriter();
            pw.println("Imported '" + docName + "'");
         }
      }
      catch ( Exception e ) {
         PrintWriter pw = cl.getWriter();
         pw.println();
         pw.println("Couldn't import '" + docName + "': "+e.getMessage());
      }
   }

   private long addFiles(List list, File dir, String wildcard, boolean recursive) throws dbXMLException {
      if ( !dir.exists() )
         throw new dbXMLException("Directory '"+dir.getPath()+"' does not exist");

      long totalSize = 0;
      File[] fl = dir.listFiles(new WildcardFilter(wildcard));
      for ( int i = 0; i < fl.length; i++ ) {
         list.add(fl[i]);
         totalSize += fl[i].length();
      }
      if ( recursive ) {
         fl = dir.listFiles(dirFilter);
         for ( int i = 0; i < fl.length; i++ )
            totalSize += addFiles(list, fl[i], wildcard, recursive);
      }
      return totalSize;
   }


   /**
    * DirectoryFilter
    */

   private static class DirectoryFilter implements FileFilter {
      public boolean accept(File file) {
         return file.isDirectory();
      }
   }


   /**
    * WildcardFilter
    */

   private class WildcardFilter implements FileFilter {
      public WildcardMatcher matcher;

      public WildcardFilter(String wildcard) throws dbXMLException {
         matcher = new WildcardMatcher(wildcard);
      }

      public boolean accept(File file) {
         return file.isFile() && matcher.matches(file.getName());
      }
   }
}



