/*
 * Created by Methodyne GmbH.
 * User: rea
 * Date: 24.04.2003
 * Time: 12:48:56

 */
package net.methodyne.bellvue.system;

import net.methodyne.bellvue.system.Application;
import net.methodyne.bellvue.system.BClass;
import net.methodyne.bellvue.core.*;

import java.util.*;
import java.io.*;
import java.lang.reflect.Field;

public class Bulk implements Serializable{
    public long id = -1l;
    public String title = "";
    public String path = "";
    public String files = "<A HREF=\"files\">files</A>";
    public String upload = "<A HREF=\"upload.html\">upload</A>";
    public String importPolicy = "add";
    public boolean use_encoding = true;
    public String encoding = "UTF-8";
    public boolean cryptography = false;
    public String cryptoPassword = "";
    public Vector bClass = new Vector();
    public Vector referenceBClass = new Vector();
    public Vector apps = new Vector();
    public Vector referenceApps = new Vector();

    public String typeBClass(){ return "net.methodyne.bellvue.system.BClass";}
    public String typeApps(){ return "net.methodyne.bellvue.system.Application";}

    public String dispEncoding(Sdata sd){
        return "SELECT,UTF-8,ISO-8859-1,US-ASCII,UTF-16";
    }

    public String dispImportPolicy(Sdata sd){
        return "SELECT,add,replace";
    }

    public String aboutFiles(Sdata sd){
        return "off";
    }
    public String aboutUpload(Sdata sd){
        return "off";
    }

    public String getTitle(){
        return title;
    }

    long records = 0;

    public void actionImport(Sdata sd) {
        records = 0;
        Date one = new Date();
        System.out.println("action:Import");
        String fs = System.getProperty("file.separator");
        String base = sd.sctx.getRealPath("/files");
        boolean clear = false;
        Vector classes = new Vector();
        BufferedReader br = null;
        // select add or replace
        if(importPolicy.equalsIgnoreCase("add")){
            clear = false;
        }
        else{
            clear = true;
        }

        File f = new File( base + path);
        File[] files;
        Finder bc = new Finder("net.methodyne.bellvue.system.BClass");
        bc.run( sd);
        if( f.isDirectory() ){
            files = f.listFiles();
        }
        else{
            files = new File[]{ f };
        }
        // get BClass names in a vector
        for(int i =0 ; i < files.length; i++){
            try{
                System.out.println(files[i].getCanonicalFile().getName());
                for (int j = 0; j < bc.olist.size(); j++) {
                    String s = ((BClass)bc.olist.elementAt(j)).bclassname;
                    if( s.equalsIgnoreCase(files[i].getCanonicalFile().getName()) ){
                        classes.add( files[i].getCanonicalFile().getName() );
                    }
                }
            }
            catch (IOException e){
                e.printStackTrace();
            }
        }

        for (int i = 0; i < classes.size(); i++) {
            String s = (String) classes.elementAt(i);
            try{ // open files
                String dir = null;
                if(f.isDirectory()){
                    dir = f.getAbsolutePath();
                }
                else{
                    dir = f.getParent();
                }
                if( cryptography ){
                    br = new BufferedReader( new InputStreamReader(
                            new PBEInputStream(cryptoPassword,
                            new FileInputStream( dir + fs + s ))));
                }
                else{
                    br = new BufferedReader( new InputStreamReader(new FileInputStream( dir + fs + s )));
                }
            }
            catch(IOException e){
                e.printStackTrace();
            }
            String fileheader = null;
//            String objheader = null;
            try{
                // dont check header - all matching fields will get assigned
                // map header fields to numbers in hashmap
                fileheader = br.readLine();
                HashMap headers = new HashMap();
                int cnt = 0;
                String fname = "";
                char cc;
                for(int a = 0 ; a < fileheader.length(); a++){
                    cc = fileheader.charAt(a);
                    if(cc == ';'){
                        headers.put(fname, new Integer(cnt));
                        cnt++;
                        //System.out.println(fname);
                        fname = "";
                    }
                    else{
                        fname += cc;
                    }
                }
                headers.put(fname, new Integer(cnt)); // last in line
                //System.out.println(fname);

                Vector newvec = new Vector();
                String rec = br.readLine();
                while(rec != null){
                    records++;
                    if( (records % 10000) == 0 ){
                        System.gc();
                        System.out.println("rec: " + records + "used mem : " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024) ) + "MB");
                    }
                    Object x = Class.forName(s).newInstance();
                    assign(headers, rec, x);
                    newvec.add(x);
                    rec = br.readLine();
                }
                if(newvec.size() > 0)
                if(clear){  // clear
                    // replace old vector with new vector
                    sd.dl.repAll(sd, newvec, s);
                    (sd.dl.memStore.system()).setId(sd, s,
                            newvec.elementAt(newvec.size()-1).getClass().getField("id")
                            .getLong(newvec.elementAt(newvec.size()-1)) );
                    if( s.endsWith(".User") ){
                        // remove loaded user with same name and add current user to new vector
                        Finder uf = new Finder(s);
                        uf.querys.put("username", sd.user.username);
                        uf.run( sd);
                        if(uf.olist.size() >0){
                            sd.dl.delete(sd, uf.olist.elementAt(0) );
                        }
                        sd.dl.store(sd, sd.user);
                    }
                }
                else{
                    // add newVector to old vector with new id
                    if( newvec.size() >0 ){
                        long id = 0;
                        id = (sd.dl.memStore.system()).getId(sd, s);
                        System.out.println("import " + s + " max id:" + id );
                        for(int e = 0; e < newvec.size(); e++){
                            newvec.elementAt(e).getClass().getField("id").setLong(newvec.elementAt(e), id);
                            id++;
                        }
                        sd.dl.addAll(sd, newvec, s);
                        (sd.dl.memStore.system()).setId(sd, s, id);
                    }
                }
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
        Date two = new Date();
        System.out.println("import duration: " + ((two.getTime() - one.getTime()) / 1000) + "sec.");
        sd.dl.takeState();
    }

