package com.chenyang.druid.sql.ast.statement;

import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLReplaceable;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLVariantRefExpr;
import com.chenyang.druid.sql.repository.SchemaObject;
import com.chenyang.druid.sql.visitor.SQLASTVisitor;
import com.chenyang.druid.util.FnvHash;
import com.chenyang.druid.util.StringUtils;
import java.util.ArrayList;
import java.util.List;

public class SQLExprTableSource extends SQLTableSourceImpl implements SQLReplaceable {
   protected SQLExpr expr;
   protected List<SQLName> partitions;
   protected SQLTableSampling sampling;
   protected SchemaObject schemaObject;
   protected List<SQLName> columns;

   public SQLExprTableSource() {
   }

   public SQLExprTableSource(String tableName) {
      this(SQLUtils.toSQLExpr(tableName), null);
   }

   public SQLExprTableSource(SQLExpr expr) {
      this(expr, null);
   }

   public SQLExprTableSource(SQLExpr expr, String alias) {
      this.setExpr(expr);
      this.setAlias(alias);
   }

   public SQLExpr getExpr() {
      return this.expr;
   }

   public void setExpr(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
      }

      this.expr = x;
   }

   public void setExpr(String name) {
      this.setExpr((SQLExpr)(new SQLIdentifierExpr(name)));
   }

   public SQLTableSampling getSampling() {
      return this.sampling;
   }

   public void setSampling(SQLTableSampling x) {
      if (x != null) {
         x.setParent(this);
      }

      this.sampling = x;
   }

   public SQLName getName() {
      return this.expr instanceof SQLName ? (SQLName)this.expr : null;
   }

   public String getTableName(boolean normalize) {
      String tableName = this.getTableName();
      return normalize ? SQLUtils.normalize(tableName) : tableName;
   }

   public String getTableName() {
      if (this.expr == null) {
         return null;
      } else if (this.expr instanceof SQLIdentifierExpr) {
         return ((SQLIdentifierExpr)this.expr).getName();
      } else if (this.expr instanceof SQLPropertyExpr) {
         return ((SQLPropertyExpr)this.expr).getSimpleName();
      } else if (this.expr instanceof SQLVariantRefExpr) {
         return ((SQLVariantRefExpr)this.expr).getName();
      } else {
         return this.expr instanceof SQLMethodInvokeExpr ? this.expr.toString() : null;
      }
   }

   public String getSchema() {
      if (this.expr == null) {
         return null;
      } else if (this.expr instanceof SQLPropertyExpr) {
         SQLExpr owner = ((SQLPropertyExpr)this.expr).getOwner();
         if (owner instanceof SQLIdentifierExpr) {
            return ((SQLIdentifierExpr)owner).getName();
         } else if (owner instanceof SQLPropertyExpr) {
            return ((SQLPropertyExpr)owner).getSimpleName();
         } else {
            return owner instanceof SQLAllColumnExpr ? "*" : null;
         }
      } else {
         return null;
      }
   }

   public String getCatalog() {
      if (this.expr instanceof SQLPropertyExpr) {
         SQLExpr owner = ((SQLPropertyExpr)this.expr).getOwner();
         if (owner instanceof SQLPropertyExpr) {
            SQLExpr catalogExpr = ((SQLPropertyExpr)owner).getOwner();
            if (catalogExpr instanceof SQLIdentifierExpr) {
               return ((SQLIdentifierExpr)catalogExpr).getName();
            }
         }

         return null;
      } else {
         return null;
      }
   }

   public boolean setCatalog(String catalog) {
      if (this.expr instanceof SQLPropertyExpr) {
         SQLPropertyExpr propertyExpr = (SQLPropertyExpr)this.expr;
         SQLExpr owner = propertyExpr.getOwner();
         if (owner instanceof SQLIdentifierExpr) {
            if (catalog == null) {
               return false;
            }

            propertyExpr.setOwner((SQLExpr)(new SQLPropertyExpr(catalog, ((SQLIdentifierExpr)owner).getName())));
            return true;
         }

         if (owner instanceof SQLPropertyExpr) {
            SQLPropertyExpr propertyOwner = (SQLPropertyExpr)owner;
            SQLExpr propertyOwnerOwner = propertyOwner.getOwner();
            if (propertyOwnerOwner instanceof SQLIdentifierExpr) {
               if (catalog == null) {
                  propertyExpr.setOwner(((SQLIdentifierExpr)propertyOwnerOwner).getName());
               } else {
                  propertyOwner.setOwner((SQLExpr)(new SQLIdentifierExpr(catalog)));
               }

               return true;
            }
         }
      }

      return false;
   }

   public void setCatalog(String catalog, String schema) {
      if (catalog == null) {
         throw new IllegalArgumentException("catalog is null.");
      } else if (schema == null) {
         throw new IllegalArgumentException("schema is null.");
      } else {
         this.setSchema(schema);
         this.setCatalog(catalog);
      }
   }

   public void setSchema(String schema) {
      if (this.expr instanceof SQLPropertyExpr) {
         SQLPropertyExpr propertyExpr = (SQLPropertyExpr)this.expr;
         if (StringUtils.isEmpty(schema)) {
            this.setExpr((SQLExpr)(new SQLIdentifierExpr(propertyExpr.getName())));
         } else {
            propertyExpr.setOwner(schema);
         }
      } else {
         if (StringUtils.isEmpty(schema)) {
            return;
         }

         String ident = ((SQLIdentifierExpr)this.expr).getName();
         this.setExpr((SQLExpr)(new SQLPropertyExpr(schema, ident)));
      }

   }

   public void setSimpleName(String name) {
      if (StringUtils.isEmpty(name)) {
         throw new IllegalArgumentException("schema is empty.");
      } else {
         if (this.expr == null) {
            this.expr = new SQLIdentifierExpr(name);
         } else if (this.expr instanceof SQLPropertyExpr) {
            ((SQLPropertyExpr)this.expr).setName(name);
         } else {
            this.expr = new SQLIdentifierExpr(name);
         }

      }
   }

   public List<SQLName> getPartitions() {
      if (this.partitions == null) {
         this.partitions = new ArrayList(2);
      }

      return this.partitions;
   }

   public int getPartitionSize() {
      return this.partitions == null ? 0 : this.partitions.size();
   }

   public void addPartition(SQLName partition) {
      if (partition != null) {
         partition.setParent(this);
      }

      if (this.partitions == null) {
         this.partitions = new ArrayList(2);
      }

      this.partitions.add(partition);
   }

   protected void accept0(SQLASTVisitor visitor) {
      if (visitor.visit(this)) {
         if (this.expr != null) {
            this.expr.accept(visitor);
         }

         if (this.sampling != null) {
            this.sampling.accept(visitor);
         }
      }

      visitor.endVisit(this);
   }

   public String computeAlias() {
      String alias = this.getAlias();
      if (alias == null && this.expr instanceof SQLName) {
         alias = ((SQLName)this.expr).getSimpleName();
      }

      return SQLUtils.normalize(alias);
   }

   public SQLExprTableSource clone() {
      SQLExprTableSource x = new SQLExprTableSource();
      this.cloneTo(x);
      return x;
   }

   public void cloneTo(SQLExprTableSource x) {
      x.alias = this.alias;
      if (this.expr != null) {
         x.setExpr(this.expr.clone());
      }

      if (this.partitions != null) {
         for(SQLName p : this.partitions) {
            SQLName p1 = p.clone();
            x.addPartition(p1);
         }
      }

      if (this.schemaObject != null) {
         x.setSchemaObject(this.schemaObject);
      }

      if (this.columns != null) {
         x.columns = new ArrayList(this.columns.size());

         for(SQLName column : this.columns) {
            SQLName clonedColumn = column.clone();
            clonedColumn.setParent(x);
            x.columns.add(clonedColumn);
         }
      }

   }

   public List<SQLName> getColumns() {
      if (this.columns == null) {
         this.columns = new ArrayList(2);
      }

      return this.columns;
   }

   public List<SQLName> getColumnsDirect() {
      return this.columns;
   }

   public SchemaObject getSchemaObject() {
      return this.schemaObject;
   }

   public void setSchemaObject(SchemaObject schemaObject) {
      this.schemaObject = schemaObject;
   }

   public boolean containsAlias(String alias) {
      long hashCode64 = FnvHash.hashCode64(alias);
      return this.containsAlias(hashCode64);
   }

   public boolean containsAlias(long aliasHash) {
      if (this.aliasHashCode64() == aliasHash) {
         return true;
      } else {
         if (this.expr instanceof SQLPropertyExpr) {
            long exprNameHash = ((SQLPropertyExpr)this.expr).hashCode64();
            if (exprNameHash == aliasHash) {
               return true;
            }
         }

         if (this.expr instanceof SQLName) {
            long exprNameHash = ((SQLName)this.expr).nameHashCode64();
            return exprNameHash == aliasHash;
         } else {
            return false;
         }
      }
   }

   public SQLColumnDefinition findColumn(String columnName) {
      if (columnName == null) {
         return null;
      } else {
         long hash = FnvHash.hashCode64(columnName);
         return this.findColumn(hash);
      }
   }

   public SQLColumnDefinition findColumn(long columnNameHash) {
      SQLObject object = this.resolveColum(columnNameHash);
      return object instanceof SQLColumnDefinition ? (SQLColumnDefinition)object : null;
   }

   public SQLObject resolveColum(long columnNameHash) {
      if (this.schemaObject != null) {
         SQLStatement stmt = this.schemaObject.getStatement();
         if (stmt instanceof SQLCreateTableStatement) {
            SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement)stmt;
            return createTableStmt.findColumn(columnNameHash);
         }
      }

      SQLObject resolvedOwnerObject = null;
      if (this.expr instanceof SQLIdentifierExpr) {
         resolvedOwnerObject = ((SQLIdentifierExpr)this.expr).getResolvedOwnerObject();
      }

      if (resolvedOwnerObject == null) {
         return resolvedOwnerObject;
      } else {
         if (resolvedOwnerObject instanceof SQLWithSubqueryClause.Entry) {
            SQLSelect subQuery = ((SQLWithSubqueryClause.Entry)resolvedOwnerObject).getSubQuery();
            if (subQuery == null) {
               return null;
            }

            SQLSelectQueryBlock firstQueryBlock = subQuery.getFirstQueryBlock();
            if (firstQueryBlock == null) {
               return null;
            }

            SQLSelectItem selectItem = firstQueryBlock.findSelectItem(columnNameHash);
            if (selectItem != null) {
               return selectItem;
            }
         }

         return null;
      }
   }

   public SQLTableSource findTableSourceWithColumn(String columnName) {
      if (columnName == null) {
         return null;
      } else {
         long hash = FnvHash.hashCode64(columnName);
         return this.findTableSourceWithColumn(hash, columnName, 0);
      }
   }

   public SQLTableSource findTableSourceWithColumn(long columnName_hash, String name, int option) {
      if (this.schemaObject != null) {
         SQLStatement stmt = this.schemaObject.getStatement();
         if (stmt instanceof SQLCreateTableStatement) {
            SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement)stmt;
            if (createTableStmt.findColumn(columnName_hash) != null) {
               return this;
            }
         }
      }

      if (this.expr instanceof SQLIdentifierExpr) {
         SQLTableSource tableSource = ((SQLIdentifierExpr)this.expr).getResolvedTableSource();
         if (tableSource != null) {
            return this;
         }
      }

      return null;
   }

   public SQLTableSource findTableSource(long alias_hash) {
      if (alias_hash == 0L) {
         return null;
      } else if (this.aliasHashCode64() == alias_hash) {
         return this;
      } else {
         if (this.expr instanceof SQLName) {
            long exprNameHash = ((SQLName)this.expr).nameHashCode64();
            if (exprNameHash == alias_hash) {
               return this;
            }
         }

         if (this.expr instanceof SQLPropertyExpr) {
            long hash = ((SQLPropertyExpr)this.expr).hashCode64();
            if (hash == alias_hash) {
               return this;
            }
         }

         return null;
      }
   }

   public boolean replace(SQLExpr expr, SQLExpr target) {
      if (expr == this.expr) {
         this.setExpr(target);
         return true;
      } else {
         if (this.partitions != null) {
            for(int i = 0; i < this.partitions.size(); ++i) {
               if (this.partitions.get(i) == expr) {
                  target.setParent(this);
                  this.partitions.set(i, (SQLName)target);
                  return true;
               }
            }
         }

         return false;
      }
   }

   public long aliasHashCode64() {
      if (this.alias != null) {
         if (this.aliasHashCode64 == 0L) {
            this.aliasHashCode64 = FnvHash.hashCode64(this.alias);
         }

         return this.aliasHashCode64;
      } else {
         return this.expr instanceof SQLName ? ((SQLName)this.expr).nameHashCode64() : 0L;
      }
   }

   public boolean equals(Object o) {
      if (this == o) {
         return true;
      } else if (o != null && this.getClass() == o.getClass()) {
         if (!super.equals(o)) {
            return false;
         } else {
            SQLExprTableSource that = (SQLExprTableSource)o;
            if (this.expr != null) {
               if (!this.expr.equals(that.expr)) {
                  return false;
               }
            } else if (that.expr != null) {
               return false;
            }

            if (this.partitions != null) {
               if (!this.partitions.equals(that.partitions)) {
                  return false;
               }
            } else if (that.partitions != null) {
               return false;
            }

            if (this.sampling != null) {
               if (!this.sampling.equals(that.sampling)) {
                  return false;
               }
            } else if (that.sampling != null) {
               return false;
            }

            if (this.schemaObject != null) {
               if (!this.schemaObject.equals(that.schemaObject)) {
                  return false;
               }
            } else if (that.schemaObject != null) {
               return false;
            }

            return this.columns != null ? this.columns.equals(that.columns) : that.columns == null;
         }
      } else {
         return false;
      }
   }

   public int hashCode() {
      int result = super.hashCode();
      result = 31 * result + (this.expr != null ? this.expr.hashCode() : 0);
      result = 31 * result + (this.partitions != null ? this.partitions.hashCode() : 0);
      result = 31 * result + (this.sampling != null ? this.sampling.hashCode() : 0);
      result = 31 * result + (this.schemaObject != null ? this.schemaObject.hashCode() : 0);
      result = 31 * result + (this.columns != null ? this.columns.hashCode() : 0);
      return result;
   }
}
