package kz.cafe;

import java.io.*;
import java.lang.reflect.*;
import java.sql.*;
import java.text.*;
import java.util.*;
import java.util.Date;
import java.util.concurrent.atomic.*;
import kz.cafe.cache.*;
import kz.cafe.constants.*;
import kz.cafe.constants.InterfaceConstants.LANGS;
import kz.cafe.db.*;
import kz.cafe.db.model.*;
import kz.cafe.db.model.anotations.*;



@SuppressWarnings("rawtypes")
public class DbExport{

   @SuppressWarnings("unchecked")
   public static final void export(String sPath,
                                   Map<String, Class> map,
                                   InterfaceConstants.LANGS lang){
      
      OnePassCache onePassCache = new OnePassCache();

      Connection c = null;
      Statement stmt = null;
      try{
         Class.forName("org.sqlite.JDBC");
         
         SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMdd");
         
         String sFullPath = sPath + "/" + sdf.format(new Date()) + "_" + lang + ".sqlite";
         
         File file = new File(sFullPath);
         file.delete();
         
         c = DriverManager.getConnection("jdbc:sqlite:" + sFullPath);

         stmt = c.createStatement();
         
         for(String sTableName : map.keySet()){
            StringBuilder sbSQL = new StringBuilder("");

            for(Field field : map.get(sTableName).getFields()){
               if(field.getAnnotation(AnnotationNotExported.class) == null){

                  String sFieldName = field.getName().toLowerCase();
                  if(field.getAnnotation(AnnotationIncusiveList.class) == null){
                     if(sbSQL.length() > 0){
                        sbSQL.append(" , ");
                     }
                     
                     if("id".equals(sFieldName)){
                        sbSQL.append("id int PRIMARY KEY NOT NULL");

                     }
                     else{
                        addFieldStructure(field, sFieldName, sbSQL);
                        
                        AnnotationExport annotationExport = field.getAnnotation(AnnotationExport.class);
                        
                        if(annotationExport == null){
                        }
                        else{
                           String[] arrstrFields = annotationExport.column().split(",");
                           for(String sFieldRelated : arrstrFields){
                              if(sbSQL.length() > 0){
                                 sbSQL.append(" , ");
                              }
                              
                              sFieldRelated = sFieldRelated.trim();
                              
                              Field fieldRealted = annotationExport.foreign().getField(sFieldRelated);
                              addFieldStructure(fieldRealted, annotationExport.local_prefix() + sFieldRelated, sbSQL);
                           }
                        }
                     }
                  }
                  else{
                     String sql = "CREATE TABLE " + sTableName + "2" + sFieldName + "("
                              + sTableName  + " int NOT NULL, "
                              + sFieldName + " int NOT NULL)";
                     stmt.executeUpdate(sql);
                  }
               }
            }
            String sSQL = "CREATE TABLE " + sTableName + " " + "(" + sbSQL.toString() + ")";
            stmt.executeUpdate(sSQL);
            stmt.close();




            
            StringBuilder sbFields = new StringBuilder();
            StringBuilder sbValues = new StringBuilder();
            
            for(Field field : map.get(sTableName).getFields()){
               if(field.getAnnotation(AnnotationNotExported.class) == null){

                  String sFieldName = field.getName().toLowerCase();
                  if(field.getAnnotation(AnnotationIncusiveList.class) == null){
                     if("id".equals(sFieldName)){
                        if(sbFields.length() > 0){
                           sbFields.append(" , ");
                        }
                        if(sbValues.length() > 0){
                           sbValues.append(" , ");
                        }
                        
                        sbFields.append(sFieldName);
                        sbValues.append("?");
                     }
                     else{
                        if(sbFields.length() > 0){
                           sbFields.append(" , ");
                        }
                        if(sbValues.length() > 0){
                           sbValues.append(" , ");
                        }
                        
                        addPrep(sbFields, sbValues, field, sFieldName);
                        
                        AnnotationExport annotationExport = field.getAnnotation(AnnotationExport.class);
                        
                        if(annotationExport == null){
                        }
                        else{
                           String[] arrstrFields = annotationExport.column().split(",");
                           for(String sFieldRelated : arrstrFields){
                              if(sbFields.length() > 0){
                                 sbFields.append(" , ");
                              }
                              if(sbValues.length() > 0){
                                 sbValues.append(" , ");
                              }
                              
                              
                              sFieldRelated = sFieldRelated.trim();
                              
                              Field fieldRealted = annotationExport.foreign().getField(sFieldRelated);
                              
                              addPrep(sbFields, sbValues, fieldRealted, annotationExport.local_prefix() + sFieldRelated);
                           }
                        }
                     }
                  }
                  else{
                     //sbFields.append(sFieldName);
                     //sbValues.append("?");
                  }
               }
            }
            
            System.out.println();
            
            PreparedStatement prep =
                     c.prepareStatement("insert into " + sTableName + "(" + sbFields.toString() + ") values (" + sbValues.toString() + ");");
            
            c.setAutoCommit(false);
            
            Set<Integer> setProcessed = new HashSet();
            
            for(Object obj : MongoService.ds.find(map.get(sTableName)).order(MAbstractDataElement.F_ID)){
               
               int ID = obj.getClass().getField("ID").getInt(obj);
               
               if(setProcessed.contains(ID)){
                  continue;
               }
               
               setProcessed.add(ID);
               

               AtomicInteger iCounter = new AtomicInteger(1);
               for(Field field : map.get(sTableName).getFields()){
                  
                  if(field.getAnnotation(AnnotationNotExported.class) == null){
                     
                     String sFieldName = field.getName().toLowerCase();
                     if(field.getAnnotation(AnnotationIncusiveList.class) == null){
                        if("id".equals(sFieldName)){
                           
                           System.out.println(map.get(sTableName).getSimpleName() + " id=" + field.getLong(obj));
                           
                           prep.setLong(iCounter.get(), field.getLong(obj));
                           
                           iCounter.incrementAndGet();
                        }
                        else{
                           Object value = field.get(obj);
                           addFieldValue(prep, field, iCounter, value, lang);
                           
                           iCounter.incrementAndGet();
                           
                           AnnotationExport annotationExport = field.getAnnotation(AnnotationExport.class);
                           
                           if(annotationExport == null){
                           }
                           else{
                              String[] arrstrFields = annotationExport.column().split(",");
                              for(String sFieldRelated : arrstrFields){
                                 sFieldRelated = sFieldRelated.trim();
                                 
                                 Field fieldRealted = annotationExport.foreign().getField(sFieldRelated);
                                 
                                 int ID_RELATED = field.getInt(obj);
                                 
                                 addFieldValue(prep,
                                               fieldRealted,
                                               iCounter,
                                               onePassCache.getValue(annotationExport.foreign(), ID_RELATED, sFieldRelated),
                                               lang);
                                 
                                 iCounter.incrementAndGet();
                              }
                           }
                        }
                        
                     }
                     else{
                        Set<Integer> setValues = (Set<Integer>) field.get(obj);
                        
                        PreparedStatement prepFK =
                                 c.prepareStatement("insert into " + sTableName + "2" + sFieldName + " values (?, ?);");
                        
                        for(Integer FK : setValues){
                           prepFK.setInt(1, obj.getClass().getField("ID").getInt(obj));
                           prepFK.setInt(2, (int) FK);
                           
                           prepFK.executeUpdate();
                        }
                     }
                  }
               }
               prep.executeUpdate();
            }
            c.commit();
         }
         c.close();
      }
      catch (Exception e){
         throw new Error(e);
      }
      
      // for(Object obj : MongoService.ds.find(clas).offset(iCounter).asList()){
      //
      //
      //
      // iCounter += FRAME;
      // }
   }
   
   
   
   
   private static void addFieldStructure(Field field, String sLocalName, StringBuilder sbSQL){
      if(field.getAnnotation(AnnotationMultiLangString.class) == null) {
         String sClassName = field.getGenericType().toString().toLowerCase();

         sbSQL.append(" " + sLocalName + " ");

         if(sClassName.startsWith("int") || sClassName.endsWith("integer")){
            sbSQL.append(" int ");
         }
         else if(sClassName.startsWith("long")){
            sbSQL.append(" int ");
         }
         else if(sClassName.startsWith("bool")){
            sbSQL.append(" bool ");
         }
         else if(sClassName.startsWith("double")){
         }
         else{
            sbSQL.append(" text ");
         }
      }
      else{
         sbSQL.append(" " + sLocalName + " ");
         sbSQL.append(" text ");
         
         if(field.isAnnotationPresent(AnnotationNoExportIndex.class)) {
         }
         else {
            sbSQL.append(" , ");
            sbSQL.append(" " + sLocalName + "_ ");
            sbSQL.append(" text ");
         }
      }
   }
   

   
   private static void addPrep(StringBuilder sbFields,
                               StringBuilder sbValues,
                               Field field,
                               String sFieldName){
      if(field.getAnnotation(AnnotationMultiLangString.class) == null) {
         sbFields.append(sFieldName);
         sbValues.append("?");
      }
      else{
         if(field.isAnnotationPresent(AnnotationNoExportIndex.class)) {
            sbFields.append(sFieldName);
            sbValues.append("?");
         }
         else {
            sbFields.append(sFieldName + "," + sFieldName + "_");
            sbValues.append("?,?");
         }
      }
   }
   

   
   private static void addFieldValue(PreparedStatement prep,
                                     Field field,
                                     AtomicInteger iCounter,
                                     Object value,
                                     InterfaceConstants.LANGS lang) throws Exception{
      if(field.getAnnotation(AnnotationMultiLangString.class) == null) {
         String sClassName = field.getGenericType().toString().toLowerCase();
         
         try {
            if(value == null) {
               //let's set nothing here
            }
            else if(sClassName.startsWith("int") || sClassName.endsWith("integer")){
               prep.setInt(iCounter.get(), (Integer) value);
            }
            else if(sClassName.startsWith("long")){
               prep.setLong(iCounter.get(), (Long) value);
            }
            else if(sClassName.startsWith("bool")){
               prep.setBoolean(iCounter.get(), (Boolean) value);
            }
            else if(sClassName.startsWith("double")){
            }
            else{
               prep.setString(iCounter.get(), "" + value);
            }
         }
         catch(Throwable e) {
            System.out.println(value + "=" + field);
            e.printStackTrace();
         }
      }
      else{
         @SuppressWarnings("unchecked")
         Map<InterfaceConstants.LANGS, String> mapValue = (Map<LANGS, String>) value;
         
         prep.setString(iCounter.get(), "" + mapValue.get(lang));

         if(field.isAnnotationPresent(AnnotationNoExportIndex.class)) {
         }
         else {
            iCounter.incrementAndGet();
            prep.setString(iCounter.get(), ("" + mapValue.get(lang)).toLowerCase());
         }
      }
      
   }
}
