package util.sqlparse.visitor.mysql.parser;

import bean.DataBase;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import util.sqlparse.visitor.common.Context;
import util.sqlparse.visitor.common.bean.FieldInfo;
import util.sqlparse.visitor.common.bean.SQLResult;
import util.sqlparse.visitor.common.bean.TableInfo;
import util.sqlparse.visitor.common.bean.ValueInfo;
import util.sqlparse.visitor.common.memo.FieldMemo;
import util.sqlparse.visitor.common.memo.TableMemo;
import util.sqlparse.visitor.common.memo.ValueMemo;
import util.sqlparse.visitor.common.scope.Scope;
import util.sqlparse.visitor.mysql.SQLParser;
import util.sqlparse.visitor.mysql.visitor.FieldVisitor;
import util.sqlparse.visitor.mysql.visitor.MySQLScopeDialector;
import util.sqlparse.visitor.mysql.visitor.ScopeVisitor;
import util.sqlparse.visitor.mysql.visitor.ValueReplacer;

public class SimpleSQLStatementParser extends SQLStatementParser {
   public SQLResult parseStatement(SQLStatement statement, DataBase dataBase, String schema) {
      Context context = new Context();
      context.initialize(dataBase, schema, "mysql");
      Scope scope = new Scope();
      scope.dialector = new MySQLScopeDialector(scope);
      scope.context = context;
      scope.isCheckIsolated = true;
      List<SQLStatement> statements = Arrays.asList(statement);
      ScopeVisitor scopeVisitor = new ScopeVisitor(scope);
      scopeVisitor.perform(statements);
      FieldVisitor fieldVisitor = new FieldVisitor(scope);
      fieldVisitor.perform(statements);
      SQLResult result = new SQLResult();
      result.sqlType = context.sqlType;
      result.tables = context.getTables();
      Map<String, TableMemo> tables = new HashMap(6);
      this.getAllTables(scope, tables);
      Collection<TableMemo> tablesTemp = tables.values();
      List<TableMemo> tableMemoList = new ArrayList();

      for(TableMemo tableMemo : tablesTemp) {
         if (!tableMemo.topTable) {
            tableMemoList.add(tableMemo);
         }
      }

      result.tableMemos = tableMemoList;
      tables.clear();
      Map<String, FieldMemo> fields = new HashMap();
      Map<String, FieldInfo> fieldInfos = new LinkedHashMap();
      this.getAllFields(scope, fields, fieldInfos);
      result.fieldMemos = new ArrayList(fields.values());
      result.fields = new ArrayList(fieldInfos.values());
      fields.clear();
      List<FieldInfo> outputFields = new ArrayList();
      this.getSelectFields(scope, fieldInfos, fields, context.getTableMap(), outputFields);
      result.outputMemos = new ArrayList(fields.values());
      result.outputs = outputFields;
      fields.clear();
      List<ValueInfo> values = new ArrayList();
      this.getValues(scope, fieldInfos, values);
      result.values = values;
      result.statement = (SQLStatement)statements.get(0);
      result.statementType = SQLParser.getStatementType(result.statement);
      result.isCaseSensitive = scope.isCaseSensitive();
      return result;
   }

   private void getAllTables(Scope scope, Map<String, TableMemo> tableMap) {
      for(Scope child : scope.getChildren()) {
         for(Map.Entry<String, TableMemo> entry : child.getTableMap().entrySet()) {
            tableMap.put(((TableMemo)entry.getValue()).getUniqueName(), entry.getValue());
         }

         this.getAllTables(child, tableMap);
      }

   }

   private void getAllFields(Scope scope, Map<String, FieldMemo> fieldMap, Map<String, FieldInfo> fields) {
      Map<String, TableInfo> tables = scope.context.getTableMap();

      for(Scope child : scope.getChildren()) {
         for(Map.Entry<String, FieldMemo> entry : child.getFieldMap().entrySet()) {
            fieldMap.put(((FieldMemo)entry.getValue()).getUniqueName(), entry.getValue());
            FieldMemo fieldMemo = (FieldMemo)entry.getValue();
            Map<String, TableInfo> tableMap = new HashMap();
            boolean isValid = true;
            if ((fieldMemo.children.size() == 0 && !fieldMemo.complex || fieldMemo.atom) && !fieldMemo.isConstant) {
               List<TableMemo> tableMemos = new ArrayList(fieldMemo.tables);
               if (fieldMemo.table != null) {
                  tableMemos.add(fieldMemo.table);
               }

               for(TableMemo table : tableMemos) {
                  TableInfo tableInfo = (TableInfo)tables.get(table.getAtomName());
                  if (tableInfo == null) {
                     isValid = false;
                     break;
                  }

                  tableMap.put(table.getAtomName(), tableInfo);
               }
            }

            if (tableMap.size() > 0 && isValid) {
               FieldInfo fieldInfo = new FieldInfo();
               fieldInfo.setName(fieldMemo.name);
               fieldInfo.setMemo(fieldMemo);
               fieldInfo.setAlias(fieldMemo.alias != null && !"".equals(fieldMemo.alias) ? fieldMemo.alias.replaceAll("`", "") : "");
               fieldInfo.setFullName(fieldMemo.getAtomName());
               fields.put(fieldMemo.getAtomName(), fieldInfo);
               fieldInfo.setTables(tableMap);
            }
         }

         this.getAllFields(child, fieldMap, fields);
      }

   }

