package util.getdata;

import bean.Column;
import bean.DataBase;
import bean.Schema;
import bean.Table;
import bean.View;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import util.JdbcCacheUtil;

public class CacheSqlServer extends CacheData {
   public CacheSqlServer(DataBase dataBase, CountDownLatch cdl) {
      super(dataBase, cdl);
   }

   public List<Schema> getSchema(Statement statement) throws SQLException {
      return null;
   }

   public List<Table> getTable(Statement statement, String schema) throws SQLException {
      return null;
   }

   public List<Column> getColumn(Statement statement, String schema, String table) throws SQLException {
      return null;
   }

   public List<View> getView(Statement statement, String schema) throws SQLException {
      return null;
   }

   public void run() {
      System.out.println("sql server开始缓存");

      try {
         this.cacheWriteToRedis();
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            JdbcCacheUtil.closeConnection(this.connection);
            if (this.statement != null) {
               this.statement.close();
            }
         } catch (SQLException throwables) {
            throwables.printStackTrace();
         }

         this.getCdl().countDown();
         System.out.println("sql server结束缓存");
      }

   }

   public List<Schema> cacheToMemory() throws SQLException {
      return this.getMeta();
   }

   public List<Schema> getMeta() throws SQLException {
      try {
         Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
      } catch (ClassNotFoundException e) {
         e.printStackTrace();
      }

      this.connection = DriverManager.getConnection(this.DataBase.getUrl(), this.DataBase.getUsername(), this.DataBase.getPassword());
      this.statement = this.connection.createStatement();
      List<String> databases = new ArrayList();
      List<String> databasesAll = this.getDatabases(this.statement);
      this.filterDatabases(databases, databasesAll);
      Map<String, Schema> schemaMap = new HashMap();
      List<Schema> schemas = this.getSchemas(this.statement, databases, schemaMap);
      this.getViews(this.statement, databases, schemaMap);
      Map<String, Table> tableMap = new HashMap();
      List<Table> tables = this.getTables(this.statement, databases, schemaMap, tableMap);
      if (tables != null && tables.size() > 0) {
         int SPLIT = 200;
         List<TableItem> items = new ArrayList();
         List<List<TableItem>> groups = new ArrayList();
         int k = 0;
         String dbName = "";

         for(Table table : tables) {
            ++k;
            if (dbName.length() == 0) {
               dbName = table.getDbName();
            }

            if (!dbName.equals(table.getDbName())) {
               groups.add(items);
               items = new ArrayList();
               dbName = table.getDbName();
               k = 0;
            }

            TableItem item = new TableItem();
            item.table = table;
            item.databaseName = table.getDbName();
            item.tableName = table.getName();
            item.schemaName = table.getSchema();
            items.add(item);
            if (k % 200 == 0) {
               groups.add(items);
               items = new ArrayList();
            }
         }

         if (items.size() > 0) {
            groups.add(items);
         }

         new ArrayList();
         CountDownLatch latch = new CountDownLatch(groups.size());

         for(List<TableItem> group : groups) {
            ColumnThread columnsGetter = new ColumnThread(this.connection, latch, group);
            Thread thread = new Thread(columnsGetter);
            thread.start();
         }

         try {
            latch.await();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }

      return schemas;
   }

   private void filterDatabases(List<String> databases, List<String> databasesAll) throws SQLException {
      for(String database : databasesAll) {
         try {
            this.getSchema(this.statement, database);
            databases.add(database);
         } catch (Exception var6) {
         }
      }

   }

   public List<String> getDatabases(Statement statement) throws SQLException {
      List<String> databases = new ArrayList();
      String sql = "select name from sysdatabases";
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         String name = resultSet.getString("name");
         databases.add(name);
      }

      resultSet.close();
      return databases;
   }

   public List<Schema> getSchema(Statement statement, String databaseName) throws SQLException {
      List<Schema> schemas = new ArrayList();
      String sql = "SELECT * FROM [" + databaseName + "].sys.schemas";
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         Schema schema = new Schema(resultSet.getString("name"));
         schema.setRealName(resultSet.getString("name"));
         schema.setDatabaseName(databaseName);
         schemas.add(schema);
      }

      resultSet.close();
      return schemas;
   }

   public List<Table> getTable(Statement statement, String schema, String databaseName) throws SQLException {
      List<Table> tables = new ArrayList();
      String sql = "select * from [" + databaseName + "].INFORMATION_SCHEMA.TABLES  where TABLE_TYPE = 'BASE TABLE' and TABLE_SCHEMA = '" + schema + "'";
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         Table table = new Table(resultSet.getString("TABLE_NAME"), schema);
         table.setRealName(resultSet.getString("TABLE_NAME"));
         tables.add(table);
      }

      resultSet.close();
      return tables;
   }

   public List<Column> getColumn(Statement statement, String schema, String table, String databaseName) throws SQLException {
      List<Column> columns = new ArrayList();
      String sql = "select a.*,NULLIF(c.pk,0) PK \nfrom [" + databaseName + "].INFORMATION_SCHEMA.COLUMNS A\nLEFT JOIN (\n\tselect d.TABLE_CATALOG ,d.TABLE_SCHEMA ,d.TABLE_NAME ,b.COLUMN_NAME ,1 as PK\n\tfrom [" + databaseName + "].INFORMATION_SCHEMA.TABLE_CONSTRAINTS d\n\tleft join [" + databaseName + "].INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE b\n\ton d.TABLE_CATALOG =b.TABLE_CATALOG  and d.TABLE_SCHEMA =b.TABLE_SCHEMA and d.TABLE_NAME =b.TABLE_NAME \n\tWHERE d.CONSTRAINT_TYPE ='PRIMARY KEY'\n) c on \n\ta.TABLE_CATALOG =c.TABLE_CATALOG \n\tAND A.TABLE_SCHEMA=C.TABLE_SCHEMA \n\tAND A.TABLE_NAME=C.TABLE_NAME \n\tAND A.COLUMN_NAME=C.COLUMN_NAME\n where A.TABLE_SCHEMA = '" + schema + "' and A.TABLE_NAME='" + table + "'";
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         Column column = new Column(resultSet.getString("COLUMN_NAME"), schema, table, resultSet.getString("DATA_TYPE"));
         int character_maximum_length = resultSet.getInt("CHARACTER_MAXIMUM_LENGTH");
         if (character_maximum_length > 0) {
            column.setLength(String.valueOf(character_maximum_length));
         }

         Object scale = resultSet.getObject("NUMERIC_SCALE");
         column.setDigits(scale == null ? "" : scale.toString());
         column.setColumnType(column.getDataType());
         column.setPk(resultSet.getInt("PK"));
         columns.add(column);
      }

      resultSet.close();
      return columns;
   }

   public List<View> getView(Statement statement, String schema, String databaseName) throws SQLException {
      List<View> views = new ArrayList();
      String sql = "select * from [" + databaseName + "].INFORMATION_SCHEMA.VIEWS where TABLE_SCHEMA = '" + schema + "'";
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         String table_name = resultSet.getString("TABLE_NAME");
         String view_definition = resultSet.getString("VIEW_DEFINITION").replaceAll("`", "");
         views.add(new View(table_name, view_definition));
      }

      resultSet.close();
      return views;
   }

   public List<Schema> getSchemas(Statement statement, List<String> databaseNames, Map<String, Schema> outMap) throws SQLException {
      if (databaseNames != null && databaseNames.size() != 0) {
         StringBuilder sql = new StringBuilder();
         List<Schema> schemas = new ArrayList();

         for(int i = 0; i < databaseNames.size(); ++i) {
            String databaseName = (String)databaseNames.get(i);
            sql.append("SELECT '" + databaseName + "' as dbname,a.* FROM [" + databaseName + "].sys.schemas a ");
            if (i != databaseNames.size() - 1) {
               sql.append(" union all ");
            }
         }

         ResultSet resultSet = statement.executeQuery(sql.toString());

         while(resultSet.next()) {
            String dbname = resultSet.getString("dbname");
            Schema schema = new Schema(resultSet.getString("name"));
            schema.setRealName(resultSet.getString("name"));
            schema.setDatabaseName(dbname);
            schemas.add(schema);
            String id = dbname + "." + schema.getName();
            outMap.put(id, schema);
         }

         return schemas;
      } else {
         return new ArrayList();
      }
   }

   public List<Table> getTables(Statement statement, List<String> databaseNames, Map<String, Schema> schemas, Map<String, Table> outMap) throws SQLException {
      if (databaseNames != null && databaseNames.size() != 0) {
         StringBuilder sql = new StringBuilder();

         for(int i = 0; i < databaseNames.size(); ++i) {
            String databaseName = (String)databaseNames.get(i);
            sql.append("select * from [" + databaseName + "].INFORMATION_SCHEMA.TABLES where TABLE_TYPE = 'BASE TABLE' ");
            if (i != databaseNames.size() - 1) {
               sql.append(" union all ");
            }
         }

         List<Table> tables = new ArrayList();
         ResultSet resultSet = statement.executeQuery(sql.toString());

         while(resultSet.next()) {
            String dbname = resultSet.getString("TABLE_CATALOG");
            String schemaName = resultSet.getString("TABLE_SCHEMA");
            String tableName = resultSet.getString("TABLE_NAME");
            Table table = new Table(tableName, schemaName);
            table.setSchema(schemaName);
            table.setDbName(dbname);
            table.setRealName(tableName);
            tables.add(table);
            String schId = dbname + "." + schemaName;
            Schema schema = (Schema)schemas.get(schId);
            if (schema != null) {
               schema.getTableList().add(table);
            }

            String tbId = dbname + "." + schemaName + "." + tableName;
            outMap.put(tbId, table);
         }

         return tables;
      } else {
         return new ArrayList();
      }
   }

   public List<View> getViews(Statement statement, List<String> databaseNames, Map<String, Schema> schemas) throws SQLException {
      if (databaseNames != null && databaseNames.size() != 0) {
         List<View> views = new ArrayList();
         StringBuilder sql = new StringBuilder();

         for(int i = 0; i < databaseNames.size(); ++i) {
            String databaseName = (String)databaseNames.get(i);
            sql.append("select * from [" + databaseName + "].INFORMATION_SCHEMA.VIEWS ");
            if (i != databaseNames.size() - 1) {
               sql.append(" union all ");
            }
         }

         ResultSet resultSet = statement.executeQuery(sql.toString());

         while(resultSet.next()) {
            String dbname = resultSet.getString("TABLE_CATALOG");
            String table_name = resultSet.getString("TABLE_NAME");
            String view_definition = resultSet.getString("VIEW_DEFINITION");
            if (view_definition != null) {
               view_definition = view_definition.replaceAll("`", "");
            }

            String sch = resultSet.getString("TABLE_SCHEMA");
            View view = new View(table_name, view_definition);
            view.setSchema(sch);
            view.setDbname(dbname);
            views.add(view);
            String schemaId = dbname + "." + sch;
            Schema schema = (Schema)schemas.get(schemaId);
            if (schema != null) {
               schema.getViewList().add(view);
            }
         }

         return views;
      } else {
         return new ArrayList();
      }
   }

   class ColumnThread implements Runnable {
      private final Connection connection;
      CountDownLatch latch;
      List<TableItem> tables;
      List<Column> columns;

      public ColumnThread(Connection connection, CountDownLatch latch, List<TableItem> tables) {
         this.connection = connection;
         this.tables = tables;
         this.latch = latch;
         this.columns = new ArrayList();
      }

      public List<Column> getColumns() {
         return this.columns;
      }

      public void run() {
         try {
            Map<String, List<TableItem>> dbTablesMap = new HashMap();
            Map<String, TableItem> tbMap = new HashMap();

            for(TableItem table : this.tables) {
               String dbName = table.databaseName;
               String tbId = table.databaseName + "." + table.schemaName + "." + table.tableName;
               tbMap.put(tbId, table);
               List<TableItem> lstTable = null;
               if (!dbTablesMap.containsKey(table.databaseName)) {
                  lstTable = (List)dbTablesMap.put(table.databaseName, new ArrayList());
               }

               if (lstTable == null) {
                  lstTable = (List)dbTablesMap.get(table.databaseName);
               }

               lstTable.add(table);
            }

            StringBuilder sql = new StringBuilder();
            Set<Map.Entry<String, List<TableItem>>> entries = dbTablesMap.entrySet();
            int size = entries.size();
            int j = 0;

            for(Map.Entry<String, List<TableItem>> entry : entries) {
               String dbname = (String)entry.getKey();
               List<TableItem> tables = (List)entry.getValue();
               sql.append("SELECT a.*,NULLIF(c.pk,0) PK  \nFROM [" + dbname + "].INFORMATION_SCHEMA.COLUMNS A \nLEFT JOIN ( \nSELECT d.TABLE_CATALOG ,d.TABLE_SCHEMA ,d.TABLE_NAME ,b.COLUMN_NAME ,1 as PK \nFROM [" + dbname + "].INFORMATION_SCHEMA.TABLE_CONSTRAINTS d \nLEFT JOIN [" + dbname + "].INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE b \non d.TABLE_CATALOG =b.TABLE_CATALOG  AND d.TABLE_SCHEMA =b.TABLE_SCHEMA AND d.TABLE_NAME =b.TABLE_NAME  \nWHERE d.CONSTRAINT_TYPE ='PRIMARY KEY' \n) c ON  \na.TABLE_CATALOG =c.TABLE_CATALOG  \nAND A.TABLE_SCHEMA=C.TABLE_SCHEMA  \nAND A.TABLE_NAME=C.TABLE_NAME  \nAND A.COLUMN_NAME=C.COLUMN_NAME WHERE EXISTS ( \n");

               for(int k = 0; k < tables.size(); ++k) {
                  TableItem tableItem = (TableItem)tables.get(k);
                  String dbName = tableItem.databaseName;
                  String tbName = tableItem.tableName;
                  String sch = tableItem.schemaName;
                  sql.append("\n SELECT 1 FROM  [" + dbName + "].INFORMATION_SCHEMA.COLUMNS e WHERE e.TABLE_SCHEMA='" + sch + "' AND e.TABLE_NAME='" + tbName + "'  AND e.TABLE_SCHEMA= a.TABLE_SCHEMA AND e.TABLE_NAME= a.TABLE_NAME");
                  if (k != tables.size() - 1) {
                     sql.append(" \n UNION ALL ");
                  }
               }

               sql.append(")");
               if (j != size - 1) {
                  sql.append("\n union all \n");
               }

               ++j;
            }

            Statement statement = null;
            ResultSet resultSet = null;

            try {
               statement = this.connection.createStatement();

               Column column;
               for(resultSet = statement.executeQuery(sql.toString()); resultSet.next(); this.columns.add(column)) {
                  String dbName = resultSet.getString("TABLE_CATALOG");
                  String schema = resultSet.getString("TABLE_SCHEMA");
                  String table = resultSet.getString("TABLE_NAME");
                  String columnName = resultSet.getString("COLUMN_NAME");
                  column = new Column(columnName, schema, table, resultSet.getString("DATA_TYPE"));
                  column.setDbname(dbName);
                  int character_maximum_length = resultSet.getInt("CHARACTER_MAXIMUM_LENGTH");
                  if (character_maximum_length > 0) {
                     column.setLength(String.valueOf(character_maximum_length));
                  }

                  Object scale = resultSet.getObject("NUMERIC_SCALE");
                  column.setDigits(scale == null ? "" : scale.toString());
                  column.setSchema(schema);
                  column.setColumnType(column.getDataType());
                  column.setPk(resultSet.getInt("PK"));
                  String tbId = dbName + "." + schema + "." + table;
                  TableItem item = (TableItem)tbMap.get(tbId);
                  if (item != null) {
                     item.table.getColumnList().add(column);
                  } else {
                     System.out.println(tbId);
                  }
               }
            } catch (SQLException throwables) {
               throwables.printStackTrace();
            } finally {
               try {
                  if (resultSet != null) {
                     resultSet.close();
                  }

                  if (statement != null) {
                     statement.close();
                  }
               } catch (SQLException throwables) {
                  throwables.printStackTrace();
               }

            }
         } finally {
            if (this.latch != null) {
               try {
                  this.latch.countDown();
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }

         }

      }
   }

   class TableItem {
      Table table;
      String databaseName;
      String tableName;
      String schemaName;
   }
}