    public String aboutImport(Sdata sd){
        return "on";
    }

    public void assign(HashMap headers, String rec, Object x ){

//        System.out.println("rec: " + rec);

        // put record fields in string array according to hashmap
        String[] values = new String[ headers.size() ];
        int cnt = 0;
        String val = "";
        char cc;
        for(int a = 0 ; a < rec.length(); a++){
            cc = rec.charAt(a);
            if(cc == ';'){
                if( cnt == headers.size() -1 )
                    break;
                else{
                    values[cnt] = val;
                    cnt++;
                    //System.out.println(val);
                    val = "";
                }
            }
            else{
                val += cc;
            }
        }
        values[cnt] = val; // last one in line

        // for each field get number from hashmap and get according string from array
        Field[] f = x.getClass().getFields();
        for(int i = 0 ; i < f.length ; i++ ){
            try {
                String name = f[i].getName() ;
                int vin = -1;
                Integer one = (Integer)headers.get(name);
                if(one != null){ // only if this field is in record
                    vin = one.intValue();
                    val = values[ vin ];
                    if(val != null){
                        //System.out.println("field: " + name + " val: " + val);
                        String type = f[i].getType().getName() ;
                        if( type.endsWith("String") ){
                            if(use_encoding){
                                f[i].set( x,  java.net.URLDecoder.decode( val, encoding ));
                            }
                            else{
                                f[i].set( x, val);
                            }
                        }
                        else if( type.equalsIgnoreCase("int") ){
                                f[i].setInt(x, Integer.parseInt(val ));
                        }
                        else if( type.equalsIgnoreCase("boolean") ){
                                f[i].setBoolean(x, val.equalsIgnoreCase("true") ? true : false );
                        }
                        else if( type.equalsIgnoreCase("float") ){
                                f[i].setFloat(x, Float.parseFloat(val ));
                        }
                        else if( type.endsWith("Date") ){
                                f[i].set( x, new Date(Long.parseLong(val )))  ;
                        }
                        else if( type.endsWith("Vector") ){
                            Vector v = new Vector();
                            if(val.length() > 0){
                                StringTokenizer s1 = new StringTokenizer(val, ",");
                                while (s1.hasMoreTokens()) {
                                    v.add( new Long ( Long.parseLong(s1.nextToken() ) ) );
                                }
                            }
                            f[i].set( x, v);
                        }
                        else if( type.equalsIgnoreCase("char") ){
                                f[i].setChar(x, val.charAt(0) );
                        }
                        else if( type.equalsIgnoreCase("short") ){
                                f[i].setShort(x, Short.parseShort(val ));
                        }
                        else if( type.equalsIgnoreCase("byte") ){
                                f[i].setByte(x, Byte.parseByte(val ));
                        }
                        else if( type.equalsIgnoreCase("long") ){
                            f[i].setLong(x, Long.parseLong(val ));
                        }
                        else if( type.equalsIgnoreCase("double") ){
                                f[i].setDouble(x, Double.parseDouble(val ));
                        }
                        else if( name.startsWith("reference") ){ // here it should be a reference to an App Object
                            //System.out.println("aaa:" + val + "< " + f[i].getName() );
                            f[i].set(x, new Long(Long.parseLong(val )) );
                        }
                    }
                }
            }
            catch(NoSuchElementException e){
                System.out.println("Bulk:imp:NoSuchElementException");
                //e.printStackTrace();
            }
            catch(IllegalAccessException e){
                System.out.println("Bulk:imp:IllegalAccessException");
                //e.printStackTrace();
            }
            catch(NumberFormatException e){
                System.out.println("Bulk:imp:NumberFormatException");
                //e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    }

    public void actionExport(Sdata sd) {
        Date one = new Date();

        File exdir = newExportDir(sd);

        if(bClass.size() > 0){ // export all classes in system
            System.out.println("action:Export:selected");
            doClass( bClass, exdir,sd);
        }
        if(apps.size() > 0){ // export all classes in system
            System.out.println("action:Export:Apps");
            for (int i = 0; i < apps.size(); i++) {
                doClass( ((Application)apps.elementAt(i)).classes , exdir,sd);
            }
        }
        System.out.println("export duration: " + ((new Date().getTime() - one.getTime()) / 1000) + "sec.");
    }

    public String aboutExport(Sdata sd){
        return "on";
    }

    public void actionExportAll(Sdata sd) {
        Date one = new Date();
        System.out.println("action:Export:all");
        File exdir = newExportDir(sd);
        Finder F = new Finder("net.methodyne.bellvue.system.BClass");
        F.run( sd);
        doClass( F.olist, exdir,sd);
        System.out.println("export duration: " + ((new Date().getTime() - one.getTime()) / 1000) + "sec.");
    }

    public String aboutExportAll(Sdata sd){
        return "on";
    }

    public void actionDelete(Sdata sd) {
        if( path.indexOf("..") >= 0 ){
            System.out.println("Only files and directories in the /files directory allowed!");
        }
        else{
            File f = new File( sd.sctx.getRealPath( "/files" + path) ) ;
            if(path.length() > 0)
            if( f.exists() ){
                if(f.isDirectory()){
                    File[] a = f.listFiles();
                    for(int i = 0; i < a.length; i++){
                        if(a[i].delete()){
                            System.out.println("deleted " + a[i].getName() );
                        }
                        else{
                            System.out.println("error deleting " + a[i].getName() );
                        }
                    }
                    if(f.delete()){
                        System.out.println("deleted " + f.getName() );
                    }
                    else{
                        System.out.println("error deleting " + f.getName() );
                    }
                }
                else{
                    if(f.delete()){
                        System.out.println("deleted " + f.getName() );
                    }
                    else{
                        System.out.println("error deleting " + f.getName() );
                    }
                }
            }
            else{
                System.out.println("file not found " + f.getName() );
            }
        }
    }

    public String aboutDelete(Sdata sd){
        return "on";
    }

    public File newExportDir(Sdata sd) {
        // assemble export dir
        String fs = System.getProperty("file.separator");
        String epath = sd.sctx.getRealPath("/files");
        int exnum = 0;
        String dir = "ex" + exnum ;
        File exdir = new File(epath + fs + dir);
        while(exdir.isDirectory()){
            exnum++;
            dir = "ex" + exnum ;
            exdir = new File(epath + fs + dir);
        }
        exdir.mkdir();
        return exdir;
    }

    public String doClass(Vector obj, String type, Sdata sd){
        String fs = System.getProperty("file.separator");
        File exfile = null;
        PrintWriter pw = null;
        String outdir = newExportDir(sd).getAbsolutePath();
        String ret = outdir.substring(  outdir.indexOf("ex"));
        exfile = new File( outdir + fs + type );
        System.out.println("" + exfile.getAbsoluteFile());
        try{
            if( cryptography ){
                pw = new PrintWriter(new BufferedWriter( new OutputStreamWriter(
                        new PBEOutputStream(cryptoPassword,
                        new FileOutputStream(exfile)))));
            }
            else{
                pw = new PrintWriter(new BufferedWriter(new FileWriter(exfile)));
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }

        try{
            pw.println( getHeader( Class.forName(type).newInstance() ));
        }
        catch(Exception e){
            e.printStackTrace();
        }

        for (int j = 0; j < obj.size(); j++) {
            Object x = obj.elementAt(j);
            pw.println( toRecord(x));
        }
        pw.flush();
        pw.close();
        return ret;
    }

    void doClass(Vector bc, File exdir, Sdata sd){
        String fs = System.getProperty("file.separator");
        File exfile = null;
        PrintWriter pw = null;

        for (int i = 0; i < bc.size(); i++) {
            BClass o = (BClass) bc.elementAt(i);
            Finder E = new Finder( o.bclassname);
            E.run( sd);

            exfile = new File(exdir.getAbsolutePath() + fs + o.bclassname  );
            System.out.println("" + exfile.getAbsoluteFile());
            try{
               if( cryptography ){
                   pw = new PrintWriter(new BufferedWriter( new OutputStreamWriter(
                        new PBEOutputStream(cryptoPassword,
                        new FileOutputStream(exfile)))));
                }
                else{
                    pw = new PrintWriter(new BufferedWriter(new FileWriter(exfile)));
                }
            }
            catch(IOException e){
                e.printStackTrace();
            }

            try{
                pw.println( getHeader( Class.forName(o.bclassname ).newInstance() ));
            }
            catch(Exception e){
                e.printStackTrace();
            }

            for (int j = 0; j < E.olist.size(); j++) {
                Object x = E.olist.elementAt(j);
                pw.println( toRecord(x));
            }
            pw.flush();
            pw.close();
        }
    }

    String toRecord(Object x){
        String rec = "";
        String tmp = null;
        Field[] f = x.getClass().getFields();
        for(int i = 0 ; i < f.length  ; i++ ){
            String name = f[i].getName() ;
            String type = f[i].getType().getName() ;
            tmp = null;
            try{
                if( type.equalsIgnoreCase("boolean") ){
                    tmp = "" + f[i].getBoolean(x);
                }
                else if( " char short byte int long float double".indexOf( type ) > 0 ){
                    tmp = "" +  f[i].get(x);
                }
                else if( type.endsWith("Date") ){
                    tmp = "" +  ((Date)f[i].get(x)).getTime() ;
                }
                else if( type.endsWith("String") ){
                    if(use_encoding){
                        tmp = "" +  java.net.URLEncoder.encode( (String)f[i].get(x), encoding );
                    }
                    else{
                        tmp = "" +  (String)f[i].get(x) ;
                    }
                }
                else if( type.endsWith("Vector") && ( name.startsWith("reference") ) ){
                    Vector v = (Vector)f[i].get(x);
                    tmp = "";
                    if(v != null)
                    if(v.size() > 0)
                    for (int j = 0; j < v.size(); j++) {
                        try{
                            tmp += "" + ((Long)v.elementAt(j)).longValue() ;
                        }
                        catch (Exception e){
                            e.printStackTrace();
                        }
                        if(j < (v.size() -1 )) tmp += ",";
                    }
                }
                else if( type.endsWith("Object") && name.startsWith("reference") ){
                    Long o = (Long)x.getClass().getField(name).get(x);
                    if( o != null ){
                         tmp = "" +  o.longValue();
                    }
                }
            }
            catch(IllegalAccessException e){
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            if(tmp != null){
                if(i < (f.length -1 )){
                    rec += tmp + ";";
                }
                else{
                    rec += tmp;
                }
            }

        }
        return rec;
    }

    String getHeader(Object x){
        String rec = "";
        String tmp = null;
        Field[] f = x.getClass().getFields();
        for(int i = 0 ; i < f.length  ; i++ ){
            String name = f[i].getName() ;
            String type = f[i].getType().getName();
            if( type.endsWith("Vector") && ( !name.startsWith("reference") ) ){}
            else if( type.endsWith("Object") && ( !name.startsWith("reference") ) ){}
            else{
                tmp = null;
                tmp = "" + name;
                if(tmp != null){
                    if(i < (f.length -1 )){
                        rec += tmp + ";";
                    }
                    else{
                        rec += tmp;
                    }
                }
            }
        }
        return rec;
    }
}