   private void getSelectFields(Scope scope, Map<String, FieldInfo> fields, Map<String, FieldMemo> fieldMap, Map<String, TableInfo> tables, List<FieldInfo> outputFields) {
      for(Scope child : scope.getChildren()) {
         if (!child.top) {
            if (child.fromField) {
               return;
            }

            if (!child.top && child.getFieldMap().size() > 0) {
               int i = 0;

               for(Map.Entry<String, FieldMemo> entry : child.getFieldMap().entrySet()) {
                  ++i;
                  FieldMemo field = (FieldMemo)entry.getValue();
                  if (field.isSelectItem && !field.isConstant) {
                     List<FieldMemo> childs = field.getChildren();
                     if (null != childs && childs.size() > 0) {
                        ListIterator<FieldMemo> listIterator = childs.listIterator();

                        while(listIterator.hasNext()) {
                           FieldMemo element = (FieldMemo)listIterator.next();
                           if (!element.isSelectItem) {
                              listIterator.remove();
                           }
                        }
                     }

                     FieldInfo info = new FieldInfo();
                     info.setFullName(field.ref == null ? field.name : field.ref.toString());
                     info.setName(field.name);
                     info.setAlias(field.alias);
                     info.setMemo(field);
                     info.setSeq(i);
                     Map<String, FieldInfo> relations = new HashMap();
                     this.getFieldInfos(field, fields, relations);
                     if (field.tables.size() > 0) {
                        this.getAtomTables(field.tables, tables, info.getTables());
                     }

                     if (relations.size() > 0) {
                        for(Map.Entry<String, FieldInfo> e : relations.entrySet()) {
                           info.getTables().putAll(((FieldInfo)e.getValue()).getTables());
                        }

                        info.setRelations(new ArrayList(relations.values()));
                     }

                     if (!field.complex || relations.size() != 0) {
                        fieldMap.put(field.getUniqueName(), field);
                        outputFields.add(info);
                        if (child.ref instanceof MySqlCreateTableStatement) {
                           this.getSelectFields(child, fields, fieldMap, tables, outputFields);
                        }
                     }
                  }
               }
            } else {
               this.getSelectFields(child, fields, fieldMap, tables, outputFields);
            }
         }
      }

   }

   private void getFieldInfos(FieldMemo fieldMemo, Map<String, FieldInfo> fields, Map<String, FieldInfo> output) {
      if (fieldMemo.children != null && fieldMemo.children.size() != 0) {
         for(FieldMemo child : fieldMemo.children) {
            this.getFieldInfos(child, fields, output);
         }
      } else {
         String name = fieldMemo.getAtomName();
         if (fields.containsKey(name)) {
            FieldInfo fieldInfo = (FieldInfo)fields.get(name);
            fieldInfo.getMemos().add(fieldMemo);
            output.put(name, fieldInfo);
         }
      }

   }

   private void getAtomTables(List<TableMemo> tables, Map<String, TableInfo> tableMap, Map<String, TableInfo> atomTableMap) {
      if (tables != null && tables.size() != 0) {
         for(TableMemo table : tables) {
            if (table.getChildren().size() == 0) {
               TableInfo tableInfo = (TableInfo)tableMap.get(table.getAtomName());
               if (tableInfo != null) {
                  atomTableMap.put(tableInfo.getAtomName(), tableInfo);
               }
            } else {
               this.getAtomTables(table.getChildren(), tableMap, atomTableMap);
            }
         }

      }
   }

   private void getValues(Scope scope, Map<String, FieldInfo> fieldMap, List<ValueInfo> values) {
      for(ValueMemo value : scope.getValues()) {
         ValueInfo valueInfo = new ValueInfo(value);
         FieldMemo field = value.field;
         Map<String, FieldInfo> fields = new HashMap();
         this.getFieldInfos(field, fieldMap, fields);
         valueInfo.setFields(new ArrayList(fields.values()));
         values.add(valueInfo);
      }

   }

   public void replace(List<ValueInfo> values, Map<String, String> replaceValue) {
      ValueReplacer replacer = new ValueReplacer(replaceValue);
      replacer.replaceValues(values);
   }

   public void replace(List<ValueInfo> values, List<Map> whereList) {
      ValueReplacer replacer = new ValueReplacer(whereList);
      replacer.replaceValuesWithList(values);
   }
}
