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: ContentExport.java,v 1.13 2004/08/22 04:18:41 bradford Exp $
 */

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.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.io.BufferedOutputStream;

/**
 * ContentExport (EXPORT) exports content from the current context
 * Collection.
 */

public final class ContentExport extends CommandBase {
   private static final int SAMPLE_SET = 100;
   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(type+" Name");
      File file = new File(filename);
      String docName = file.getName();
      boolean asUsed = false;
      while ( cl.hasMoreTokens() ) {
         String adj = cl.getNextToken("As");
         if ( adj.equalsIgnoreCase("AS") ) {
            asUsed = true;
            if ( cl.hasMoreTokens() )
               docName = cl.getNextToken("Filename");
            else
               throw new dbXMLException(type + " name required");
         }
      }

      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();

         addFiles(files, col, fName);
         int fileCount = files.size();

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

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

         boolean estimate = fileCount > SAMPLE_SET * 10;
         Stopwatch sw = new Stopwatch("Estimated remaining export time");
         for ( int i = 0; i < fileCount; i++ ) {
            if ( estimate && i == 0 )
               sw.start();
            String fn = (String)files.get(i);
            File f = new File(dir, fn);

            if ( binary )
               exportBin(col, f, fn, estimate);
            else
               exportDoc(col, f, fn, 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 )
            exportBin(col, file, docName, false);
         else
            exportDoc(col, file, docName, false);
      }
   }

   private void exportBin(CollectionClient col, File file, String docName, boolean estimates) throws dbXMLException {
      try {
         FileOutputStream fos = new FileOutputStream(file);
         byte[] b = col.getValue(docName);
         fos.write(b);
         fos.close();

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

   private void exportDoc(CollectionClient col, File file, String docName, boolean estimates) throws dbXMLException {
      try {
         FileOutputStream fos = new FileOutputStream(file);
         BufferedOutputStream bos = new BufferedOutputStream(fos, 4096);
         OutputStreamWriter osw = new OutputStreamWriter(bos, "UTF8");
         String doc = col.getDocumentAsText(docName);
         osw.write(doc);
         osw.flush();
         osw.close();

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

   private void addFiles(List list, CollectionClient col, String wildcard) throws dbXMLException {
      WildcardMatcher matcher = new WildcardMatcher(wildcard);

      String[] dl = col.listKeys();
      for ( int i = 0; i < dl.length; i++ ) {
         if ( matcher.matches(dl[i]) )
            list.add(dl[i]);
      }
   }
}
