/*
 * Copyright 
 *   2008 axYus - www.axyus.com
 *   2008 L. Meckert - laurent.meckert@axyus.com
 * This file is part of XEMELIOS.
 * 
 * XEMELIOS 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.
 * 
 * XEMELIOS 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 XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.export;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;

import fr.gouv.finances.dgfip.utils.Amount;

import fr.gouv.finances.dgfip.xemelios.common.config.ElementModel;



import java.io.IOException;
import java.io.FileWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import org.apache.log4j.Logger;

import fr.gouv.finances.cp.utils.StringUtilities;

import fr.gouv.finances.dgfip.xemelios.utils.CSVWriter;

// ABANDONNE POUR LE MOMENT
import fr.gouv.finances.dgfip.xemelios.export.ExportElementModel;
// AU BESOIN REPRENDRE D'APRES HtmlTableExporter.java 


public class CsvExporter extends ExportUndertaker {
   private static final Logger logger = Logger.getLogger(CsvExporter.class);
   private static final int MAX_ROWS_PER_SHEET =  65000 ; // test   30 
 
   // contiendra la liste des sheets XLS, dans l'ordre devant être créé (par ex : SHEET_Bordereau, SHEET_Titre, SHEET_Ligne)
   private Hashtable<String, Vector<CsvSheet>> sheets = new Hashtable<String, Vector<CsvSheet>>();
	
   private Exporter caller;
   // private HtmlSheet mainSheet;
   private String workbookPrefix = null;
   private SimpleDateFormat df = new SimpleDateFormat(System.getProperty("xemelios.date.format", "dd/MM/yyyy"));


   public    CsvExporter(Exporter caller) {
      this.caller = caller;
   }
	
   // Fonctions appellees par Exporter.java

   /**
    * Initialisation
    */
   void initialize()throws Exception {
      /**
       * 
       *  Version rudimentaire pour demarrer
       *  A partir du nom de fichier demande, deduit le prefixe 
       *  utilise par tous les fchiers csv. 
       */

      // Pour que l'appelant soit content je lui genene  un fichier excel vide
      //
      FileWriter xl = new FileWriter(caller.fDest);
      xl.close(); 
             
      workbookPrefix = StringUtilities.removeFileNameSuffix(caller.fDest.getPath());

   }
   void recopieFichier(String src, String dst, boolean overwrite) {
      FileChannel in = null; // canal d'entrÃ©e
      FileChannel out = null; // canal de sortie

      try {
         // Initimport java.nio.channels.FileChannel;
         in = new FileInputStream(src).getChannel();
         out = new FileOutputStream(dst).getChannel();

         // Copie depuis le in vers le out
         in.transferTo(0, in.size(), out);
      } catch (Exception e) {
         e.printStackTrace(); // n'importe quelle exception
      } finally { // finalement on ferme
         if (in != null) {
            try {
               in.close();
            } catch (IOException e) {
            }
         }
         if (out != null) {
            try {
               out.close();
            } catch (IOException e) {
            }
         }
      }
   }

   
   /**
    * Crée un <tt>CsvSheet</tt> et ajoute la liste des entetes de colonne
    * @param eem
    * @throws Exception
    */
   void buildSheet (ExportElementModel eem) throws Exception {
      Vector<CsvSheet> v = sheets.get(eem.getId());
      if (v == null) v = new Vector<CsvSheet>();
      sheets.put(eem.getId(), v);
   
      String fileName = workbookPrefix + "_" + eem.getLibelle() ; // + ".csv";

      CsvSheet  sheet= new CsvSheet();
      sheet.open(fileName);

      v.add (sheet);
			
      for (ExportElementModel e:eem.getEnfants()) {
         buildSheet (e);
      }
      caller.firstSheetId = eem.getId(); 
   }
   /**
    * Apporte des finitions 
    */
   void finish() throws Exception{
      // pour l'instant rien
   }

   /**
    * Fermeture et nettoyage
    */
   void close(){
      Enumeration elems = sheets.keys();
      while(elems.hasMoreElements() ){
         String k = (String)elems .nextElement() ;
         Vector<CsvSheet> v = sheets.get(k);
         for (CsvSheet s:v)
            {

               s.close();
            }

      }
      //  mainSheet.close();
   }
    
   /**
    * Verifie l'existence d'unr feuille dans le classeur
    * @param sheetId le nom de la feuille
    */
   boolean sheetExists(String sheetId) {
      Vector<CsvSheet> v = sheets.get (sheetId);
      if(v != null) 
         return true;
      else
         return false;
   }

	




   /**
    * renvoie le dernier numero de ligne utilisee dans la derniere feuille de nom sheetId
    */
   int getLastLineLastSheet (String sheetId) {
      // suppose qu'on a appele sheetExists(sheetId) 
      CsvSheet ws = getLastSheet(sheetId);
      return   (ws.getRows()<2 ? 2:ws.getRows());
   }


   /**
    * Remplit une cellule avec la valeur correctement formatÃ©e.
    * @param sheet
    * @param o
    * @param ligne
    * @param col
    * @throws WriteException
    */
   void writeCell (String sheetId, Object o, int line, int col, boolean bypassLineRenum) throws Exception {
   

    
      int newline = 0;
      if (bypassLineRenum) {
         newline = line;
      } else {
         int _ligne = caller.getLastLine(sheetId);
         if (_ligne >= (MAX_ROWS_PER_SHEET*sheets.get(sheetId).size())) {
            splitSheet (sheetId);
         }
            
         // renumÃ©rotation de la ligne
         newline = _ligne;
         newline=_ligne%(MAX_ROWS_PER_SHEET)+2;
      }
     
      CsvSheet sheet = getLastSheet(sheetId);
      String s = formatted(o, line);
      // Si line = 0  = c'est un titre
      if (line == 0 ) {
         sheet.addTitle(s, col);
      }else   if (line == 1 ) {
         // Si line = 1  = c'est un  nom de champ
         sheet.addName(s, col);
      } else {
         // sinon c'est une ligne normale
         sheet.addEntry(s, col);
      }
        
      
   }

   /**
    * reunit des cellules dans la derniere des feuilles de nom sheetId
    * @param sheetId = nom de la feuille 
    * @param  col1 = colonne de depart
    * @param row1 = ligne de depart
    * @param col2 = colonne d'arrivee
    * @param row2= ligne d'arrivee
  
   */
   void mergeCells( String sheetId, int col1, int row1, int col2, int row2) 
   {
      /* forget it
         CsvSheet _ws = getLastSheet(sheetId);

         try {
         _ws.mergeCells(col1, row1,  col2, row2);
         } catch(Exception e) {}
      */
        
   }


   void flushLine(String sheetId){
      CsvSheet _ws = getLastSheet(sheetId);
      _ws.flushLine();
   }
 

   // Fonctions a usage interne mises en protected on sait jamais


   /**
    * Formatage de l'objet a ecrire
    */
   protected String formatted( Object o, Integer line) {
      String rawStr;
      if (o instanceof java.lang.Integer) {
         rawStr = ((Integer) o).toString();
      } else if (o instanceof java.lang.Float) {
         rawStr = ((Float) o).toString();
      } else if (o instanceof java.util.Date) {
         rawStr = df.format(o);
      } else if (o instanceof Amount) {
         Double d = new Double(((Amount) o).doubleValue());
         rawStr = d.toString();
      } else if (o instanceof java.lang.String) {
         rawStr = (String) o;
      } else {
         rawStr = (String) o;
      }
      return rawStr;	
   }


   /**
    * Numero d'ordre suivant celui de la feuille de nom id
    */
   protected int afterSheet (String id) {
      /*	String[]names = workbook.getSheetNames();
                for (int i=1; i<names.length; i++) {
                if (names[i].equals(id)) return i+1;
                }
                return 1;*/
      return 1;
   }

   /**
    * Récupère la derniere feuille correspondant a
    * l'objet de nom id (c'est celle en cours d'edition)
    */
   protected CsvSheet getLastSheet (String sheetId) {
      Vector<CsvSheet> v = sheets.get (sheetId);
      if(v != null) 
         return v.get (v.size()-1);
      else
         return null;
   }
	
   /**
    * Récupère la premiere feuille correspondant au premier parent de
    * l'objet de nom id
    */
   protected CsvSheet getRootSheetBefore (String id) {
      CsvSheet ret = null;
      ElementModel e = caller.getEMFromId(id);
      if (e.getSimpleParent()!=null) {
         String pName = e.getSimpleParent().getElement();
         ret = sheets.get(pName).get(0);
      }
      return ret;
   }
	

   /**
    * creee une nouvelle feuille de nom id suivi d'un _  er d'un numero d'ordre
    * appelle lorsque la feuille 
    * precedente de meme nom "deborde"
    */
   protected CsvSheet splitSheet (String id) {
      Vector<CsvSheet> v = sheets.get(id);
      CsvSheet wsRef = v.get(0);
      CsvSheet otherRef = getLastSheet(id);

      wsRef.close();

      CsvSheet ws = new CsvSheet();


      NumberFormat nf = NumberFormat.getInstance(); // Get Instance of NumberFormat
      nf.setMinimumIntegerDigits(2);  // The minimum Digits required is 5
      nf.setMaximumIntegerDigits(3); // The maximum Digits required is 5

      String sb = (nf.format((long) v.size()));
      String fname = wsRef.getName() + "_" + sb;
      ws.open(fname);

      addRefInMainSheet(fname, false);
      ws.titles = wsRef.titles;
      ws.names = wsRef.names;
      ws.maxTitle = wsRef.maxTitle;
      ws.maxName = wsRef.maxName;
      //    ws.maxEntry = wsRef.maxEntry;

      ws.flushTitles();
      ws.flushNames();

      v.add(ws);
      return ws;
  
       
   }
   void addRefInMainSheet(String libelle, boolean addPrefix) {
      String fileName;
      if (addPrefix) {
         fileName = workbookPrefix + "_" + libelle + ".csv";
      } else {
         fileName = libelle + ".csv";
      }

      String href = "<A HREF=\"file:///" + fileName + "\">" + libelle + "</A>";
  
      // mainSheet.addEntry(href, 0);
      //mainSheet.flushLine();
   
   }

   protected  class CsvSheet{

      String name; // nom de la "feuille" 
      CSVWriter writer; // objet consituant les lignes et les ecrivant dans le fichier
      String[] titles; // pour noter la ligne de titre
      String[] names;  // pour noter la ligne de noms de champs
      String[] entries; // pour noter la ligne en cours
      int curTitle;
      int curName;
      int curEntry;
      int colCount;
      int rowCount;
      int maxTitle;
      int maxName;
      int maxEntry;
      boolean titlesFlushed;
      boolean namesFlushed;

      void open(String fname) {
         try {
            FileWriter out = new FileWriter(fname + ".csv");
            writer = new CSVWriter(out);
         } catch (IOException e) {
            logger.error("HtmlSheet.open("+fname+")",e);
         }
         // Version initial tableau surdimensionnnes hum=
         name = fname;
         titles = new String[100];
         names = new String[100];
         entries = new String[100];
         maxTitle = 0;
         maxName = 0;
         maxEntry = 0;
         rowCount = 0;
         titlesFlushed = false;
         namesFlushed = false;
      }

      void close() {
         if (writer != null) {
            try {
               writer.close();

            } catch (IOException e) {
               logger.error("HtmlSheet.close()",e);
            }
            writer = null;
         }
      }

      String getName() {
         return name;
      }

      int getRows() {
         return rowCount;
      }

      void addTitle(String s, int col) {
         if (!titlesFlushed) {
            if (col >= titles.length || maxTitle >= titles.length) {
               // cas ou il faut redimensionner
               int oldlength = titles.length;
               int newlength = oldlength + oldlength / 2;
               String[] tm = new String[newlength];
               for (int i = 0; i < oldlength; ++i) {
                  tm[i] = titles[i];
               }
               titles = tm;
            }
            while (maxTitle + 1 < col) {
               maxTitle++;
               titles[maxTitle] = formatted("", 0); // pour simuler la presentation en Excel pur

            }
            titles[col] = s;
            if (col > maxTitle) {
               maxTitle = col;
            }

         }
      }

      void addName(String s, int col) {
         if (!namesFlushed) {
            if (col >= names.length || maxName >= names.length) {
               // cas ou il faut redimensionner
               int oldlength = names.length;
               int newlength = oldlength + oldlength / 2;
               String[] tm = new String[newlength];
               for (int i = 0; i < oldlength; ++i) {
                  tm[i] = names[i];
               }
               names = tm;
            }
            while (maxName + 1 < col) {
               maxName++;
               names[maxName] = formatted("", 1);
            }
            names[col] = s;
            if (col > maxName) {
               maxName = col;
            }

         }
      }

      void addEntry(String s, int col) {
         if (col >= entries.length || maxEntry >= entries.length) {
            // cas ou il faut redimensionner
            int oldlength = entries.length;
            int newlength = oldlength + oldlength / 2;
            String[] tm = new String[newlength];
            for (int i = 0; i < oldlength; ++i) {
               tm[i] = entries[i];
            }
            entries = tm;
         }


         while (maxEntry + 1 < col) {
            maxEntry++;
            entries[maxEntry] = formatted("", 999);
         }
         entries[col] = s;
         if (col > maxEntry) {
            maxEntry = col;
         }
      }

      void mergeCells(int col1, int row1, int col2, int row2) {
         /*   INUTILE  if( col1 == col2) { // on ne sait pas faire autrement!!
              int i = row1 + 1;
              while(i < row2) {
              if(col1 == 0)   { // titres  
              titles[i] = formatted("", 0); 
              } else   if(col1 == 1)    {// noms de champs 
              names[i] = formatted("", 0); 
              }
              ++i;   
              }
              }*/
      }

      void flushTitles() {
         writer.writeNext(titles, maxTitle);
         titlesFlushed = true;
         ++rowCount;
      }

      void flushNames() {
         writer.writeNext(names, maxName);
         namesFlushed = true;
         ++rowCount;
      }

      void flushLine() {
         if (!titlesFlushed) {
            flushTitles();
         }
         if (!namesFlushed) {
            flushNames();
         }

         writer.writeNext(entries, maxEntry);
         maxEntry = 0;
         ++rowCount;
      }
   };
}
